diff options
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 132 |
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 | */ | ||
216 | static 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 | |||
212 | static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, | 253 | static 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; |