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