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