1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2007 4 * 5 * Author: Eric Biederman <ebiederm@xmision.com> 6 */ 7 8 #include <linux/module.h> 9 #include <linux/ipc.h> 10 #include <linux/nsproxy.h> 11 #include <linux/sysctl.h> 12 #include <linux/uaccess.h> 13 #include <linux/capability.h> 14 #include <linux/ipc_namespace.h> 15 #include <linux/msg.h> 16 #include <linux/slab.h> 17 #include <linux/cred.h> 18 #include "util.h" 19 20 static int proc_ipc_dointvec_minmax_orphans(const struct ctl_table *table, int write, 21 void *buffer, size_t *lenp, loff_t *ppos) 22 { 23 struct ipc_namespace *ns = 24 container_of(table->data, struct ipc_namespace, shm_rmid_forced); 25 int err; 26 27 err = proc_dointvec_minmax(table, write, buffer, lenp, ppos); 28 29 if (err < 0) 30 return err; 31 if (ns->shm_rmid_forced) 32 shm_destroy_orphaned(ns); 33 return err; 34 } 35 36 static int proc_ipc_auto_msgmni(const struct ctl_table *table, int write, 37 void *buffer, size_t *lenp, loff_t *ppos) 38 { 39 struct ctl_table ipc_table; 40 int dummy = 0; 41 42 memcpy(&ipc_table, table, sizeof(ipc_table)); 43 ipc_table.data = &dummy; 44 45 if (write) 46 pr_info_once("writing to auto_msgmni has no effect"); 47 48 return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); 49 } 50 51 static int proc_ipc_sem_dointvec(const struct ctl_table *table, int write, 52 void *buffer, size_t *lenp, loff_t *ppos) 53 { 54 struct ipc_namespace *ns = 55 container_of(table->data, struct ipc_namespace, sem_ctls); 56 int ret, semmni; 57 58 semmni = ns->sem_ctls[3]; 59 ret = proc_dointvec(table, write, buffer, lenp, ppos); 60 61 if (!ret) 62 ret = sem_check_semmni(ns); 63 64 /* 65 * Reset the semmni value if an error happens. 66 */ 67 if (ret) 68 ns->sem_ctls[3] = semmni; 69 return ret; 70 } 71 72 int ipc_mni = IPCMNI; 73 int ipc_mni_shift = IPCMNI_SHIFT; 74 int ipc_min_cycle = RADIX_TREE_MAP_SIZE; 75 76 static struct ctl_table ipc_sysctls[] = { 77 { 78 .procname = "shmmax", 79 .data = &init_ipc_ns.shm_ctlmax, 80 .maxlen = sizeof(init_ipc_ns.shm_ctlmax), 81 .mode = 0644, 82 .proc_handler = proc_doulongvec_minmax, 83 }, 84 { 85 .procname = "shmall", 86 .data = &init_ipc_ns.shm_ctlall, 87 .maxlen = sizeof(init_ipc_ns.shm_ctlall), 88 .mode = 0644, 89 .proc_handler = proc_doulongvec_minmax, 90 }, 91 { 92 .procname = "shmmni", 93 .data = &init_ipc_ns.shm_ctlmni, 94 .maxlen = sizeof(init_ipc_ns.shm_ctlmni), 95 .mode = 0644, 96 .proc_handler = proc_dointvec_minmax, 97 .extra1 = SYSCTL_ZERO, 98 .extra2 = &ipc_mni, 99 }, 100 { 101 .procname = "shm_rmid_forced", 102 .data = &init_ipc_ns.shm_rmid_forced, 103 .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), 104 .mode = 0644, 105 .proc_handler = proc_ipc_dointvec_minmax_orphans, 106 .extra1 = SYSCTL_ZERO, 107 .extra2 = SYSCTL_ONE, 108 }, 109 { 110 .procname = "msgmax", 111 .data = &init_ipc_ns.msg_ctlmax, 112 .maxlen = sizeof(init_ipc_ns.msg_ctlmax), 113 .mode = 0644, 114 .proc_handler = proc_dointvec_minmax, 115 .extra1 = SYSCTL_ZERO, 116 .extra2 = SYSCTL_INT_MAX, 117 }, 118 { 119 .procname = "msgmni", 120 .data = &init_ipc_ns.msg_ctlmni, 121 .maxlen = sizeof(init_ipc_ns.msg_ctlmni), 122 .mode = 0644, 123 .proc_handler = proc_dointvec_minmax, 124 .extra1 = SYSCTL_ZERO, 125 .extra2 = &ipc_mni, 126 }, 127 { 128 .procname = "auto_msgmni", 129 .data = NULL, 130 .maxlen = sizeof(int), 131 .mode = 0644, 132 .proc_handler = proc_ipc_auto_msgmni, 133 .extra1 = SYSCTL_ZERO, 134 .extra2 = SYSCTL_ONE, 135 }, 136 { 137 .procname = "msgmnb", 138 .data = &init_ipc_ns.msg_ctlmnb, 139 .maxlen = sizeof(init_ipc_ns.msg_ctlmnb), 140 .mode = 0644, 141 .proc_handler = proc_dointvec_minmax, 142 .extra1 = SYSCTL_ZERO, 143 .extra2 = SYSCTL_INT_MAX, 144 }, 145 { 146 .procname = "sem", 147 .data = &init_ipc_ns.sem_ctls, 148 .maxlen = 4*sizeof(int), 149 .mode = 0644, 150 .proc_handler = proc_ipc_sem_dointvec, 151 }, 152 #ifdef CONFIG_CHECKPOINT_RESTORE 153 { 154 .procname = "sem_next_id", 155 .data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id, 156 .maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id), 157 .mode = 0444, 158 .proc_handler = proc_dointvec_minmax, 159 .extra1 = SYSCTL_ZERO, 160 .extra2 = SYSCTL_INT_MAX, 161 }, 162 { 163 .procname = "msg_next_id", 164 .data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id, 165 .maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id), 166 .mode = 0444, 167 .proc_handler = proc_dointvec_minmax, 168 .extra1 = SYSCTL_ZERO, 169 .extra2 = SYSCTL_INT_MAX, 170 }, 171 { 172 .procname = "shm_next_id", 173 .data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id, 174 .maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id), 175 .mode = 0444, 176 .proc_handler = proc_dointvec_minmax, 177 .extra1 = SYSCTL_ZERO, 178 .extra2 = SYSCTL_INT_MAX, 179 }, 180 #endif 181 }; 182 183 static struct ctl_table_set *set_lookup(struct ctl_table_root *root) 184 { 185 return ¤t->nsproxy->ipc_ns->ipc_set; 186 } 187 188 static int set_is_seen(struct ctl_table_set *set) 189 { 190 return ¤t->nsproxy->ipc_ns->ipc_set == set; 191 } 192 193 static void ipc_set_ownership(struct ctl_table_header *head, 194 kuid_t *uid, kgid_t *gid) 195 { 196 struct ipc_namespace *ns = 197 container_of(head->set, struct ipc_namespace, ipc_set); 198 199 kuid_t ns_root_uid = make_kuid(ns->user_ns, 0); 200 kgid_t ns_root_gid = make_kgid(ns->user_ns, 0); 201 202 *uid = uid_valid(ns_root_uid) ? ns_root_uid : GLOBAL_ROOT_UID; 203 *gid = gid_valid(ns_root_gid) ? ns_root_gid : GLOBAL_ROOT_GID; 204 } 205 206 static int ipc_permissions(struct ctl_table_header *head, const struct ctl_table *table) 207 { 208 int mode = table->mode; 209 210 #ifdef CONFIG_CHECKPOINT_RESTORE 211 struct ipc_namespace *ns = 212 container_of(head->set, struct ipc_namespace, ipc_set); 213 214 if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) || 215 (table->data == &ns->ids[IPC_MSG_IDS].next_id) || 216 (table->data == &ns->ids[IPC_SHM_IDS].next_id)) && 217 checkpoint_restore_ns_capable(ns->user_ns)) 218 mode = 0666; 219 else 220 #endif 221 { 222 kuid_t ns_root_uid; 223 kgid_t ns_root_gid; 224 225 ipc_set_ownership(head, &ns_root_uid, &ns_root_gid); 226 227 if (uid_eq(current_euid(), ns_root_uid)) 228 mode >>= 6; 229 230 else if (in_egroup_p(ns_root_gid)) 231 mode >>= 3; 232 } 233 234 mode &= 7; 235 236 return (mode << 6) | (mode << 3) | mode; 237 } 238 239 static struct ctl_table_root set_root = { 240 .lookup = set_lookup, 241 .permissions = ipc_permissions, 242 .set_ownership = ipc_set_ownership, 243 }; 244 245 bool setup_ipc_sysctls(struct ipc_namespace *ns) 246 { 247 struct ctl_table *tbl; 248 249 setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen); 250 251 tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL); 252 if (tbl) { 253 int i; 254 255 for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) { 256 if (tbl[i].data == &init_ipc_ns.shm_ctlmax) 257 tbl[i].data = &ns->shm_ctlmax; 258 259 else if (tbl[i].data == &init_ipc_ns.shm_ctlall) 260 tbl[i].data = &ns->shm_ctlall; 261 262 else if (tbl[i].data == &init_ipc_ns.shm_ctlmni) 263 tbl[i].data = &ns->shm_ctlmni; 264 265 else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced) 266 tbl[i].data = &ns->shm_rmid_forced; 267 268 else if (tbl[i].data == &init_ipc_ns.msg_ctlmax) 269 tbl[i].data = &ns->msg_ctlmax; 270 271 else if (tbl[i].data == &init_ipc_ns.msg_ctlmni) 272 tbl[i].data = &ns->msg_ctlmni; 273 274 else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb) 275 tbl[i].data = &ns->msg_ctlmnb; 276 277 else if (tbl[i].data == &init_ipc_ns.sem_ctls) 278 tbl[i].data = &ns->sem_ctls; 279 #ifdef CONFIG_CHECKPOINT_RESTORE 280 else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id) 281 tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id; 282 283 else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id) 284 tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id; 285 286 else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id) 287 tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id; 288 #endif 289 else 290 tbl[i].data = NULL; 291 } 292 293 ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl, 294 ARRAY_SIZE(ipc_sysctls)); 295 } 296 if (!ns->ipc_sysctls) { 297 kfree(tbl); 298 retire_sysctl_set(&ns->ipc_set); 299 return false; 300 } 301 302 return true; 303 } 304 305 void retire_ipc_sysctls(struct ipc_namespace *ns) 306 { 307 const struct ctl_table *tbl; 308 309 tbl = ns->ipc_sysctls->ctl_table_arg; 310 unregister_sysctl_table(ns->ipc_sysctls); 311 retire_sysctl_set(&ns->ipc_set); 312 kfree(tbl); 313 } 314 315 static int __init ipc_sysctl_init(void) 316 { 317 if (!setup_ipc_sysctls(&init_ipc_ns)) { 318 pr_warn("ipc sysctl registration failed\n"); 319 return -ENOMEM; 320 } 321 return 0; 322 } 323 324 device_initcall(ipc_sysctl_init); 325 326 static int __init ipc_mni_extend(char *str) 327 { 328 ipc_mni = IPCMNI_EXTEND; 329 ipc_mni_shift = IPCMNI_EXTEND_SHIFT; 330 ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE; 331 pr_info("IPCMNI extended to %d.\n", ipc_mni); 332 return 0; 333 } 334 early_param("ipcmni_extend", ipc_mni_extend); 335
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.