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