aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYordan Karadzhov (VMware) <y.karadz@gmail.com>2021-11-02 19:02:19 +0200
committerYordan Karadzhov (VMware) <y.karadz@gmail.com>2021-11-05 13:53:13 +0200
commit218b0d61a5a05695e6f25692271fbe8606d9c6a1 (patch)
tree485c68d3d823b5ec59d3670a2b16cc7dc393274e
parentf7211964654580ea1b2e7da3cb962915fc50a432 (diff)
downloadlinux-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.h26
-rw-r--r--kernel/fork.c12
-rw-r--r--kernel/nsproxy.c133
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);