aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/base.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-07-13 16:40:13 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2019-07-16 12:57:52 -0400
commit3d712546d8ba9f25cdf080d79f90482aa4231ed4 (patch)
treed391efbe325a7ec74c2766a7cb3e04d555f674c5 /fs/proc/base.c
parent0ecfebd2b52404ae0c54a878c872bb93363ada36 (diff)
/proc/<pid>/cmdline: remove all the special cases
Start off with a clean slate that only reads exactly from arg_start to arg_end, without any oddities. This simplifies the code and in the process removes the case that caused us to potentially leak an uninitialized byte from the temporary kernel buffer. Note that in order to start from scratch with an understandable base, this simplifies things _too_ much, and removes all the legacy logic to handle setproctitle() having changed the argument strings. We'll add back those special cases very differently in the next commit. Link: https://lore.kernel.org/lkml/20190712160913.17727-1-izbyshev@ispras.ru/ Fixes: f5b65348fd77 ("proc: fix missing final NUL in get_mm_cmdline() rewrite") Cc: Alexey Izbyshev <izbyshev@ispras.ru> Cc: Alexey Dobriyan <adobriyan@gmail.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r--fs/proc/base.c71
1 files changed, 8 insertions, 63 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 255f6754c70d..8040f9d1cf07 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -212,7 +212,7 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
212static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, 212static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
213 size_t count, loff_t *ppos) 213 size_t count, loff_t *ppos)
214{ 214{
215 unsigned long arg_start, arg_end, env_start, env_end; 215 unsigned long arg_start, arg_end;
216 unsigned long pos, len; 216 unsigned long pos, len;
217 char *page; 217 char *page;
218 218
@@ -223,36 +223,18 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
223 spin_lock(&mm->arg_lock); 223 spin_lock(&mm->arg_lock);
224 arg_start = mm->arg_start; 224 arg_start = mm->arg_start;
225 arg_end = mm->arg_end; 225 arg_end = mm->arg_end;
226 env_start = mm->env_start;
227 env_end = mm->env_end;
228 spin_unlock(&mm->arg_lock); 226 spin_unlock(&mm->arg_lock);
229 227
230 if (arg_start >= arg_end) 228 if (arg_start >= arg_end)
231 return 0; 229 return 0;
232 230
233 /*
234 * We have traditionally allowed the user to re-write
235 * the argument strings and overflow the end result
236 * into the environment section. But only do that if
237 * the environment area is contiguous to the arguments.
238 */
239 if (env_start != arg_end || env_start >= env_end)
240 env_start = env_end = arg_end;
241
242 /* .. and limit it to a maximum of one page of slop */
243 if (env_end >= arg_end + PAGE_SIZE)
244 env_end = arg_end + PAGE_SIZE - 1;
245
246 /* We're not going to care if "*ppos" has high bits set */ 231 /* We're not going to care if "*ppos" has high bits set */
247 pos = arg_start + *ppos;
248
249 /* .. but we do check the result is in the proper range */ 232 /* .. but we do check the result is in the proper range */
250 if (pos < arg_start || pos >= env_end) 233 pos = arg_start + *ppos;
234 if (pos < arg_start || pos >= arg_end)
251 return 0; 235 return 0;
252 236 if (count > arg_end - pos)
253 /* .. and we never go past env_end */ 237 count = arg_end - pos;
254 if (env_end - pos < count)
255 count = env_end - pos;
256 238
257 page = (char *)__get_free_page(GFP_KERNEL); 239 page = (char *)__get_free_page(GFP_KERNEL);
258 if (!page) 240 if (!page)
@@ -262,48 +244,11 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
262 while (count) { 244 while (count) {
263 int got; 245 int got;
264 size_t size = min_t(size_t, PAGE_SIZE, count); 246 size_t size = min_t(size_t, PAGE_SIZE, count);
265 long offset;
266 247
267 /* 248 got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
268 * Are we already starting past the official end? 249 if (got <= 0)
269 * We always include the last byte that is *supposed*
270 * to be NUL
271 */
272 offset = (pos >= arg_end) ? pos - arg_end + 1 : 0;
273
274 got = access_remote_vm(mm, pos - offset, page, size + offset, FOLL_ANON);
275 if (got <= offset)
276 break; 250 break;
277 got -= offset; 251 got -= copy_to_user(buf, page, got);
278
279 /* Don't walk past a NUL character once you hit arg_end */
280 if (pos + got >= arg_end) {
281 int n = 0;
282
283 /*
284 * If we started before 'arg_end' but ended up
285 * at or after it, we start the NUL character
286 * check at arg_end-1 (where we expect the normal
287 * EOF to be).
288 *
289 * NOTE! This is smaller than 'got', because
290 * pos + got >= arg_end
291 */
292 if (pos < arg_end)
293 n = arg_end - pos - 1;
294
295 /* Cut off at first NUL after 'n' */
296 got = n + strnlen(page+n, offset+got-n);
297 if (got < offset)
298 break;
299 got -= offset;
300
301 /* Include the NUL if it existed */
302 if (got < size)
303 got++;
304 }
305
306 got -= copy_to_user(buf, page+offset, got);
307 if (unlikely(!got)) { 252 if (unlikely(!got)) {
308 if (!len) 253 if (!len)
309 len = -EFAULT; 254 len = -EFAULT;