Skip to content

Commit 1888e9b

Browse files
committed
Merge tag 'per-namespace-ipc-sysctls-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull ipc sysctl namespace updates from Eric Biederman: "This updates the ipc sysctls so that they are fundamentally per ipc namespace. Previously these sysctls depended upon a hack to simulate being per ipc namespace by looking up the ipc namespace in read or write. With this set of changes the ipc sysctls are registered per ipc namespace and open looks up the ipc namespace. Not only does this series of changes ensure the traditional binding at open time happens, but it sets a foundation for being able to relax the permission checks to allow a user namspace root to change the ipc sysctls for an ipc namespace that the user namespace root requires. To do this requires the ipc namespace to be known at open time" * tag 'per-namespace-ipc-sysctls-for-v5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: ipc: Remove extra braces ipc: Check permissions for checkpoint_restart sysctls at open time ipc: Remove extra1 field abuse to pass ipc namespace ipc: Use the same namespace to modify and validate ipc: Store ipc sysctls in the ipc namespace ipc: Store mqueue sysctls in the ipc namespace
2 parents 07953c5 + 38cd5b1 commit 1888e9b

File tree

5 files changed

+238
-145
lines changed

5 files changed

+238
-145
lines changed

include/linux/ipc_namespace.h

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/ns_common.h>
1111
#include <linux/refcount.h>
1212
#include <linux/rhashtable-types.h>
13+
#include <linux/sysctl.h>
1314

1415
struct user_namespace;
1516

@@ -63,6 +64,12 @@ struct ipc_namespace {
6364
unsigned int mq_msg_default;
6465
unsigned int mq_msgsize_default;
6566

67+
struct ctl_table_set mq_set;
68+
struct ctl_table_header *mq_sysctls;
69+
70+
struct ctl_table_set ipc_set;
71+
struct ctl_table_header *ipc_sysctls;
72+
6673
/* user_ns which owns the ipc ns */
6774
struct user_namespace *user_ns;
6875
struct ucounts *ucounts;
@@ -169,15 +176,37 @@ static inline void put_ipc_ns(struct ipc_namespace *ns)
169176

170177
#ifdef CONFIG_POSIX_MQUEUE_SYSCTL
171178

172-
struct ctl_table_header;
173-
extern struct ctl_table_header *mq_register_sysctl_table(void);
179+
void retire_mq_sysctls(struct ipc_namespace *ns);
180+
bool setup_mq_sysctls(struct ipc_namespace *ns);
174181

175182
#else /* CONFIG_POSIX_MQUEUE_SYSCTL */
176183

177-
static inline struct ctl_table_header *mq_register_sysctl_table(void)
184+
static inline void retire_mq_sysctls(struct ipc_namespace *ns)
178185
{
179-
return NULL;
186+
}
187+
188+
static inline bool setup_mq_sysctls(struct ipc_namespace *ns)
189+
{
190+
return true;
180191
}
181192

182193
#endif /* CONFIG_POSIX_MQUEUE_SYSCTL */
194+
195+
#ifdef CONFIG_SYSVIPC_SYSCTL
196+
197+
bool setup_ipc_sysctls(struct ipc_namespace *ns);
198+
void retire_ipc_sysctls(struct ipc_namespace *ns);
199+
200+
#else /* CONFIG_SYSVIPC_SYSCTL */
201+
202+
static inline void retire_ipc_sysctls(struct ipc_namespace *ns)
203+
{
204+
}
205+
206+
static inline bool setup_ipc_sysctls(struct ipc_namespace *ns)
207+
{
208+
return true;
209+
}
210+
211+
#endif /* CONFIG_SYSVIPC_SYSCTL */
183212
#endif

ipc/ipc_sysctl.c

Lines changed: 125 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,17 @@
1313
#include <linux/capability.h>
1414
#include <linux/ipc_namespace.h>
1515
#include <linux/msg.h>
16+
#include <linux/slab.h>
1617
#include "util.h"
1718

18-
static void *get_ipc(struct ctl_table *table)
19-
{
20-
char *which = table->data;
21-
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
22-
which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns;
23-
return which;
24-
}
25-
26-
static int proc_ipc_dointvec(struct ctl_table *table, int write,
27-
void *buffer, size_t *lenp, loff_t *ppos)
28-
{
29-
struct ctl_table ipc_table;
30-
31-
memcpy(&ipc_table, table, sizeof(ipc_table));
32-
ipc_table.data = get_ipc(table);
33-
34-
return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
35-
}
36-
37-
static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
38-
void *buffer, size_t *lenp, loff_t *ppos)
39-
{
40-
struct ctl_table ipc_table;
41-
42-
memcpy(&ipc_table, table, sizeof(ipc_table));
43-
ipc_table.data = get_ipc(table);
44-
45-
return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
46-
}
47-
4819
static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
4920
void *buffer, size_t *lenp, loff_t *ppos)
5021
{
51-
struct ipc_namespace *ns = current->nsproxy->ipc_ns;
52-
int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
22+
struct ipc_namespace *ns =
23+
container_of(table->data, struct ipc_namespace, shm_rmid_forced);
24+
int err;
25+
26+
err = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
5327

5428
if (err < 0)
5529
return err;
@@ -58,17 +32,6 @@ static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
5832
return err;
5933
}
6034

