Commit | Line | Data |
---|---|---|
b886d83c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
a5494dcd EB |
2 | /* |
3 | * Copyright (C) 2007 | |
4 | * | |
5 | * Author: Eric Biederman <ebiederm@xmision.com> | |
a5494dcd EB |
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> | |
5563cabd | 13 | #include <linux/capability.h> |
ae5e1b22 | 14 | #include <linux/ipc_namespace.h> |
6546bc42 | 15 | #include <linux/msg.h> |
1f5c135e | 16 | #include <linux/slab.h> |
6546bc42 | 17 | #include "util.h" |
a5494dcd | 18 | |
1f5c135e | 19 | static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write, |
32927393 | 20 | void *buffer, size_t *lenp, loff_t *ppos) |
b34a6b1d | 21 | { |
1f5c135e | 22 | struct ipc_namespace *ns = table->extra1; |
b34a6b1d | 23 | struct ctl_table ipc_table; |
1f5c135e | 24 | int err; |
b34a6b1d VK |
25 | |
26 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
b34a6b1d | 27 | |
1f5c135e AG |
28 | ipc_table.extra1 = SYSCTL_ZERO; |
29 | ipc_table.extra2 = SYSCTL_ONE; | |
b34a6b1d | 30 | |
1f5c135e | 31 | err = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); |
b34a6b1d VK |
32 | |
33 | if (err < 0) | |
34 | return err; | |
35 | if (ns->shm_rmid_forced) | |
36 | shm_destroy_orphaned(ns); | |
37 | return err; | |
38 | } | |
39 | ||
0050ee05 | 40 | static int proc_ipc_auto_msgmni(struct ctl_table *table, int write, |
32927393 | 41 | void *buffer, size_t *lenp, loff_t *ppos) |
9eefe520 ND |
42 | { |
43 | struct ctl_table ipc_table; | |
0050ee05 | 44 | int dummy = 0; |
9eefe520 ND |
45 | |
46 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
0050ee05 MS |
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); | |
9eefe520 ND |
53 | } |
54 | ||
8c81ddd2 | 55 | static int proc_ipc_sem_dointvec(struct ctl_table *table, int write, |
fff1662c | 56 | void *buffer, size_t *lenp, loff_t *ppos) |
8c81ddd2 | 57 | { |
1f5c135e AG |
58 | struct ipc_namespace *ns = table->extra1; |
59 | struct ctl_table ipc_table; | |
8c81ddd2 | 60 | int ret, semmni; |
1f5c135e AG |
61 | |
62 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
63 | ||
64 | ipc_table.extra1 = NULL; | |
65 | ipc_table.extra2 = NULL; | |
8c81ddd2 WL |
66 | |
67 | semmni = ns->sem_ctls[3]; | |
1f5c135e | 68 | ret = proc_dointvec(table, write, buffer, lenp, ppos); |
8c81ddd2 WL |
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 | ||
5563cabd MC |
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 | { | |
1f5c135e AG |
85 | struct ipc_namespace *ns = table->extra1; |
86 | struct ctl_table ipc_table; | |
5563cabd | 87 | |
1f5c135e | 88 | if (write && !checkpoint_restore_ns_capable(ns->user_ns)) |
5563cabd MC |
89 | return -EPERM; |
90 | ||
1f5c135e AG |
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); | |
5563cabd MC |
97 | } |
98 | #endif | |
99 | ||
5ac893b8 WL |
100 | int ipc_mni = IPCMNI; |
101 | int ipc_mni_shift = IPCMNI_SHIFT; | |
99db46ea | 102 | int ipc_min_cycle = RADIX_TREE_MAP_SIZE; |
9eefe520 | 103 | |
1f5c135e | 104 | static struct ctl_table ipc_sysctls[] = { |
a5494dcd | 105 | { |
a5494dcd EB |
106 | .procname = "shmmax", |
107 | .data = &init_ipc_ns.shm_ctlmax, | |
239521f3 | 108 | .maxlen = sizeof(init_ipc_ns.shm_ctlmax), |
a5494dcd | 109 | .mode = 0644, |
1f5c135e | 110 | .proc_handler = proc_doulongvec_minmax, |
a5494dcd EB |
111 | }, |
112 | { | |
a5494dcd EB |
113 | .procname = "shmall", |
114 | .data = &init_ipc_ns.shm_ctlall, | |
239521f3 | 115 | .maxlen = sizeof(init_ipc_ns.shm_ctlall), |
a5494dcd | 116 | .mode = 0644, |
1f5c135e | 117 | .proc_handler = proc_doulongvec_minmax, |
a5494dcd EB |
118 | }, |
119 | { | |
a5494dcd EB |
120 | .procname = "shmmni", |
121 | .data = &init_ipc_ns.shm_ctlmni, | |
239521f3 | 122 | .maxlen = sizeof(init_ipc_ns.shm_ctlmni), |
a5494dcd | 123 | .mode = 0644, |
1f5c135e | 124 | .proc_handler = proc_dointvec_minmax, |
eec4844f | 125 | .extra1 = SYSCTL_ZERO, |
6730e658 | 126 | .extra2 = &ipc_mni, |
a5494dcd | 127 | }, |
b34a6b1d VK |
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, | |
b34a6b1d | 134 | }, |
a5494dcd | 135 | { |
a5494dcd EB |
136 | .procname = "msgmax", |
137 | .data = &init_ipc_ns.msg_ctlmax, | |
239521f3 | 138 | .maxlen = sizeof(init_ipc_ns.msg_ctlmax), |
a5494dcd | 139 | .mode = 0644, |
1f5c135e | 140 | .proc_handler = proc_dointvec_minmax, |
eec4844f MC |
141 | .extra1 = SYSCTL_ZERO, |
142 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
143 | }, |
144 | { | |
a5494dcd EB |
145 | .procname = "msgmni", |
146 | .data = &init_ipc_ns.msg_ctlmni, | |
239521f3 | 147 | .maxlen = sizeof(init_ipc_ns.msg_ctlmni), |
a5494dcd | 148 | .mode = 0644, |
1f5c135e | 149 | .proc_handler = proc_dointvec_minmax, |
eec4844f | 150 | .extra1 = SYSCTL_ZERO, |
6730e658 | 151 | .extra2 = &ipc_mni, |
a5494dcd | 152 | }, |
0050ee05 MS |
153 | { |
154 | .procname = "auto_msgmni", | |
155 | .data = NULL, | |
156 | .maxlen = sizeof(int), | |
157 | .mode = 0644, | |
158 | .proc_handler = proc_ipc_auto_msgmni, | |
eec4844f MC |
159 | .extra1 = SYSCTL_ZERO, |
160 | .extra2 = SYSCTL_ONE, | |
0050ee05 | 161 | }, |
a5494dcd | 162 | { |
a5494dcd EB |
163 | .procname = "msgmnb", |
164 | .data = &init_ipc_ns.msg_ctlmnb, | |
239521f3 | 165 | .maxlen = sizeof(init_ipc_ns.msg_ctlmnb), |
a5494dcd | 166 | .mode = 0644, |
1f5c135e | 167 | .proc_handler = proc_dointvec_minmax, |
eec4844f MC |
168 | .extra1 = SYSCTL_ZERO, |
169 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
170 | }, |
171 | { | |
a5494dcd EB |
172 | .procname = "sem", |
173 | .data = &init_ipc_ns.sem_ctls, | |
239521f3 | 174 | .maxlen = 4*sizeof(int), |
a5494dcd | 175 | .mode = 0644, |
8c81ddd2 | 176 | .proc_handler = proc_ipc_sem_dointvec, |
a5494dcd | 177 | }, |
03f59566 SK |
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), | |
5563cabd MC |
183 | .mode = 0666, |
184 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
03f59566 SK |
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), | |
5563cabd MC |
190 | .mode = 0666, |
191 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
03f59566 SK |
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), | |
5563cabd MC |
197 | .mode = 0666, |
198 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
03f59566 SK |
199 | }, |
200 | #endif | |
a5494dcd EB |
201 | {} |
202 | }; | |
203 | ||
1f5c135e AG |
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, | |
a5494dcd EB |
216 | }; |
217 | ||
1f5c135e AG |
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 | ||
a5494dcd EB |
293 | static int __init ipc_sysctl_init(void) |
294 | { | |
1f5c135e AG |
295 | if (!setup_ipc_sysctls(&init_ipc_ns)) { |
296 | pr_warn("ipc sysctl registration failed\n"); | |
297 | return -ENOMEM; | |
298 | } | |
a5494dcd EB |
299 | return 0; |
300 | } | |
301 | ||
6d08a256 | 302 | device_initcall(ipc_sysctl_init); |
5ac893b8 WL |
303 | |
304 | static int __init ipc_mni_extend(char *str) | |
305 | { | |
306 | ipc_mni = IPCMNI_EXTEND; | |
307 | ipc_mni_shift = IPCMNI_EXTEND_SHIFT; | |
99db46ea | 308 | ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE; |
5ac893b8 WL |
309 | pr_info("IPCMNI extended to %d.\n", ipc_mni); |
310 | return 0; | |
311 | } | |
312 | early_param("ipcmni_extend", ipc_mni_extend); |