summaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/ipc_sysctl.c189
-rw-r--r--ipc/namespace.c4
2 files changed, 126 insertions, 67 deletions
diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c
index f101c171753f..15210ac47e9e 100644
--- a/ipc/ipc_sysctl.c
+++ b/ipc/ipc_sysctl.c
@@ -13,43 +13,22 @@
#include <linux/capability.h>
#include <linux/ipc_namespace.h>
#include <linux/msg.h>
+#include <linux/slab.h>
#include "util.h"
-static void *get_ipc(struct ctl_table *table)
-{
- char *which = table->data;
- struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
- which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns;
- return which;
-}
-
-static int proc_ipc_dointvec(struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos)
-{
- struct ctl_table ipc_table;
-
- memcpy(&ipc_table, table, sizeof(ipc_table));
- ipc_table.data = get_ipc(table);
-
- return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
-}
-
-static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
+static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
+ struct ipc_namespace *ns = table->extra1;
struct ctl_table ipc_table;
+ int err;
memcpy(&ipc_table, table, sizeof(ipc_table));
- ipc_table.data = get_ipc(table);
- return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
-}
+ ipc_table.extra1 = SYSCTL_ZERO;
+ ipc_table.extra2 = SYSCTL_ONE;
-static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos)
-{
- struct ipc_namespace *ns = current->nsproxy->ipc_ns;
- int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ err = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
if (err < 0)
return err;
@@ -58,17 +37,6 @@ static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
return err;
}
-static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos)
-{
- struct ctl_table ipc_table;
- memcpy(&ipc_table, table, sizeof(ipc_table));
- ipc_table.data = get_ipc(table);
-
- return proc_doulongvec_minmax(&ipc_table, write, buffer,
- lenp, ppos);
-}
-
static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
@@ -87,11 +55,17 @@ static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
+ struct ipc_namespace *ns = table->extra1;
+ struct ctl_table ipc_table;
int ret, semmni;
- struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+
+ memcpy(&ipc_table, table, sizeof(ipc_table));
+
+ ipc_table.extra1 = NULL;
+ ipc_table.extra2 = NULL;
semmni = ns->sem_ctls[3];
- ret = proc_ipc_dointvec(table, write, buffer, lenp, ppos);
+ ret = proc_dointvec(table, write, buffer, lenp, ppos);
if (!ret)
ret = sem_check_semmni(current->nsproxy->ipc_ns);
@@ -108,12 +82,18 @@ static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
static int proc_ipc_dointvec_minmax_checkpoint_restore(struct ctl_table *table,
int write, void *buffer, size_t *lenp, loff_t *ppos)
{
- struct user_namespace *user_ns = current->nsproxy->ipc_ns->user_ns;
+ struct ipc_namespace *ns = table->extra1;
+ struct ctl_table ipc_table;
- if (write && !checkpoint_restore_ns_capable(user_ns))
+ if (write && !checkpoint_restore_ns_capable(ns->user_ns))
return -EPERM;
- return proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+ memcpy(&ipc_table, table, sizeof(ipc_table));
+
+ ipc_table.extra1 = SYSCTL_ZERO;
+ ipc_table.extra2 = SYSCTL_INT_MAX;
+
+ return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
}
#endif
@@ -121,27 +101,27 @@ int ipc_mni = IPCMNI;
int ipc_mni_shift = IPCMNI_SHIFT;
int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
-static struct ctl_table ipc_kern_table[] = {
+static struct ctl_table ipc_sysctls[] = {
{
.procname = "shmmax",
.data = &init_ipc_ns.shm_ctlmax,
.maxlen = sizeof(init_ipc_ns.shm_ctlmax),
.mode = 0644,
- .proc_handler = proc_ipc_doulongvec_minmax,
+ .proc_handler = proc_doulongvec_minmax,
},
{
.procname = "shmall",
.data = &init_ipc_ns.shm_ctlall,
.maxlen = sizeof(init_ipc_ns.shm_ctlall),
.mode = 0644,
- .proc_handler = proc_ipc_doulongvec_minmax,
+ .proc_handler = proc_doulongvec_minmax,
},
{
.procname = "shmmni",
.data = &init_ipc_ns.shm_ctlmni,
.maxlen = sizeof(init_ipc_ns.shm_ctlmni),
.mode = 0644,
- .proc_handler = proc_ipc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = &ipc_mni,
},
@@ -151,15 +131,13 @@ static struct ctl_table ipc_kern_table[] = {
.maxlen = sizeof(init_ipc_ns.shm_rmid_forced),
.mode = 0644,
.proc_handler = proc_ipc_dointvec_minmax_orphans,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
},
{
.procname = "msgmax",
.data = &init_ipc_ns.msg_ctlmax,
.maxlen = sizeof(init_ipc_ns.msg_ctlmax),
.mode = 0644,
- .proc_handler = proc_ipc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_INT_MAX,
},
@@ -168,7 +146,7 @@ static struct ctl_table ipc_kern_table[] = {
.data = &init_ipc_ns.msg_ctlmni,
.maxlen = sizeof(init_ipc_ns.msg_ctlmni),
.mode = 0644,
- .proc_handler = proc_ipc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = &ipc_mni,
},
@@ -186,7 +164,7 @@ static struct ctl_table ipc_kern_table[] = {
.data = &init_ipc_ns.msg_ctlmnb,
.maxlen = sizeof(init_ipc_ns.msg_ctlmnb),
.mode = 0644,
- .proc_handler = proc_ipc_dointvec_minmax,
+ .proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_INT_MAX,
},
@@ -204,8 +182,6 @@ static struct ctl_table ipc_kern_table[] = {
.maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
.mode = 0666,
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
},
{
.procname = "msg_next_id",
@@ -213,8 +189,6 @@ static struct ctl_table ipc_kern_table[] = {
.maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
.mode = 0666,
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
},
{
.procname = "shm_next_id",
@@ -222,25 +196,106 @@ static struct ctl_table ipc_kern_table[] = {
.maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
.mode = 0666,
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
},
#endif
{}
};
-static struct ctl_table ipc_root_table[] = {
- {
- .procname = "kernel",
- .mode = 0555,
- .child = ipc_kern_table,
- },
- {}
+static struct ctl_table_set *set_lookup(struct ctl_table_root *root)
+{
+ return &current->nsproxy->ipc_ns->ipc_set;
+}
+
+static int set_is_seen(struct ctl_table_set *set)
+{
+ return &current->nsproxy->ipc_ns->ipc_set == set;
+}
+
+static struct ctl_table_root set_root = {
+ .lookup = set_lookup,
};
+bool setup_ipc_sysctls(struct ipc_namespace *ns)
+{
+ struct ctl_table *tbl;
+
+ setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen);
+
+ tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL);
+ if (tbl) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) {
+ if (tbl[i].data == &init_ipc_ns.shm_ctlmax) {
+ tbl[i].data = &ns->shm_ctlmax;
+
+ } else if (tbl[i].data == &init_ipc_ns.shm_ctlall) {
+ tbl[i].data = &ns->shm_ctlall;
+
+ } else if (tbl[i].data == &init_ipc_ns.shm_ctlmni) {
+ tbl[i].data = &ns->shm_ctlmni;
+
+ } else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced) {
+ tbl[i].data = &ns->shm_rmid_forced;
+ tbl[i].extra1 = ns;
+
+ } else if (tbl[i].data == &init_ipc_ns.msg_ctlmax) {
+ tbl[i].data = &ns->msg_ctlmax;
+
+ } else if (tbl[i].data == &init_ipc_ns.msg_ctlmni) {
+ tbl[i].data = &ns->msg_ctlmni;
+
+ } else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb) {
+ tbl[i].data = &ns->msg_ctlmnb;
+
+ } else if (tbl[i].data == &init_ipc_ns.sem_ctls) {
+ tbl[i].data = &ns->sem_ctls;
+ tbl[i].extra1 = ns;
+#ifdef CONFIG_CHECKPOINT_RESTORE
+ } else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id) {
+ tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id;
+ tbl[i].extra1 = ns;
+
+ } else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id) {
+ tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
+ tbl[i].extra1 = ns;
+
+ } else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id) {
+ tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
+ tbl[i].extra1 = ns;
+#endif
+ } else {
+ tbl[i].data = NULL;
+ }
+ }
+
+ ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl);
+ }
+ if (!ns->ipc_sysctls) {
+ kfree(tbl);
+ retire_sysctl_set(&ns->ipc_set);
+ return false;
+ }
+
+ return true;
+}
+
+void retire_ipc_sysctls(struct ipc_namespace *ns)
+{
+ struct ctl_table *tbl;
+
+ tbl = ns->ipc_sysctls->ctl_table_arg;
+ unregister_sysctl_table(ns->ipc_sysctls);
+ retire_sysctl_set(&ns->ipc_set);
+ kfree(tbl);
+}
+
static int __init ipc_sysctl_init(void)
{
- register_sysctl_table(ipc_root_table);
+ if (!setup_ipc_sysctls(&init_ipc_ns)) {
+ pr_warn("ipc sysctl registration failed\n");
+ return -ENOMEM;
+ }
return 0;
}
diff --git a/ipc/namespace.c b/ipc/namespace.c
index f760243ca685..754f3237194a 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -63,6 +63,9 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
if (!setup_mq_sysctls(ns))
goto fail_put;
+ if (!setup_ipc_sysctls(ns))
+ goto fail_put;
+
sem_init_ns(ns);
msg_init_ns(ns);
shm_init_ns(ns);
@@ -130,6 +133,7 @@ static void free_ipc_ns(struct ipc_namespace *ns)
shm_exit_ns(ns);
retire_mq_sysctls(ns);
+ retire_ipc_sysctls(ns);
dec_ipc_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);