aboutsummaryrefslogtreecommitdiffstats
path: root/fs/proc/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r--fs/proc/base.c132
1 files changed, 75 insertions, 57 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 77eb628ecc7f..ebea9501afb8 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -209,12 +209,53 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
209 return result; 209 return result;
210} 210}
211 211
212/*
213 * If the user used setproctitle(), we just get the string from
214 * user space at arg_start, and limit it to a maximum of one page.
215 */
216static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf,
217 size_t count, unsigned long pos,
218 unsigned long arg_start)
219{
220 char *page;
221 int ret, got;
222
223 if (pos >= PAGE_SIZE)
224 return 0;
225
226 page = (char *)__get_free_page(GFP_KERNEL);
227 if (!page)
228 return -ENOMEM;
229
230 ret = 0;
231 got = access_remote_vm(mm, arg_start, page, PAGE_SIZE, FOLL_ANON);
232 if (got > 0) {
233 int len = strnlen(page, got);
234
235 /* Include the NUL character if it was found */
236 if (len < got)
237 len++;
238
239 if (len > pos) {
240 len -= pos;
241 if (len > count)
242 len = count;
243 len -= copy_to_user(buf, page+pos, len);
244 if (!len)
245 len = -EFAULT;
246 ret = len;
247 }
248 }
249 free_page((unsigned long)page);
250 return ret;
251}
252
212static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, 253static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
213 size_t count, loff_t *ppos) 254 size_t count, loff_t *ppos)
214{ 255{
215 unsigned long arg_start, arg_end, env_start, env_end; 256 unsigned long arg_start, arg_end, env_start, env_end;
216 unsigned long pos, len; 257 unsigned long pos, len;
217 char *page; 258 char *page, c;
218 259
219 /* Check if process spawned far enough to have cmdline. */ 260 /* Check if process spawned far enough to have cmdline. */
220 if (!mm->env_end) 261 if (!mm->env_end)
@@ -231,28 +272,42 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
231 return 0; 272 return 0;
232 273
233 /* 274 /*
234 * We have traditionally allowed the user to re-write 275 * We allow setproctitle() to overwrite the argument
235 * the argument strings and overflow the end result 276 * strings, and overflow past the original end. But
236 * into the environment section. But only do that if 277 * only when it overflows into the environment area.
237 * the environment area is contiguous to the arguments.
238 */ 278 */
239 if (env_start != arg_end || env_start >= env_end) 279 if (env_start != arg_end || env_end < env_start)
240 env_start = env_end = arg_end; 280 env_start = env_end = arg_end;
241 281 len = env_end - arg_start;
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 282
246 /* We're not going to care if "*ppos" has high bits set */ 283 /* We're not going to care if "*ppos" has high bits set */
247 pos = arg_start + *ppos; 284 pos = *ppos;
248 285 if (pos >= len)
249 /* .. but we do check the result is in the proper range */
250 if (pos < arg_start || pos >= env_end)
251 return 0; 286 return 0;
287 if (count > len - pos)
288 count = len - pos;
289 if (!count)
290 return 0;
291
292 /*
293 * Magical special case: if the argv[] end byte is not
294 * zero, the user has overwritten it with setproctitle(3).
295 *
296 * Possible future enhancement: do this only once when
297 * pos is 0, and set a flag in the 'struct file'.
298 */
299 if (access_remote_vm(mm, arg_end-1, &c, 1, FOLL_ANON) == 1 && c)
300 return get_mm_proctitle(mm, buf, count, pos, arg_start);
252 301
253 /* .. and we never go past env_end */ 302 /*
254 if (env_end - pos < count) 303 * For the non-setproctitle() case we limit things strictly
255 count = env_end - pos; 304 * to the [arg_start, arg_end[ range.
305 */
306 pos += arg_start;
307 if (pos < arg_start || pos >= arg_end)
308 return 0;
309 if (count > arg_end - pos)
310 count = arg_end - pos;
256 311
257 page = (char *)__get_free_page(GFP_KERNEL); 312 page = (char *)__get_free_page(GFP_KERNEL);
258 if (!page) 313 if (!page)
@@ -262,48 +317,11 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
262 while (count) { 317 while (count) {
263 int got; 318 int got;
264 size_t size = min_t(size_t, PAGE_SIZE, count); 319 size_t size = min_t(size_t, PAGE_SIZE, count);
265 long offset;
266 320
267 /* 321 got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
268 * Are we already starting past the official end? 322 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; 323 break;
277 got -= offset; 324 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)) { 325 if (unlikely(!got)) {
308 if (!len) 326 if (!len)
309 len = -EFAULT; 327 len = -EFAULT;