lkml.org 
[lkml]   [2008]   [Apr]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 10/10] sysfs: user namespaces: add ns to user_struct
    Add the user_namespace to user_struct.

    Add a user_namespace to the sysfs_tag struct.

    Mark the /sys/kernel/uids directory to be tagged so that processes in
    different user namespaces can remount /sys and see their own uid
    listings.

    Without this patch, having CONFIG_FAIR_SCHED=y makes user namespaces
    unusable, because when you
    clone(CLONE_NEWUSER)
    it will auto-create the root userid and try to create
    /sys/kernel/uids/0. Since that already exists from the parent user
    namespace, the create fails, and the clone misleadingly ends up
    returning -ENOMEM.

    This patch fixes the issue by allowing each user namespace to remount
    /sys, and having /sys filter the /sys/kernel/uid/ entries by user
    namespace.

    Signed-off-by: Serge Hallyn <serue@us.ibm.com>
    ---
    fs/dquot.c | 2 -
    fs/ioprio.c | 2 -
    fs/sysfs/mount.c | 25 +++++++++++++++++++++
    include/linux/sched.h | 3 +-
    include/linux/sysfs.h | 9 +++++++
    include/linux/types.h | 5 ++++
    include/linux/user_namespace.h | 5 ++++
    kernel/user.c | 48 ++++++++++++++++++++++++++++++++++-------
    kernel/user_namespace.c | 3 +-
    security/keys/process_keys.c | 14 +++++------
    10 files changed, 97 insertions(+), 19 deletions(-)

    Index: linux-mm/fs/dquot.c
    ===================================================================
    --- linux-mm.orig/fs/dquot.c
    +++ linux-mm/fs/dquot.c
    @@ -968,7 +968,7 @@ static void send_warning(const struct dq
    MINOR(dquot->dq_sb->s_dev));
    if (ret)
    goto attr_err_out;
    - ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid);
    + ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid.uid);
    if (ret)
    goto attr_err_out;
    genlmsg_end(skb, msg_head);
    Index: linux-mm/fs/ioprio.c
    ===================================================================
    --- linux-mm.orig/fs/ioprio.c
    +++ linux-mm/fs/ioprio.c
    @@ -226,7 +226,7 @@ asmlinkage long sys_ioprio_get(int which
    break;

    do_each_thread(g, p) {
    - if (p->uid != user->uid)
    + if (!task_user_equiv(p, user))
    continue;
    tmpio = get_task_ioprio(p);
    if (tmpio < 0)
    Index: linux-mm/fs/sysfs/mount.c
    ===================================================================
    --- linux-mm.orig/fs/sysfs/mount.c
    +++ linux-mm/fs/sysfs/mount.c
    @@ -17,6 +17,7 @@
    #include <linux/pagemap.h>
    #include <linux/init.h>
    #include <linux/nsproxy.h>
    +#include <linux/user_namespace.h>
    #include <net/net_namespace.h>

    #include "sysfs.h"
    @@ -81,6 +82,7 @@ static int sysfs_fill_super(struct super
    sb->s_root = root;
    sb->s_fs_info = info;
    info->tag.net_ns = hold_net(current->nsproxy->net_ns);
    + info->tag.user_ns = current->nsproxy->user_ns;
    return 0;

    out_err:
    @@ -100,6 +102,8 @@ static int sysfs_test_super(struct super

    if (task->nsproxy->net_ns != info->tag.net_ns)
    found = 0;
    + if (task->nsproxy->user_ns != info->tag.user_ns)
    + found = 0;

    return found;
    }
    @@ -214,6 +218,27 @@ static struct pernet_operations sysfs_ne
    };
    #endif

    +#ifdef CONFIG_USER_NS
    +void sysfs_userns_exit(struct user_namespace *user_ns)
    +{
    + /* Allow the net namespace to go away while sysfs is still mounted. */
    + struct super_block *sb;
    + printk(KERN_NOTICE "sysfs: user namespace exiting\n");
    + mutex_lock(&sysfs_rename_mutex);
    + sysfs_grab_supers();
    + mutex_lock(&sysfs_mutex);
    + list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
    + struct sysfs_super_info *info = sysfs_info(sb);
    + if (info->tag.user_ns != user_ns)
    + continue;
    + info->tag.user_ns = NULL;
    + }
    + mutex_unlock(&sysfs_mutex);
    + sysfs_release_supers();
    + mutex_unlock(&sysfs_rename_mutex);
    +}
    +#endif
    +
    int __init sysfs_init(void)
    {
    int err = -ENOMEM;
    Index: linux-mm/include/linux/sched.h
    ===================================================================
    --- linux-mm.orig/include/linux/sched.h
    +++ linux-mm/include/linux/sched.h
    @@ -598,7 +598,7 @@ struct user_struct {

    /* Hash table maintenance information */
    struct hlist_node uidhash_node;
    - uid_t uid;
    + struct k_uid_t uid;

    #ifdef CONFIG_USER_SCHED
    struct task_group *tg;
    @@ -1675,6 +1675,7 @@ static inline struct user_struct *get_ui
    extern void free_uid(struct user_struct *);
    extern void switch_uid(struct user_struct *);
    extern void release_uids(struct user_namespace *ns);
    +extern int task_user_equiv(struct task_struct *tsk, struct user_struct *u);

    #include <asm/current.h>

    Index: linux-mm/include/linux/sysfs.h
    ===================================================================
    --- linux-mm.orig/include/linux/sysfs.h
    +++ linux-mm/include/linux/sysfs.h
    @@ -20,6 +20,7 @@
    struct kobject;
    struct module;
    struct net;
    +struct user_namespace;

    /* FIXME
    * The *owner field is no longer used, but leave around
    @@ -81,6 +82,7 @@ struct sysfs_ops {

    struct sysfs_tag_info {
    struct net *net_ns;
    + struct user_namespace *user_ns;
    };

    struct sysfs_tagged_dir_operations {
    @@ -135,6 +137,9 @@ int sysfs_enable_tagging(struct kobject

    extern int __must_check sysfs_init(void);

    +struct user_namespace;
    +void sysfs_userns_exit(struct user_namespace *user_ns);
    +
    #else /* CONFIG_SYSFS */

    static inline int sysfs_schedule_callback(struct kobject *kobj,
    @@ -253,6 +258,10 @@ static inline void sysfs_printk_last_fil
    {
    }

    +static inline void sysfs_userns_exit(struct user_namespace *user_ns)
    +{
    +}
    +
    #endif /* CONFIG_SYSFS */

    #endif /* _SYSFS_H_ */
    Index: linux-mm/include/linux/types.h
    ===================================================================
    --- linux-mm.orig/include/linux/types.h
    +++ linux-mm/include/linux/types.h
    @@ -37,6 +37,11 @@ typedef __kernel_gid32_t gid_t;
    typedef __kernel_uid16_t uid16_t;
    typedef __kernel_gid16_t gid16_t;

    +struct k_uid_t {
    + uid_t uid;
    + struct user_namespace *ns;
    +};
    +
    typedef unsigned long uintptr_t;

    #ifdef CONFIG_UID16
    Index: linux-mm/include/linux/user_namespace.h
    ===================================================================
    --- linux-mm.orig/include/linux/user_namespace.h
    +++ linux-mm/include/linux/user_namespace.h
    @@ -12,10 +12,15 @@
    struct user_namespace {
    struct kref kref;
    struct hlist_head uidhash_table[UIDHASH_SZ];
    + struct kset *kset;
    struct user_struct *root_user;
    };

    extern struct user_namespace init_user_ns;
    +extern int register_user_ns_kobj(struct user_namespace *ns);
    +extern void unregister_user_ns_kobj(struct user_namespace *ns);
    +extern int register_user_ns_kobj(struct user_namespace *ns);
    +extern void unregister_user_ns_kobj(struct user_namespace *ns);

    #ifdef CONFIG_USER_NS

    Index: linux-mm/kernel/user.c
    ===================================================================
    --- linux-mm.orig/kernel/user.c
    +++ linux-mm/kernel/user.c
    @@ -53,6 +53,10 @@ struct user_struct root_user = {
    .files = ATOMIC_INIT(0),
    .sigpending = ATOMIC_INIT(0),
    .locked_shm = 0,
    + .uid = {
    + .uid = 0,
    + .ns = &init_user_ns,
    + },
    #ifdef CONFIG_USER_SCHED
    .tg = &init_task_group,
    #endif
    @@ -71,13 +75,23 @@ static void uid_hash_remove(struct user_
    hlist_del_init(&up->uidhash_node);
    }

    -static struct user_struct *uid_hash_find(uid_t uid, struct hlist_head *hashent)
    +int task_user_equiv(struct task_struct *tsk, struct user_struct *u)
    +{
    + if (tsk->uid != u->uid.uid)
    + return 0;
    + if (tsk->nsproxy->user_ns != u->uid.ns)
    + return 0;
    + return 1;
    +}
    +
    +static struct user_struct *uid_hash_find(uid_t uid,
    + struct hlist_head *hashent)
    {
    struct user_struct *user;
    struct hlist_node *h;

    hlist_for_each_entry(user, h, hashent, uidhash_node) {
    - if (user->uid == uid) {
    + if (user->uid.uid == uid) {
    atomic_inc(&user->__count);
    return user;
    }
    @@ -236,6 +250,23 @@ static void uids_release(struct kobject
    return;
    }

    +static const void *userns_sb_tag(struct sysfs_tag_info *info)
    +{
    + return info->user_ns;
    +}
    +
    +static const void *userns_kobject_tag(struct kobject *kobj)
    +{
    + struct user_struct *up;
    + up = container_of(kobj, struct user_struct, kobj);
    + return up->uid.ns;
    +}
    +
    +static struct sysfs_tagged_dir_operations userns_tagged_dir_operations = {
    + .sb_tag = userns_sb_tag,
    + .kobject_tag = userns_kobject_tag,
    +};
    +
    static struct kobj_type uids_ktype = {
    .sysfs_ops = &kobj_sysfs_ops,
    .default_attrs = uids_attributes,
    @@ -246,19 +277,19 @@ static struct kobj_type uids_ktype = {
    static int uids_user_create(struct user_struct *up)
    {
    struct kobject *kobj = &up->kobj;
    - int error;
    + int err;

    memset(kobj, 0, sizeof(struct kobject));
    kobj->kset = uids_kset;
    - error = kobject_init_and_add(kobj, &uids_ktype, NULL, "%d", up->uid);
    - if (error) {
    + err = kobject_init_and_add(kobj, &uids_ktype, NULL, "%d", up->uid.uid);
    + if (err) {
    kobject_put(kobj);
    goto done;
    }

    kobject_uevent(kobj, KOBJ_ADD);
    done:
    - return error;
    + return err;
    }

    /* create these entries in sysfs:
    @@ -271,7 +302,7 @@ int __init uids_sysfs_init(void)
    uids_kset = kset_create_and_add("uids", NULL, kernel_kobj);
    if (!uids_kset)
    return -ENOMEM;
    -
    + sysfs_enable_tagging(&uids_kset->kobj, &userns_tagged_dir_operations);
    return uids_user_create(&root_user);
    }

    @@ -403,7 +434,8 @@ struct user_struct *alloc_uid(struct use
    if (!new)
    goto out_unlock;

    - new->uid = uid;
    + new->uid.uid = uid;
    + new->uid.ns = ns;
    atomic_set(&new->__count, 1);

    if (sched_create_user(new) < 0)
    Index: linux-mm/kernel/user_namespace.c
    ===================================================================
    --- linux-mm.orig/kernel/user_namespace.c
    +++ linux-mm/kernel/user_namespace.c
    @@ -22,7 +22,7 @@ static struct user_namespace *clone_user
    struct user_struct *new_user;
    int n;

    - ns = kmalloc(sizeof(struct user_namespace), GFP_KERNEL);
    + ns = kzalloc(sizeof(struct user_namespace), GFP_KERNEL);
    if (!ns)
    return ERR_PTR(-ENOMEM);

    @@ -71,6 +71,7 @@ void free_user_ns(struct kref *kref)
    struct user_namespace *ns;

    ns = container_of(kref, struct user_namespace, kref);
    + sysfs_userns_exit(ns);
    release_uids(ns);
    kfree(ns);
    }
    Index: linux-mm/security/keys/process_keys.c
    ===================================================================
    --- linux-mm.orig/security/keys/process_keys.c
    +++ linux-mm/security/keys/process_keys.c
    @@ -47,7 +47,7 @@ static int install_user_keyrings(struct
    char buf[20];
    int ret;

    - kenter("%p{%u}", user, user->uid);
    + kenter("%p{%u}", user, user->uid.uid);

    if (user->uid_keyring) {
    kleave(" = 0 [exist]");
    @@ -62,13 +62,13 @@ static int install_user_keyrings(struct
    * - there may be one in existence already as it may have been
    * pinned by a session, but the user_struct pointing to it
    * may have been destroyed by setuid */
    - sprintf(buf, "_uid.%u", user->uid);
    + sprintf(buf, "_uid.%u", user->uid.uid);

    uid_keyring = find_keyring_by_name(buf, true);
    if (IS_ERR(uid_keyring)) {
    - uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
    - tsk, KEY_ALLOC_IN_QUOTA,
    - NULL);
    + uid_keyring = keyring_alloc(buf, user->uid.uid,
    + (gid_t) -1, tsk,
    + KEY_ALLOC_IN_QUOTA, NULL);
    if (IS_ERR(uid_keyring)) {
    ret = PTR_ERR(uid_keyring);
    goto error;
    @@ -77,12 +77,12 @@ static int install_user_keyrings(struct

    /* get a default session keyring (which might also exist
    * already) */
    - sprintf(buf, "_uid_ses.%u", user->uid);
    + sprintf(buf, "_uid_ses.%u", user->uid.uid);

    session_keyring = find_keyring_by_name(buf, true);
    if (IS_ERR(session_keyring)) {
    session_keyring =
    - keyring_alloc(buf, user->uid, (gid_t) -1,
    + keyring_alloc(buf, user->uid.uid, (gid_t) -1,
    tsk, KEY_ALLOC_IN_QUOTA, NULL);
    if (IS_ERR(session_keyring)) {
    ret = PTR_ERR(session_keyring);
    --


    \
     
     \ /
      Last update: 2008-04-29 19:43    [W:0.041 / U:30.808 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site