diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/proc/base.c | 205 |
1 files changed, 196 insertions, 9 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 286a422f440e..bd7a9affc14b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -196,18 +196,205 @@ static int proc_root_link(struct dentry *dentry, struct path *path) | |||
196 | return result; | 196 | return result; |
197 | } | 197 | } |
198 | 198 | ||
199 | static int proc_pid_cmdline(struct seq_file *m, struct pid_namespace *ns, | 199 | static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf, |
200 | struct pid *pid, struct task_struct *task) | 200 | size_t _count, loff_t *pos) |
201 | { | 201 | { |
202 | struct task_struct *tsk; | ||
203 | struct mm_struct *mm; | ||
204 | char *page; | ||
205 | unsigned long count = _count; | ||
206 | unsigned long arg_start, arg_end, env_start, env_end; | ||
207 | unsigned long len1, len2, len; | ||
208 | unsigned long p; | ||
209 | char c; | ||
210 | ssize_t rv; | ||
211 | |||
212 | BUG_ON(*pos < 0); | ||
213 | |||
214 | tsk = get_proc_task(file_inode(file)); | ||
215 | if (!tsk) | ||
216 | return -ESRCH; | ||
217 | mm = get_task_mm(tsk); | ||
218 | put_task_struct(tsk); | ||
219 | if (!mm) | ||
220 | return 0; | ||
221 | /* Check if process spawned far enough to have cmdline. */ | ||
222 | if (!mm->env_end) { | ||
223 | rv = 0; | ||
224 | goto out_mmput; | ||
225 | } | ||
226 | |||
227 | page = (char *)__get_free_page(GFP_TEMPORARY); | ||
228 | if (!page) { | ||
229 | rv = -ENOMEM; | ||
230 | goto out_mmput; | ||
231 | } | ||
232 | |||
233 | down_read(&mm->mmap_sem); | ||
234 | arg_start = mm->arg_start; | ||
235 | arg_end = mm->arg_end; | ||
236 | env_start = mm->env_start; | ||
237 | env_end = mm->env_end; | ||
238 | up_read(&mm->mmap_sem); | ||
239 | |||
240 | BUG_ON(arg_start > arg_end); | ||
241 | BUG_ON(env_start > env_end); | ||
242 | |||
243 | len1 = arg_end - arg_start; | ||
244 | len2 = env_end - env_start; | ||
245 | |||
202 | /* | 246 | /* |
203 | * Rely on struct seq_operations::show() being called once | 247 | * Inherently racy -- command line shares address space |
204 | * per internal buffer allocation. See single_open(), traverse(). | 248 | * with code and data. |
205 | */ | 249 | */ |
206 | BUG_ON(m->size < PAGE_SIZE); | 250 | rv = access_remote_vm(mm, arg_end - 1, &c, 1, 0); |
207 | m->count += get_cmdline(task, m->buf, PAGE_SIZE); | 251 | if (rv <= 0) |
208 | return 0; | 252 | goto out_free_page; |
253 | |||
254 | rv = 0; | ||
255 | |||
256 | if (c == '\0') { | ||
257 | /* Command line (set of strings) occupies whole ARGV. */ | ||
258 | if (len1 <= *pos) | ||
259 | goto out_free_page; | ||
260 | |||
261 | p = arg_start + *pos; | ||
262 | len = len1 - *pos; | ||
263 | while (count > 0 && len > 0) { | ||
264 | unsigned int _count; | ||
265 | int nr_read; | ||
266 | |||
267 | _count = min3(count, len, PAGE_SIZE); | ||
268 | nr_read = access_remote_vm(mm, p, page, _count, 0); | ||
269 | if (nr_read < 0) | ||
270 | rv = nr_read; | ||
271 | if (nr_read <= 0) | ||
272 | goto out_free_page; | ||
273 | |||
274 | if (copy_to_user(buf, page, nr_read)) { | ||
275 | rv = -EFAULT; | ||
276 | goto out_free_page; | ||
277 | } | ||
278 | |||
279 | p += nr_read; | ||
280 | len -= nr_read; | ||
281 | buf += nr_read; | ||
282 | count -= nr_read; | ||
283 | rv += nr_read; | ||
284 | } | ||
285 | } else { | ||
286 | /* | ||
287 | * Command line (1 string) occupies ARGV and maybe | ||
288 | * extends into ENVP. | ||
289 | */ | ||
290 | if (len1 + len2 <= *pos) | ||
291 | goto skip_argv_envp; | ||
292 | if (len1 <= *pos) | ||
293 | goto skip_argv; | ||
294 | |||
295 | p = arg_start + *pos; | ||
296 | len = len1 - *pos; | ||
297 | while (count > 0 && len > 0) { | ||
298 | unsigned int _count, l; | ||
299 | int nr_read; | ||
300 | bool final; | ||
301 | |||
302 | _count = min3(count, len, PAGE_SIZE); | ||
303 | nr_read = access_remote_vm(mm, p, page, _count, 0); | ||
304 | if (nr_read < 0) | ||
305 | rv = nr_read; | ||
306 | if (nr_read <= 0) | ||
307 | goto out_free_page; | ||
308 | |||
309 | /* | ||
310 | * Command line can be shorter than whole ARGV | ||
311 | * even if last "marker" byte says it is not. | ||
312 | */ | ||
313 | final = false; | ||
314 | l = strnlen(page, nr_read); | ||
315 | if (l < nr_read) { | ||
316 | nr_read = l; | ||
317 | final = true; | ||
318 | } | ||
319 | |||
320 | if (copy_to_user(buf, page, nr_read)) { | ||
321 | rv = -EFAULT; | ||
322 | goto out_free_page; | ||
323 | } | ||
324 | |||
325 | p += nr_read; | ||
326 | len -= nr_read; | ||
327 | buf += nr_read; | ||
328 | count -= nr_read; | ||
329 | rv += nr_read; | ||
330 | |||
331 | if (final) | ||
332 | goto out_free_page; | ||
333 | } | ||
334 | skip_argv: | ||
335 | /* | ||
336 | * Command line (1 string) occupies ARGV and | ||
337 | * extends into ENVP. | ||
338 | */ | ||
339 | if (len1 <= *pos) { | ||
340 | p = env_start + *pos - len1; | ||
341 | len = len1 + len2 - *pos; | ||
342 | } else { | ||
343 | p = env_start; | ||
344 | len = len2; | ||
345 | } | ||
346 | while (count > 0 && len > 0) { | ||
347 | unsigned int _count, l; | ||
348 | int nr_read; | ||
349 | bool final; | ||
350 | |||
351 | _count = min3(count, len, PAGE_SIZE); | ||
352 | nr_read = access_remote_vm(mm, p, page, _count, 0); | ||
353 | if (nr_read < 0) | ||
354 | rv = nr_read; | ||
355 | if (nr_read <= 0) | ||
356 | goto out_free_page; | ||
357 | |||
358 | /* Find EOS. */ | ||
359 | final = false; | ||
360 | l = strnlen(page, nr_read); | ||
361 | if (l < nr_read) { | ||
362 | nr_read = l; | ||
363 | final = true; | ||
364 | } | ||
365 | |||
366 | if (copy_to_user(buf, page, nr_read)) { | ||
367 | rv = -EFAULT; | ||
368 | goto out_free_page; | ||
369 | } | ||
370 | |||
371 | p += nr_read; | ||
372 | len -= nr_read; | ||
373 | buf += nr_read; | ||
374 | count -= nr_read; | ||
375 | rv += nr_read; | ||
376 | |||
377 | if (final) | ||
378 | goto out_free_page; | ||
379 | } | ||
380 | skip_argv_envp: | ||
381 | ; | ||
382 | } | ||
383 | |||
384 | out_free_page: | ||
385 | free_page((unsigned long)page); | ||
386 | out_mmput: | ||
387 | mmput(mm); | ||
388 | if (rv > 0) | ||
389 | *pos += rv; | ||
390 | return rv; | ||
209 | } | 391 | } |
210 | 392 | ||
393 | static const struct file_operations proc_pid_cmdline_ops = { | ||
394 | .read = proc_pid_cmdline_read, | ||
395 | .llseek = generic_file_llseek, | ||
396 | }; | ||
397 | |||
211 | static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, | 398 | static int proc_pid_auxv(struct seq_file *m, struct pid_namespace *ns, |
212 | struct pid *pid, struct task_struct *task) | 399 | struct pid *pid, struct task_struct *task) |
213 | { | 400 | { |
@@ -2572,7 +2759,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
2572 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | 2759 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK |
2573 | ONE("syscall", S_IRUSR, proc_pid_syscall), | 2760 | ONE("syscall", S_IRUSR, proc_pid_syscall), |
2574 | #endif | 2761 | #endif |
2575 | ONE("cmdline", S_IRUGO, proc_pid_cmdline), | 2762 | REG("cmdline", S_IRUGO, proc_pid_cmdline_ops), |
2576 | ONE("stat", S_IRUGO, proc_tgid_stat), | 2763 | ONE("stat", S_IRUGO, proc_tgid_stat), |
2577 | ONE("statm", S_IRUGO, proc_pid_statm), | 2764 | ONE("statm", S_IRUGO, proc_pid_statm), |
2578 | REG("maps", S_IRUGO, proc_pid_maps_operations), | 2765 | REG("maps", S_IRUGO, proc_pid_maps_operations), |
@@ -2918,7 +3105,7 @@ static const struct pid_entry tid_base_stuff[] = { | |||
2918 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK | 3105 | #ifdef CONFIG_HAVE_ARCH_TRACEHOOK |
2919 | ONE("syscall", S_IRUSR, proc_pid_syscall), | 3106 | ONE("syscall", S_IRUSR, proc_pid_syscall), |
2920 | #endif | 3107 | #endif |
2921 | ONE("cmdline", S_IRUGO, proc_pid_cmdline), | 3108 | REG("cmdline", S_IRUGO, proc_pid_cmdline_ops), |
2922 | ONE("stat", S_IRUGO, proc_tid_stat), | 3109 | ONE("stat", S_IRUGO, proc_tid_stat), |
2923 | ONE("statm", S_IRUGO, proc_pid_statm), | 3110 | ONE("statm", S_IRUGO, proc_pid_statm), |
2924 | REG("maps", S_IRUGO, proc_tid_maps_operations), | 3111 | REG("maps", S_IRUGO, proc_tid_maps_operations), |