diff options
author | Yordan Karadzhov (VMware) <y.karadz@gmail.com> | 2021-11-02 19:02:19 +0200 |
---|---|---|
committer | Yordan Karadzhov (VMware) <y.karadz@gmail.com> | 2021-11-05 13:53:13 +0200 |
commit | 218b0d61a5a05695e6f25692271fbe8606d9c6a1 (patch) | |
tree | 485c68d3d823b5ec59d3670a2b16cc7dc393274e | |
parent | f7211964654580ea1b2e7da3cb962915fc50a432 (diff) | |
download | linux-218b0d61a5a05695e6f25692271fbe8606d9c6a1.tar.gz |
namespacefs: WiP
Still need to find a proper way to handle the case when the 'pid' object
is freed in detach_pid() and change_pid().
Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
-rw-r--r-- | include/linux/nsproxy.h | 26 | ||||
-rw-r--r-- | kernel/fork.c | 12 | ||||
-rw-r--r-- | kernel/nsproxy.c | 133 |
3 files changed, 161 insertions, 10 deletions
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h index cdb171efc7cb05..4f0e11a5049c26 100644 --- a/include/linux/nsproxy.h +++ b/include/linux/nsproxy.h @@ -93,7 +93,31 @@ static inline struct cred *nsset_cred(struct nsset *set) int copy_namespaces(unsigned long flags, struct task_struct *tsk); void exit_task_namespaces(struct task_struct *tsk); -void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); + +#ifdef CONFIG_NAMESPACE_FS + +int nsproxy_tasks_update(struct task_struct *p, struct pid *pid); + +// int nsproxy_change_pid(struct nsproxy *nsp, struct pid *old_pid, +// struct pid *new_pid); + +#else + +static inline int +nsproxy_tasks_update(struct task_struct *p, struct pid *pid) +{ + return 0; +} + +// static inline int +// nsproxy_change_pid(struct task_struct *p, struct pid *new_pid) +// { +// return 0; +// } + +#endif /* CONFIG_NAMESPACE_FS */ + +int switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); void free_nsproxy(struct nsproxy *ns); int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **, struct cred *, struct fs_struct *); diff --git a/kernel/fork.c b/kernel/fork.c index 3f112b11a9ad14..c3c2bc1a8445da 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2282,6 +2282,10 @@ static __latent_entropy struct task_struct *copy_process( p->kretprobe_instances.first = NULL; #endif + retval = nsproxy_tasks_update(p, pid); + if (retval) + goto bad_fork_free_pid; + /* * Ensure that the cgroup subsystem policies allow the new process to be * forked. It should be noted that the new process's css_set can be changed @@ -3095,8 +3099,11 @@ int ksys_unshare(unsigned long unshare_flags) shm_init_task(current); } - if (new_nsproxy) - switch_task_namespaces(current, new_nsproxy); + if (new_nsproxy) { + err = switch_task_namespaces(current, new_nsproxy); + if (err) + goto bad_nsproxy_switch; + } task_lock(current); @@ -3128,6 +3135,7 @@ int ksys_unshare(unsigned long unshare_flags) perf_event_namespaces(current); +bad_nsproxy_switch: bad_unshare_cleanup_cred: if (new_cred) put_cred(new_cred); diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index eec72ca962e249..6bf0e5c12443c8 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -59,6 +59,89 @@ static inline struct nsproxy *create_nsproxy(void) return nsproxy; } +#ifdef CONFIG_NAMESPACE_FS + +static int ns_add_pid(struct ns_common *ns, struct pid *pid) +{ + pid_t nr; + int i; + + /* + * No need to store the PID if this is the main instance of the + * namespace. + */ + if (ns->inum == init_uts_ns.ns.inum) + return 0; + + nr = pid_nr(pid); + idr_preload(GFP_KERNEL); + i = idr_alloc(&ns->idr, pid, nr, nr + 1, GFP_ATOMIC); + idr_preload_end(); + trace_printk("adding %i (%i ptr: %p) to [%u]\n", nr, i, (void *) pid, ns->inum); + return 0; +} + +static int ns_rmv_pid(struct ns_common *ns, struct pid *pid) +{ + /* Do nothing in the main instance of the namespace. */ + if (ns->inum == init_uts_ns.ns.inum) + return 0; + + idr_remove(&ns->idr, pid_nr(pid)); + trace_printk("removing %i from [%u]\n", pid_nr(pid), ns->inum); + return 0; +} + +int nsproxy_tasks_update(struct task_struct *p, struct pid *pid) +{ + struct nsproxy *nsp = p->nsproxy; + int err; + + task_lock(p); + err = ns_add_pid(&nsp->uts_ns->ns, pid); + /* All other namespaces (except 'pid') to be added here/ */ + task_unlock(p); + + return err; +} + +static int nsproxy_tasks_switch(struct pid *pid, + struct ns_common *old_ns, + struct ns_common *new_ns) +{ + int err = 0; + + if (new_ns) + err = ns_add_pid(new_ns, pid); + + return err ? err : ns_rmv_pid(old_ns, pid); +} + +int nsproxy_change_pid(struct nsproxy *nsp, struct pid *old_pid, + struct pid *new_pid) +{ + int err; + + if (nsp->uts_ns->ns.inum != init_uts_ns.ns.inum) { + err = ns_rmv_pid(&nsp->uts_ns->ns, old_pid); + if (!err && new_pid) + err = ns_add_pid(&nsp->uts_ns->ns, new_pid); + } + + return err; +} + +#else + +static int nsproxy_tasks_switch(struct pid *pid, + struct ns_common *old_ns, + struct ns_common *new_ns) +{ + return 0; +} + +#endif /* CONFIG_NAMESPACE_FS */ + /* * Create new nsproxy and all of its the associated namespaces. * Return the newly created nsproxy. Do not attach this to the task, @@ -87,6 +170,17 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, goto out_uts; } + if (flags & CLONE_NEWUTS) { + task_lock(tsk); + err = nsproxy_tasks_switch(task_pid(tsk), + &tsk->nsproxy->uts_ns->ns, + &new_nsp->uts_ns->ns); + task_unlock(tsk); + if (err) + goto out_ipc; + + } /* Same must be done for all other namespaces (except 'pid'). */ + new_nsp->ipc_ns = copy_ipcs(flags, user_ns, tsk->nsproxy->ipc_ns); if (IS_ERR(new_nsp->ipc_ns)) { err = PTR_ERR(new_nsp->ipc_ns); @@ -182,6 +276,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) timens_on_fork(new_ns, tsk); tsk->nsproxy = new_ns; + return 0; } @@ -234,19 +329,35 @@ out: return err; } -void switch_task_namespaces(struct task_struct *p, struct nsproxy *new) +int switch_task_namespaces(struct task_struct *p, struct nsproxy *new) { struct nsproxy *ns; + int err; might_sleep(); task_lock(p); ns = p->nsproxy; + + if (new) { + err = nsproxy_tasks_switch(task_pid(p), &ns->uts_ns->ns, + &new->uts_ns->ns); + if (err) { + task_unlock(p); + return err; + } + /* + * The switching of all other namespaces (except 'pid') must + * be added here. + */ + } + p->nsproxy = new; task_unlock(p); if (ns) put_nsproxy(ns); + return 0; } void exit_task_namespaces(struct task_struct *p) @@ -490,10 +601,11 @@ out: * exported anymore a simple commit handler for each namespace * should be added to ns_common. */ -static void commit_nsset(struct nsset *nsset) +static int commit_nsset(struct nsset *nsset) { unsigned flags = nsset->flags; struct task_struct *me = current; + int ret; #ifdef CONFIG_USER_NS if (flags & CLONE_NEWUSER) { @@ -520,8 +632,9 @@ static void commit_nsset(struct nsset *nsset) #endif /* transfer ownership */ - switch_task_namespaces(me, nsset->nsproxy); + ret = switch_task_namespaces(me, nsset->nsproxy); nsset->nsproxy = NULL; + return ret; } SYSCALL_DEFINE2(setns, int, fd, int, flags) @@ -556,10 +669,16 @@ SYSCALL_DEFINE2(setns, int, fd, int, flags) err = validate_ns(&nsset, ns); else err = validate_nsset(&nsset, file->private_data); - if (!err) { - commit_nsset(&nsset); - perf_event_namespaces(current); - } + if (err) + goto put_nss; + + err = commit_nsset(&nsset); + if (err) + goto put_nss; + + perf_event_namespaces(current); + +put_nss: put_nsset(&nsset); out: fput(file); |