123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2007
- *
- * Author: Eric Biederman <[email protected]>
- */
- #include <linux/module.h>
- #include <linux/ipc.h>
- #include <linux/nsproxy.h>
- #include <linux/sysctl.h>
- #include <linux/uaccess.h>
- #include <linux/capability.h>
- #include <linux/ipc_namespace.h>
- #include <linux/msg.h>
- #include <linux/slab.h>
- #include "util.h"
- 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 =
- container_of(table->data, struct ipc_namespace, shm_rmid_forced);
- int err;
- err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
- if (err < 0)
- return err;
- if (ns->shm_rmid_forced)
- shm_destroy_orphaned(ns);
- return err;
- }
- static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos)
- {
- struct ctl_table ipc_table;
- int dummy = 0;
- memcpy(&ipc_table, table, sizeof(ipc_table));
- ipc_table.data = &dummy;
- if (write)
- pr_info_once("writing to auto_msgmni has no effect");
- return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
- }
- static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos)
- {
- struct ipc_namespace *ns =
- container_of(table->data, struct ipc_namespace, sem_ctls);
- int ret, semmni;
- semmni = ns->sem_ctls[3];
- ret = proc_dointvec(table, write, buffer, lenp, ppos);
- if (!ret)
- ret = sem_check_semmni(ns);
- /*
- * Reset the semmni value if an error happens.
- */
- if (ret)
- ns->sem_ctls[3] = semmni;
- return ret;
- }
- int ipc_mni = IPCMNI;
- int ipc_mni_shift = IPCMNI_SHIFT;
- int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
- 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_doulongvec_minmax,
- },
- {
- .procname = "shmall",
- .data = &init_ipc_ns.shm_ctlall,
- .maxlen = sizeof(init_ipc_ns.shm_ctlall),
- .mode = 0644,
- .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_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = &ipc_mni,
- },
- {
- .procname = "shm_rmid_forced",
- .data = &init_ipc_ns.shm_rmid_forced,
- .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_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "msgmni",
- .data = &init_ipc_ns.msg_ctlmni,
- .maxlen = sizeof(init_ipc_ns.msg_ctlmni),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = &ipc_mni,
- },
- {
- .procname = "auto_msgmni",
- .data = NULL,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_ipc_auto_msgmni,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_ONE,
- },
- {
- .procname = "msgmnb",
- .data = &init_ipc_ns.msg_ctlmnb,
- .maxlen = sizeof(init_ipc_ns.msg_ctlmnb),
- .mode = 0644,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "sem",
- .data = &init_ipc_ns.sem_ctls,
- .maxlen = 4*sizeof(int),
- .mode = 0644,
- .proc_handler = proc_ipc_sem_dointvec,
- },
- #ifdef CONFIG_CHECKPOINT_RESTORE
- {
- .procname = "sem_next_id",
- .data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
- .maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
- .mode = 0444,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "msg_next_id",
- .data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
- .maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
- .mode = 0444,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- {
- .procname = "shm_next_id",
- .data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
- .maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
- .mode = 0444,
- .proc_handler = proc_dointvec_minmax,
- .extra1 = SYSCTL_ZERO,
- .extra2 = SYSCTL_INT_MAX,
- },
- #endif
- {}
- };
- static struct ctl_table_set *set_lookup(struct ctl_table_root *root)
- {
- return ¤t->nsproxy->ipc_ns->ipc_set;
- }
- static int set_is_seen(struct ctl_table_set *set)
- {
- return ¤t->nsproxy->ipc_ns->ipc_set == set;
- }
- static int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
- {
- int mode = table->mode;
- #ifdef CONFIG_CHECKPOINT_RESTORE
- struct ipc_namespace *ns = current->nsproxy->ipc_ns;
- if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
- (table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
- (table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
- checkpoint_restore_ns_capable(ns->user_ns))
- mode = 0666;
- #endif
- return mode;
- }
- static struct ctl_table_root set_root = {
- .lookup = set_lookup,
- .permissions = ipc_permissions,
- };
- 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;
- 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;
- #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;
- else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id)
- tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
- else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id)
- tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
- #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)
- {
- if (!setup_ipc_sysctls(&init_ipc_ns)) {
- pr_warn("ipc sysctl registration failed\n");
- return -ENOMEM;
- }
- return 0;
- }
- device_initcall(ipc_sysctl_init);
- static int __init ipc_mni_extend(char *str)
- {
- ipc_mni = IPCMNI_EXTEND;
- ipc_mni_shift = IPCMNI_EXTEND_SHIFT;
- ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE;
- pr_info("IPCMNI extended to %d.\n", ipc_mni);
- return 0;
- }
- early_param("ipcmni_extend", ipc_mni_extend);
|