The uts sysctl table contains two writable fields
(domainname and nodename), so split the table into
common (read-only) part and writable (shadowed).

This fixes the BUG! You may create a namespace and
then writing to /proc/sys/hostname will cause an
init_uts_ns overwrite.

Signed-off-by: Pavel Emelyanov <[EMAIL PROTECTED]>

---

diff --git a/include/linux/utsname.h b/include/linux/utsname.h
index 923db99..7517b36 100644
--- a/include/linux/utsname.h
+++ b/include/linux/utsname.h
@@ -40,6 +40,7 @@ struct new_utsname {
 struct uts_namespace {
        struct kref kref;
        struct new_utsname name;
+       struct ctl_table_header *ctl_header;
 };
 extern struct uts_namespace init_uts_ns;
 
@@ -66,6 +67,9 @@ static inline struct new_utsname *init_utsname(void)
        return &init_uts_ns.name;
 }
 
+int clone_uts_sysctl(struct uts_namespace *ns);
+void free_uts_sysctl(struct uts_namespace *ns);
+
 extern struct rw_semaphore uts_sem;
 
 #endif /* __KERNEL__ */
diff --git a/kernel/utsname.c b/kernel/utsname.c
index 816d7b2..22e40bb 100644
--- a/kernel/utsname.c
+++ b/kernel/utsname.c
@@ -26,13 +26,21 @@ static struct uts_namespace *clone_uts_ns(struct 
uts_namespace *old_ns)
 
        ns = kmalloc(sizeof(struct uts_namespace), GFP_KERNEL);
        if (!ns)
-               return ERR_PTR(-ENOMEM);
+               goto err_alloc;
+
+       if (clone_uts_sysctl(ns))
+               goto err_sysctl;
 
        down_read(&uts_sem);
        memcpy(&ns->name, &old_ns->name, sizeof(ns->name));
        up_read(&uts_sem);
        kref_init(&ns->kref);
        return ns;
+
+err_sysctl:
+       kfree(ns);
+err_alloc:
+       return ERR_PTR(-ENOMEM);
 }
 
 /*
@@ -62,5 +70,6 @@ void free_uts_ns(struct kref *kref)
        struct uts_namespace *ns;
 
        ns = container_of(kref, struct uts_namespace, kref);
+       free_uts_sysctl(ns);
        kfree(ns);
 }
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c
index c76c064..8a06f0b 100644
--- a/kernel/utsname_sysctl.c
+++ b/kernel/utsname_sysctl.c
@@ -75,6 +75,11 @@ static int sysctl_uts_string(ctl_table *table, int __user 
*name, int nlen,
 #define sysctl_uts_string NULL
 #endif
 
+static struct ctl_table_header *uts_sysctl_shadow(struct ctl_table_header *h)
+{
+       return current->nsproxy->uts_ns->ctl_header;
+}
+
 static struct ctl_table uts_kern_table[] = {
        {
                .ctl_name       = KERN_OSTYPE,
@@ -103,6 +108,20 @@ static struct ctl_table uts_kern_table[] = {
                .proc_handler   = proc_do_uts_string,
                .strategy       = sysctl_uts_string,
        },
+       {}
+};
+
+static struct ctl_table uts_root_table[] = {
+       {
+               .ctl_name       = CTL_KERN,
+               .procname       = "kernel",
+               .mode           = 0555,
+               .child          = uts_kern_table,
+       },
+       {}
+};
+
+static struct ctl_table uts_kern_table_sh[] = {
        {
                .ctl_name       = KERN_NODENAME,
                .procname       = "hostname",
@@ -124,19 +143,44 @@ static struct ctl_table uts_kern_table[] = {
        {}
 };
 
-static struct ctl_table uts_root_table[] = {
+static struct ctl_table uts_root_table_sh[] = {
        {
                .ctl_name       = CTL_KERN,
                .procname       = "kernel",
                .mode           = 0555,
-               .child          = uts_kern_table,
+               .child          = uts_kern_table_sh,
        },
        {}
 };
 
+int clone_uts_sysctl(struct uts_namespace *ns)
+{
+       struct ctl_table_header *h;
+       struct ctl_table *tbl;
+
+       h = create_sysctl_shadow(init_uts_ns.ctl_header);
+       if (h == NULL)
+               return -ENOMEM;
+
+       tbl = h->ctl_table->child;
+
+       tbl[0].data = ns->name.nodename;
+       tbl[1].data = ns->name.domainname;
+
+       ns->ctl_header = h;
+       return 0;
+}
+
+void free_uts_sysctl(struct uts_namespace *ns)
+{
+       free_sysctl_shadow(ns->ctl_header);
+}
+
 static int __init utsname_sysctl_init(void)
 {
        register_sysctl_table(uts_root_table);
+       init_uts_ns.ctl_header = register_sysctl_table_shadow(uts_root_table_sh,
+                       uts_sysctl_shadow);
        return 0;
 }
 

_______________________________________________
Containers mailing list
[EMAIL PROTECTED]
https://lists.linux-foundation.org/mailman/listinfo/containers

_______________________________________________
Devel mailing list
[email protected]
https://openvz.org/mailman/listinfo/devel

Reply via email to