1 /* 2 * Copyright (C) 2007 3 * 4 * Author: Eric Biederman <ebiederm@xmision.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation, version 2 of the 9 * License. 10 */ 11 12 #include <linux/module.h> 13 #include <linux/ipc.h> 14 #include <linux/nsproxy.h> 15 #include <linux/sysctl.h> 16 #include <linux/uaccess.h> 17 #include <linux/ipc_namespace.h> 18 #include <linux/msg.h> 19 #include "util.h" 20 21 static void *get_ipc(struct ctl_table *table) 22 { 23 char *which = table->data; 24 struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; 25 which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns; 26 return which; 27 } 28 29 #ifdef CONFIG_PROC_SYSCTL 30 static int proc_ipc_dointvec(struct ctl_table *table, int write, 31 void __user *buffer, size_t *lenp, loff_t *ppos) 32 { 33 struct ctl_table ipc_table; 34 35 memcpy(&ipc_table, table, sizeof(ipc_table)); 36 ipc_table.data = get_ipc(table); 37 38 return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); 39 } 40 41 static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write, 42 void __user *buffer, size_t *lenp, loff_t *ppos) 43 { 44 struct ctl_table ipc_table; 45 46 memcpy(&ipc_table, table, sizeof(ipc_table)); 47 ipc_table.data = get_ipc(table); 48 49 return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); 50 } 51 52 static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write, 53 void __user *buffer, size_t *lenp, loff_t *ppos) 54 { 55 struct ipc_namespace *ns = current->nsproxy->ipc_ns; 56 int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos); 57 58 if (err < 0) 59 return err; 60 if (ns->shm_rmid_forced) 61 shm_destroy_orphaned(ns); 62 return err; 63 } 64 65 static int proc_ipc_callback_dointvec_minmax(struct ctl_table *table, int write, 66 void __user *buffer, size_t *lenp, loff_t *ppos) 67 { 68 struct ctl_table ipc_table; 69 size_t lenp_bef = *lenp; 70 int rc; 71 72 memcpy(&ipc_table, table, sizeof(ipc_table)); 73 ipc_table.data = get_ipc(table); 74 75 rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); 76 77 if (write && !rc && lenp_bef == *lenp) 78 /* 79 * Tunable has successfully been changed by hand. Disable its 80 * automatic adjustment. This simply requires unregistering 81 * the notifiers that trigger recalculation. 82 */ 83 unregister_ipcns_notifier(current->nsproxy->ipc_ns); 84 85 return rc; 86 } 87 88 static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write, 89 void __user *buffer, size_t *lenp, loff_t *ppos) 90 { 91 struct ctl_table ipc_table; 92 memcpy(&ipc_table, table, sizeof(ipc_table)); 93 ipc_table.data = get_ipc(table); 94 95 return proc_doulongvec_minmax(&ipc_table, write, buffer, 96 lenp, ppos); 97 } 98 99 /* 100 * Routine that is called when the file "auto_msgmni" has successfully been 101 * written. 102 * Two values are allowed: 103 * 0: unregister msgmni's callback routine from the ipc namespace notifier 104 * chain. This means that msgmni won't be recomputed anymore upon memory 105 * add/remove or ipc namespace creation/removal. 106 * 1: register back the callback routine. 107 */ 108 static void ipc_auto_callback(int val) 109 { 110 if (!val) 111 unregister_ipcns_notifier(current->nsproxy->ipc_ns); 112 else { 113 /* 114 * Re-enable automatic recomputing only if not already 115 * enabled. 116 */ 117 recompute_msgmni(current->nsproxy->ipc_ns); 118 cond_register_ipcns_notifier(current->nsproxy->ipc_ns); 119 } 120 } 121 122 static int proc_ipcauto_dointvec_minmax(struct ctl_table *table, int write, 123 void __user *buffer, size_t *lenp, loff_t *ppos) 124 { 125 struct ctl_table ipc_table; 126 size_t lenp_bef = *lenp; 127 int oldval; 128 int rc; 129 130 memcpy(&ipc_table, table, sizeof(ipc_table)); 131 ipc_table.data = get_ipc(table); 132 oldval = *((int *)(ipc_table.data)); 133 134 rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); 135 136 if (write && !rc && lenp_bef == *lenp) { 137 int newval = *((int *)(ipc_table.data)); 138 /* 139 * The file "auto_msgmni" has correctly been set. 140 * React by (un)registering the corresponding tunable, if the 141 * value has changed. 142 */ 143 if (newval != oldval) 144 ipc_auto_callback(newval); 145 } 146 147 return rc; 148 } 149 150 #else 151 #define proc_ipc_doulongvec_minmax NULL 152 #define proc_ipc_dointvec NULL 153 #define proc_ipc_dointvec_minmax NULL 154 #define proc_ipc_dointvec_minmax_orphans NULL 155 #define proc_ipc_callback_dointvec_minmax NULL 156 #define proc_ipcauto_dointvec_minmax NULL 157 #endif 158 159 static int zero; 160 static int one = 1; 161 static int int_max = INT_MAX; 162 163 static struct ctl_table ipc_kern_table[] = { 164 { 165 .procname = "shmmax", 166 .data = &init_ipc_ns.shm_ctlmax, 167 .maxlen = sizeof(init_ipc_ns.shm_ctlmax), 168 .mode = 0644, 169 .proc_handler = proc_ipc_doulongvec_minmax, 170 }, 171 { 172 .procname = "shmall", 173 .data = &init_ipc_ns.shm_ctlall, 174 .maxlen = sizeof(init_ipc_ns.shm_ctlall), 175 .mode = 0644, 176 .proc_handler = proc_ipc_doulongvec_minmax, 177 }, 178 { 179 .procname = "shmmni", 180 .data = &init_ipc_ns.shm_ctlmni, 181 .maxlen = sizeof(init_ipc_ns.shm_ctlmni), 182 .mode = 0644, 183 .proc_handler = proc_ipc_dointvec, 184 }, 185 { 186 .procname = "shm_rmid_forced", 187 .data = &init_ipc_ns.shm_rmid_forced, 188 .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), 189 .mode = 0644, 190 .proc_handler = proc_ipc_dointvec_minmax_orphans, 191 .extra1 = &zero, 192 .extra2 = &one, 193 }, 194 { 195 .procname = "msgmax", 196 .data = &init_ipc_ns.msg_ctlmax, 197 .maxlen = sizeof(init_ipc_ns.msg_ctlmax), 198 .mode = 0644, 199 .proc_handler = proc_ipc_dointvec_minmax, 200 .extra1 = &zero, 201 .extra2 = &int_max, 202 }, 203 { 204 .procname = "msgmni", 205 .data = &init_ipc_ns.msg_ctlmni, 206 .maxlen = sizeof(init_ipc_ns.msg_ctlmni), 207 .mode = 0644, 208 .proc_handler = proc_ipc_callback_dointvec_minmax, 209 .extra1 = &zero, 210 .extra2 = &int_max, 211 }, 212 { 213 .procname = "msgmnb", 214 .data = &init_ipc_ns.msg_ctlmnb, 215 .maxlen = sizeof(init_ipc_ns.msg_ctlmnb), 216 .mode = 0644, 217 .proc_handler = proc_ipc_dointvec_minmax, 218 .extra1 = &zero, 219 .extra2 = &int_max, 220 }, 221 { 222 .procname = "sem", 223 .data = &init_ipc_ns.sem_ctls, 224 .maxlen = 4*sizeof(int), 225 .mode = 0644, 226 .proc_handler = proc_ipc_dointvec, 227 }, 228 { 229 .procname = "auto_msgmni", 230 .data = &init_ipc_ns.auto_msgmni, 231 .maxlen = sizeof(int), 232 .mode = 0644, 233 .proc_handler = proc_ipcauto_dointvec_minmax, 234 .extra1 = &zero, 235 .extra2 = &one, 236 }, 237 #ifdef CONFIG_CHECKPOINT_RESTORE 238 { 239 .procname = "sem_next_id", 240 .data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id, 241 .maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id), 242 .mode = 0644, 243 .proc_handler = proc_ipc_dointvec_minmax, 244 .extra1 = &zero, 245 .extra2 = &int_max, 246 }, 247 { 248 .procname = "msg_next_id", 249 .data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id, 250 .maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id), 251 .mode = 0644, 252 .proc_handler = proc_ipc_dointvec_minmax, 253 .extra1 = &zero, 254 .extra2 = &int_max, 255 }, 256 { 257 .procname = "shm_next_id", 258 .data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id, 259 .maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id), 260 .mode = 0644, 261 .proc_handler = proc_ipc_dointvec_minmax, 262 .extra1 = &zero, 263 .extra2 = &int_max, 264 }, 265 #endif 266 {} 267 }; 268 269 static struct ctl_table ipc_root_table[] = { 270 { 271 .procname = "kernel", 272 .mode = 0555, 273 .child = ipc_kern_table, 274 }, 275 {} 276 }; 277 278 static int __init ipc_sysctl_init(void) 279 { 280 register_sysctl_table(ipc_root_table); 281 return 0; 282 } 283 284 device_initcall(ipc_sysctl_init); 285