ipc: Store ipc sysctls in the ipc namespace
[linux-2.6-microblaze.git] / ipc / ipc_sysctl.c
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 "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(current->nsproxy->ipc_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 &current->nsproxy->ipc_ns->ipc_set;
207 }
208
209 static int set_is_seen(struct ctl_table_set *set)
210 {
211         return &current->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);