61-
static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
62-
void *buffer, size_t *lenp, loff_t *ppos)
63-
{
64-
struct ctl_table ipc_table;
65-
memcpy(&ipc_table, table, sizeof(ipc_table));
66-
ipc_table.data = get_ipc(table);
67-
68-
return proc_doulongvec_minmax(&ipc_table, write, buffer,
69-
lenp, ppos);
70-
}
71-
7235
static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
7336
void *buffer, size_t *lenp, loff_t *ppos)
7437
{
@@ -87,14 +50,15 @@ static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
8750
static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
8851
void *buffer, size_t *lenp, loff_t *ppos)
8952
{
53+
struct ipc_namespace *ns =
54+
container_of(table->data, struct ipc_namespace, sem_ctls);
9055
int ret, semmni;
91-
struct ipc_namespace *ns = current->nsproxy->ipc_ns;
9256

9357
semmni = ns->sem_ctls[3];
94-
ret = proc_ipc_dointvec(table, write, buffer, lenp, ppos);
58+
ret = proc_dointvec(table, write, buffer, lenp, ppos);
9559

9660
if (!ret)
97-
ret = sem_check_semmni(current->nsproxy->ipc_ns);
61+
ret = sem_check_semmni(ns);
9862

9963
/*
10064
* Reset the semmni value if an error happens.
@@ -104,44 +68,31 @@ static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
10468
return ret;
10569
}
10670

107-
#ifdef CONFIG_CHECKPOINT_RESTORE
108-
static int proc_ipc_dointvec_minmax_checkpoint_restore(struct ctl_table *table,
109-
int write, void *buffer, size_t *lenp, loff_t *ppos)
110-
{
111-
struct user_namespace *user_ns = current->nsproxy->ipc_ns->user_ns;
112-
113-
if (write && !checkpoint_restore_ns_capable(user_ns))
114-
return -EPERM;
115-
116-
return proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
117-
}
118-
#endif
119-
12071
int ipc_mni = IPCMNI;
12172
int ipc_mni_shift = IPCMNI_SHIFT;
12273
int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
12374

124-
static struct ctl_table ipc_kern_table[] = {
75+
static struct ctl_table ipc_sysctls[] = {
12576
{
12677
.procname = "shmmax",
12778
.data = &init_ipc_ns.shm_ctlmax,
12879
.maxlen = sizeof(init_ipc_ns.shm_ctlmax),
12980
.mode = 0644,
130-
.proc_handler = proc_ipc_doulongvec_minmax,
81+
.proc_handler = proc_doulongvec_minmax,
13182
},
13283
{
13384
.procname = "shmall",
13485
.data = &init_ipc_ns.shm_ctlall,
13586
.maxlen = sizeof(init_ipc_ns.shm_ctlall),
13687
.mode = 0644,
137-
.proc_handler = proc_ipc_doulongvec_minmax,
88+
.proc_handler = proc_doulongvec_minmax,
13889
},
13990
{
14091
.procname = "shmmni",
14192
.data = &init_ipc_ns.shm_ctlmni,
14293
.maxlen = sizeof(init_ipc_ns.shm_ctlmni),
14394
.mode = 0644,
144-
.proc_handler = proc_ipc_dointvec_minmax,
95+
.proc_handler = proc_dointvec_minmax,
14596
.extra1 = SYSCTL_ZERO,
14697
.extra2 = &ipc_mni,
14798
},
@@ -159,7 +110,7 @@ static struct ctl_table ipc_kern_table[] = {
159110
.data = &init_ipc_ns.msg_ctlmax,
160111
.maxlen = sizeof(init_ipc_ns.msg_ctlmax),
161112
.mode = 0644,
162-
.proc_handler = proc_ipc_dointvec_minmax,
113+
.proc_handler = proc_dointvec_minmax,
163114
.extra1 = SYSCTL_ZERO,
164115
.extra2 = SYSCTL_INT_MAX,
165116
},
@@ -168,7 +119,7 @@ static struct ctl_table ipc_kern_table[] = {
168119
.data = &init_ipc_ns.msg_ctlmni,
169120
.maxlen = sizeof(init_ipc_ns.msg_ctlmni),
170121
.mode = 0644,
171-
.proc_handler = proc_ipc_dointvec_minmax,
122+
.proc_handler = proc_dointvec_minmax,
172123
.extra1 = SYSCTL_ZERO,
173124
.extra2 = &ipc_mni,
174125
},
@@ -186,7 +137,7 @@ static struct ctl_table ipc_kern_table[] = {
186137
.data = &init_ipc_ns.msg_ctlmnb,
187138
.maxlen = sizeof(init_ipc_ns.msg_ctlmnb),
188139
.mode = 0644,
189-
.proc_handler = proc_ipc_dointvec_minmax,
140+
.proc_handler = proc_dointvec_minmax,
190141
.extra1 = SYSCTL_ZERO,
191142
.extra2 = SYSCTL_INT_MAX,
192143
},
@@ -202,45 +153,139 @@ static struct ctl_table ipc_kern_table[] = {
202153
.procname = "sem_next_id",
203154
.data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id,
204155
.maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
205-
.mode = 0666,
206-
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
156+
.mode = 0444,
157+
.proc_handler = proc_dointvec_minmax,
207158
.extra1 = SYSCTL_ZERO,
208159
.extra2 = SYSCTL_INT_MAX,
209160
},
210161
{
211162
.procname = "msg_next_id",
212163
.data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id,
213164
.maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
214-
.mode = 0666,
215-
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
165+
.mode = 0444,
166+
.proc_handler = proc_dointvec_minmax,
216167
.extra1 = SYSCTL_ZERO,
217168
.extra2 = SYSCTL_INT_MAX,
218169
},
219170
{
220171
.procname = "shm_next_id",
221172
.data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id,
222173
.maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
223-
.mode = 0666,
224-
.proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore,
174+
.mode = 0444,
175+
.proc_handler = proc_dointvec_minmax,
225176
.extra1 = SYSCTL_ZERO,
226177
.extra2 = SYSCTL_INT_MAX,
227178
},
228179
#endif
229180
{}
230181
};
231182

232-
static struct ctl_table ipc_root_table[] = {
233-
{
234-
.procname = "kernel",
235-
.mode = 0555,
236-
.child = ipc_kern_table,
237-
},
238-
{}
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 int ipc_permissions(struct ctl_table_header *head, struct ctl_table *table)
194+
{
195+
int mode = table->mode;
196+
197+
#ifdef CONFIG_CHECKPOINT_RESTORE
198+
struct ipc_namespace *ns = current->nsproxy->ipc_ns;
199+
200+
if (((table->data == &ns->ids[IPC_SEM_IDS].next_id) ||
201+
(table->data == &ns->ids[IPC_MSG_IDS].next_id) ||
202+
(table->data == &ns->ids[IPC_SHM_IDS].next_id)) &&
203+
checkpoint_restore_ns_capable(ns->user_ns))
204+
mode = 0666;
205+
#endif
206+
return mode;
207+
}
208+
209+
static struct ctl_table_root set_root = {
210+
.lookup = set_lookup,
211+
.permissions = ipc_permissions,
239212
};
240213

214+
bool setup_ipc_sysctls(struct ipc_namespace *ns)
215+
{
216+
struct ctl_table *tbl;
217+
218+
setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen);
219+
220+
tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL);
221+
if (tbl) {
222+
int i;
223+
224+
for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) {
225+
if (tbl[i].data == &init_ipc_ns.shm_ctlmax)
226+
tbl[i].data = &ns->shm_ctlmax;
227+
228+
else if (tbl[i].data == &init_ipc_ns.shm_ctlall)
229+
tbl[i].data = &ns->shm_ctlall;
230+
231+
else if (tbl[i].data == &init_ipc_ns.shm_ctlmni)
232+
tbl[i].data = &ns->shm_ctlmni;
233+
234+
else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced)
235+
tbl[i].data = &ns->shm_rmid_forced;
236+
237+
else if (tbl[i].data == &init_ipc_ns.msg_ctlmax)
238+
tbl[i].data = &ns->msg_ctlmax;
239+
240+
else if (tbl[i].data == &init_ipc_ns.msg_ctlmni)
241+
tbl[i].data = &ns->msg_ctlmni;
242+
243+
else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb)
244+
tbl[i].data = &ns->msg_ctlmnb;
245+
246+
else if (tbl[i].data == &init_ipc_ns.sem_ctls)
247+
tbl[i].data = &ns->sem_ctls;
248+
#ifdef CONFIG_CHECKPOINT_RESTORE
249+
else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id)
250+
tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id;
251+
252+
else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id)
253+
tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
254+
255+
else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id)
256+
tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
257+
#endif
258+
else
259+
tbl[i].data = NULL;
260+
}
261+
262+
ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl);
263+
}
264+
if (!ns->ipc_sysctls) {
265+
kfree(tbl);
266+
retire_sysctl_set(&ns->ipc_set);
267+
return false;
268+
}
269+
270+
return true;
271+
}
272+
273+
void retire_ipc_sysctls(struct ipc_namespace *ns)
274+
{
275+
struct ctl_table *tbl;
276+
277+
tbl = ns->ipc_sysctls->ctl_table_arg;
278+
unregister_sysctl_table(ns->ipc_sysctls);
279+
retire_sysctl_set(&ns->ipc_set);
280+
kfree(tbl);
281+
}
282+
241283
static int __init ipc_sysctl_init(void)
242284
{
243-
register_sysctl_table(ipc_root_table);
285+
if (!setup_ipc_sysctls(&init_ipc_ns)) {
286+
pr_warn("ipc sysctl registration failed\n");
287+
return -ENOMEM;
288+
}
244289
return 0;
245290
}
246291

0 commit comments

Comments
 (0)