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 ND |
15 | #include <linux/msg.h> |
16 | #include "util.h" | |
a5494dcd | 17 | |
a5c5928b | 18 | static void *get_ipc(struct ctl_table *table) |
a5494dcd EB |
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 | } | |
a5494dcd | 25 | |
11dea190 | 26 | #ifdef CONFIG_PROC_SYSCTL |
a5c5928b | 27 | static int proc_ipc_dointvec(struct ctl_table *table, int write, |
32927393 | 28 | void *buffer, size_t *lenp, loff_t *ppos) |
a5494dcd EB |
29 | { |
30 | struct ctl_table ipc_table; | |
b34a6b1d | 31 | |
a5494dcd EB |
32 | memcpy(&ipc_table, table, sizeof(ipc_table)); |
33 | ipc_table.data = get_ipc(table); | |
34 | ||
8d65af78 | 35 | return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); |
a5494dcd EB |
36 | } |
37 | ||
a5c5928b | 38 | static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write, |
32927393 | 39 | void *buffer, size_t *lenp, loff_t *ppos) |
b34a6b1d VK |
40 | { |
41 | struct ctl_table ipc_table; | |
42 | ||
43 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
44 | ipc_table.data = get_ipc(table); | |
45 | ||
46 | return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); | |
47 | } | |
48 | ||
a5c5928b | 49 | static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write, |
32927393 | 50 | void *buffer, size_t *lenp, loff_t *ppos) |
b34a6b1d VK |
51 | { |
52 | struct ipc_namespace *ns = current->nsproxy->ipc_ns; | |
53 | int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos); | |
54 | ||
55 | if (err < 0) | |
56 | return err; | |
57 | if (ns->shm_rmid_forced) | |
58 | shm_destroy_orphaned(ns); | |
59 | return err; | |
60 | } | |
61 | ||
a5c5928b | 62 | static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write, |
32927393 | 63 | void *buffer, size_t *lenp, loff_t *ppos) |
a5494dcd EB |
64 | { |
65 | struct ctl_table ipc_table; | |
66 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
67 | ipc_table.data = get_ipc(table); | |
68 | ||
8d65af78 | 69 | return proc_doulongvec_minmax(&ipc_table, write, buffer, |
a5494dcd EB |
70 | lenp, ppos); |
71 | } | |
72 | ||
0050ee05 | 73 | static int proc_ipc_auto_msgmni(struct ctl_table *table, int write, |
32927393 | 74 | void *buffer, size_t *lenp, loff_t *ppos) |
9eefe520 ND |
75 | { |
76 | struct ctl_table ipc_table; | |
0050ee05 | 77 | int dummy = 0; |
9eefe520 ND |
78 | |
79 | memcpy(&ipc_table, table, sizeof(ipc_table)); | |
0050ee05 MS |
80 | ipc_table.data = &dummy; |
81 | ||
82 | if (write) | |
83 | pr_info_once("writing to auto_msgmni has no effect"); | |
84 | ||
85 | return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); | |
9eefe520 ND |
86 | } |
87 | ||
8c81ddd2 | 88 | static int proc_ipc_sem_dointvec(struct ctl_table *table, int write, |
fff1662c | 89 | void *buffer, size_t *lenp, loff_t *ppos) |
8c81ddd2 WL |
90 | { |
91 | int ret, semmni; | |
92 | struct ipc_namespace *ns = current->nsproxy->ipc_ns; | |
93 | ||
94 | semmni = ns->sem_ctls[3]; | |
95 | ret = proc_ipc_dointvec(table, write, buffer, lenp, ppos); | |
96 | ||
97 | if (!ret) | |
98 | ret = sem_check_semmni(current->nsproxy->ipc_ns); | |
99 | ||
100 | /* | |
101 | * Reset the semmni value if an error happens. | |
102 | */ | |
103 | if (ret) | |
104 | ns->sem_ctls[3] = semmni; | |
105 | return ret; | |
106 | } | |
107 | ||
5563cabd MC |
108 | #ifdef CONFIG_CHECKPOINT_RESTORE |
109 | static int proc_ipc_dointvec_minmax_checkpoint_restore(struct ctl_table *table, | |
110 | int write, void *buffer, size_t *lenp, loff_t *ppos) | |
111 | { | |
112 | struct user_namespace *user_ns = current->nsproxy->ipc_ns->user_ns; | |
113 | ||
114 | if (write && !checkpoint_restore_ns_capable(user_ns)) | |
115 | return -EPERM; | |
116 | ||
117 | return proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos); | |
118 | } | |
119 | #endif | |
120 | ||
a5494dcd EB |
121 | #else |
122 | #define proc_ipc_doulongvec_minmax NULL | |
123 | #define proc_ipc_dointvec NULL | |
b34a6b1d VK |
124 | #define proc_ipc_dointvec_minmax NULL |
125 | #define proc_ipc_dointvec_minmax_orphans NULL | |
0050ee05 | 126 | #define proc_ipc_auto_msgmni NULL |
8c81ddd2 | 127 | #define proc_ipc_sem_dointvec NULL |
5563cabd MC |
128 | #ifdef CONFIG_CHECKPOINT_RESTORE |
129 | #define proc_ipc_dointvec_minmax_checkpoint_restore NULL | |
130 | #endif /* CONFIG_CHECKPOINT_RESTORE */ | |
a5494dcd EB |
131 | #endif |
132 | ||
5ac893b8 WL |
133 | int ipc_mni = IPCMNI; |
134 | int ipc_mni_shift = IPCMNI_SHIFT; | |
99db46ea | 135 | int ipc_min_cycle = RADIX_TREE_MAP_SIZE; |
9eefe520 | 136 | |
a5494dcd EB |
137 | static struct ctl_table ipc_kern_table[] = { |
138 | { | |
a5494dcd EB |
139 | .procname = "shmmax", |
140 | .data = &init_ipc_ns.shm_ctlmax, | |
239521f3 | 141 | .maxlen = sizeof(init_ipc_ns.shm_ctlmax), |
a5494dcd EB |
142 | .mode = 0644, |
143 | .proc_handler = proc_ipc_doulongvec_minmax, | |
a5494dcd EB |
144 | }, |
145 | { | |
a5494dcd EB |
146 | .procname = "shmall", |
147 | .data = &init_ipc_ns.shm_ctlall, | |
239521f3 | 148 | .maxlen = sizeof(init_ipc_ns.shm_ctlall), |
a5494dcd EB |
149 | .mode = 0644, |
150 | .proc_handler = proc_ipc_doulongvec_minmax, | |
a5494dcd EB |
151 | }, |
152 | { | |
a5494dcd EB |
153 | .procname = "shmmni", |
154 | .data = &init_ipc_ns.shm_ctlmni, | |
239521f3 | 155 | .maxlen = sizeof(init_ipc_ns.shm_ctlmni), |
a5494dcd | 156 | .mode = 0644, |
6730e658 | 157 | .proc_handler = proc_ipc_dointvec_minmax, |
eec4844f | 158 | .extra1 = SYSCTL_ZERO, |
6730e658 | 159 | .extra2 = &ipc_mni, |
a5494dcd | 160 | }, |
b34a6b1d VK |
161 | { |
162 | .procname = "shm_rmid_forced", | |
163 | .data = &init_ipc_ns.shm_rmid_forced, | |
164 | .maxlen = sizeof(init_ipc_ns.shm_rmid_forced), | |
165 | .mode = 0644, | |
166 | .proc_handler = proc_ipc_dointvec_minmax_orphans, | |
eec4844f MC |
167 | .extra1 = SYSCTL_ZERO, |
168 | .extra2 = SYSCTL_ONE, | |
b34a6b1d | 169 | }, |
a5494dcd | 170 | { |
a5494dcd EB |
171 | .procname = "msgmax", |
172 | .data = &init_ipc_ns.msg_ctlmax, | |
239521f3 | 173 | .maxlen = sizeof(init_ipc_ns.msg_ctlmax), |
a5494dcd | 174 | .mode = 0644, |
9bf76ca3 | 175 | .proc_handler = proc_ipc_dointvec_minmax, |
eec4844f MC |
176 | .extra1 = SYSCTL_ZERO, |
177 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
178 | }, |
179 | { | |
a5494dcd EB |
180 | .procname = "msgmni", |
181 | .data = &init_ipc_ns.msg_ctlmni, | |
239521f3 | 182 | .maxlen = sizeof(init_ipc_ns.msg_ctlmni), |
a5494dcd | 183 | .mode = 0644, |
0050ee05 | 184 | .proc_handler = proc_ipc_dointvec_minmax, |
eec4844f | 185 | .extra1 = SYSCTL_ZERO, |
6730e658 | 186 | .extra2 = &ipc_mni, |
a5494dcd | 187 | }, |
0050ee05 MS |
188 | { |
189 | .procname = "auto_msgmni", | |
190 | .data = NULL, | |
191 | .maxlen = sizeof(int), | |
192 | .mode = 0644, | |
193 | .proc_handler = proc_ipc_auto_msgmni, | |
eec4844f MC |
194 | .extra1 = SYSCTL_ZERO, |
195 | .extra2 = SYSCTL_ONE, | |
0050ee05 | 196 | }, |
a5494dcd | 197 | { |
a5494dcd EB |
198 | .procname = "msgmnb", |
199 | .data = &init_ipc_ns.msg_ctlmnb, | |
239521f3 | 200 | .maxlen = sizeof(init_ipc_ns.msg_ctlmnb), |
a5494dcd | 201 | .mode = 0644, |
9bf76ca3 | 202 | .proc_handler = proc_ipc_dointvec_minmax, |
eec4844f MC |
203 | .extra1 = SYSCTL_ZERO, |
204 | .extra2 = SYSCTL_INT_MAX, | |
a5494dcd EB |
205 | }, |
206 | { | |
a5494dcd EB |
207 | .procname = "sem", |
208 | .data = &init_ipc_ns.sem_ctls, | |
239521f3 | 209 | .maxlen = 4*sizeof(int), |
a5494dcd | 210 | .mode = 0644, |
8c81ddd2 | 211 | .proc_handler = proc_ipc_sem_dointvec, |
a5494dcd | 212 | }, |
03f59566 SK |
213 | #ifdef CONFIG_CHECKPOINT_RESTORE |
214 | { | |
215 | .procname = "sem_next_id", | |
216 | .data = &init_ipc_ns.ids[IPC_SEM_IDS].next_id, | |
217 | .maxlen = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id), | |
5563cabd MC |
218 | .mode = 0666, |
219 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
eec4844f MC |
220 | .extra1 = SYSCTL_ZERO, |
221 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
222 | }, |
223 | { | |
224 | .procname = "msg_next_id", | |
225 | .data = &init_ipc_ns.ids[IPC_MSG_IDS].next_id, | |
226 | .maxlen = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id), | |
5563cabd MC |
227 | .mode = 0666, |
228 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
eec4844f MC |
229 | .extra1 = SYSCTL_ZERO, |
230 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
231 | }, |
232 | { | |
233 | .procname = "shm_next_id", | |
234 | .data = &init_ipc_ns.ids[IPC_SHM_IDS].next_id, | |
235 | .maxlen = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id), | |
5563cabd MC |
236 | .mode = 0666, |
237 | .proc_handler = proc_ipc_dointvec_minmax_checkpoint_restore, | |
eec4844f MC |
238 | .extra1 = SYSCTL_ZERO, |
239 | .extra2 = SYSCTL_INT_MAX, | |
03f59566 SK |
240 | }, |
241 | #endif | |
a5494dcd EB |
242 | {} |
243 | }; | |
244 | ||
245 | static struct ctl_table ipc_root_table[] = { | |
246 | { | |
a5494dcd EB |
247 | .procname = "kernel", |
248 | .mode = 0555, | |
249 | .child = ipc_kern_table, | |
250 | }, | |
251 | {} | |
252 | }; | |
253 | ||
254 | static int __init ipc_sysctl_init(void) | |
255 | { | |
0b4d4147 | 256 | register_sysctl_table(ipc_root_table); |
a5494dcd EB |
257 | return 0; |
258 | } | |
259 | ||
6d08a256 | 260 | device_initcall(ipc_sysctl_init); |
5ac893b8 WL |
261 | |
262 | static int __init ipc_mni_extend(char *str) | |
263 | { | |
264 | ipc_mni = IPCMNI_EXTEND; | |
265 | ipc_mni_shift = IPCMNI_EXTEND_SHIFT; | |
99db46ea | 266 | ipc_min_cycle = IPCMNI_EXTEND_MIN_CYCLE; |
5ac893b8 WL |
267 | pr_info("IPCMNI extended to %d.\n", ipc_mni); |
268 | return 0; | |
269 | } | |
270 | early_param("ipcmni_extend", ipc_mni_extend); |