diff options
-rw-r--r-- | fs/proc/base.c | 81 |
1 files changed, 77 insertions, 4 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 8040f9d1cf07..8fbab527bd24 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; | 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) |
@@ -223,14 +264,46 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, | |||
223 | spin_lock(&mm->arg_lock); | 264 | spin_lock(&mm->arg_lock); |
224 | arg_start = mm->arg_start; | 265 | arg_start = mm->arg_start; |
225 | arg_end = mm->arg_end; | 266 | arg_end = mm->arg_end; |
267 | env_start = mm->env_start; | ||
268 | env_end = mm->env_end; | ||
226 | spin_unlock(&mm->arg_lock); | 269 | spin_unlock(&mm->arg_lock); |
227 | 270 | ||
228 | if (arg_start >= arg_end) | 271 | if (arg_start >= arg_end) |
229 | return 0; | 272 | return 0; |
230 | 273 | ||
274 | /* | ||
275 | * We allow setproctitle() to overwrite the argument | ||
276 | * strings, and overflow past the original end. But | ||
277 | * only when it overflows into the environment area. | ||
278 | */ | ||
279 | if (env_start != arg_end || env_end < env_start) | ||
280 | env_start = env_end = arg_end; | ||
281 | len = env_end - arg_start; | ||
282 | |||
231 | /* 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 */ |
232 | /* .. but we do check the result is in the proper range */ | 284 | pos = *ppos; |
233 | pos = arg_start + *ppos; | 285 | if (pos >= len) |
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); | ||
301 | |||
302 | /* | ||
303 | * For the non-setproctitle() case we limit things strictly | ||
304 | * to the [arg_start, arg_end[ range. | ||
305 | */ | ||
306 | pos += arg_start; | ||
234 | if (pos < arg_start || pos >= arg_end) | 307 | if (pos < arg_start || pos >= arg_end) |
235 | return 0; | 308 | return 0; |
236 | if (count > arg_end - pos) | 309 | if (count > arg_end - pos) |