ipc: Store ipc sysctls in the ipc namespace
[linux-block.git] / ipc / ipc_sysctl.c
CommitLineData
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 19static 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 40static 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 55static 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
82static 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
100int ipc_mni = IPCMNI;
101int ipc_mni_shift = IPCMNI_SHIFT;
99db46ea 102int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
9eefe520 103
1f5c135e 104static 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
204static struct ctl_table_set *set_lookup(struct ctl_table_root *root)
205{
206 return &current->nsproxy->ipc_ns->ipc_set;
207}
208
209static int set_is_seen(struct ctl_table_set *set)
210{
211 return &current->nsproxy->ipc_ns->ipc_set == set;
212}
213
214static struct ctl_table_root set_root = {
215 .lookup = set_lookup,
a5494dcd
EB
216};
217
1f5c135e
AG
218bool 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
283void 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
293static 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 302device_initcall(ipc_sysctl_init);
5ac893b8
WL
303
304static 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}
312early_param("ipcmni_extend", ipc_mni_extend);