Commit 3673fb08 authored by Omar Sandoval's avatar Omar Sandoval Committed by Linus Torvalds

proc/kcore: hold lock during read

Now that we're using an rwsem, we can hold it during the entirety of
read_kcore() and have a common return path.  This is preparation for the
next change.

[akpm@linux-foundation.org: fix locking bug reported by Tetsuo Handa]
Link: http://lkml.kernel.org/r/d7cfbc1e8a76616f3b699eaff9df0a2730380534.1531953780.git.osandov@fb.comSigned-off-by: default avatarOmar Sandoval <osandov@fb.com>
Cc: Alexey Dobriyan <adobriyan@gmail.com>
Cc: Bhupesh Sharma <bhsharma@redhat.com>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: James Morse <james.morse@arm.com>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent b66fb005
...@@ -440,19 +440,18 @@ static ssize_t ...@@ -440,19 +440,18 @@ static ssize_t
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
{ {
char *buf = file->private_data; char *buf = file->private_data;
ssize_t acc = 0;
size_t size, tsz; size_t size, tsz;
size_t elf_buflen; size_t elf_buflen;
int nphdr; int nphdr;
unsigned long start; unsigned long start;
size_t orig_buflen = buflen;
int ret = 0;
down_read(&kclist_lock); down_read(&kclist_lock);
size = get_kcore_size(&nphdr, &elf_buflen); size = get_kcore_size(&nphdr, &elf_buflen);
if (buflen == 0 || *fpos >= size) { if (buflen == 0 || *fpos >= size)
up_read(&kclist_lock); goto out;
return 0;
}
/* trim buflen to not go beyond EOF */ /* trim buflen to not go beyond EOF */
if (buflen > size - *fpos) if (buflen > size - *fpos)
...@@ -465,28 +464,26 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) ...@@ -465,28 +464,26 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
tsz = elf_buflen - *fpos; tsz = elf_buflen - *fpos;
if (buflen < tsz) if (buflen < tsz)
tsz = buflen; tsz = buflen;
elf_buf = kzalloc(elf_buflen, GFP_ATOMIC); elf_buf = kzalloc(elf_buflen, GFP_KERNEL);
if (!elf_buf) { if (!elf_buf) {
up_read(&kclist_lock); ret = -ENOMEM;
return -ENOMEM; goto out;
} }
elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen); elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen);
up_read(&kclist_lock);
if (copy_to_user(buffer, elf_buf + *fpos, tsz)) { if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
kfree(elf_buf); kfree(elf_buf);
return -EFAULT; ret = -EFAULT;
goto out;
} }
kfree(elf_buf); kfree(elf_buf);
buflen -= tsz; buflen -= tsz;
*fpos += tsz; *fpos += tsz;
buffer += tsz; buffer += tsz;
acc += tsz;
/* leave now if filled buffer already */ /* leave now if filled buffer already */
if (buflen == 0) if (buflen == 0)
return acc; goto out;
} else }
up_read(&kclist_lock);
/* /*
* Check to see if our file offset matches with any of * Check to see if our file offset matches with any of
...@@ -499,25 +496,29 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) ...@@ -499,25 +496,29 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
while (buflen) { while (buflen) {
struct kcore_list *m; struct kcore_list *m;
down_read(&kclist_lock);
list_for_each_entry(m, &kclist_head, list) { list_for_each_entry(m, &kclist_head, list) {
if (start >= m->addr && start < (m->addr+m->size)) if (start >= m->addr && start < (m->addr+m->size))
break; break;
} }
up_read(&kclist_lock);
if (&m->list == &kclist_head) { if (&m->list == &kclist_head) {
if (clear_user(buffer, tsz)) if (clear_user(buffer, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} else if (m->type == KCORE_VMALLOC) { } else if (m->type == KCORE_VMALLOC) {
vread(buf, (char *)start, tsz); vread(buf, (char *)start, tsz);
/* we have to zero-fill user buffer even if no read */ /* we have to zero-fill user buffer even if no read */
if (copy_to_user(buffer, buf, tsz)) if (copy_to_user(buffer, buf, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} else if (m->type == KCORE_USER) { } else if (m->type == KCORE_USER) {
/* User page is handled prior to normal kernel page: */ /* User page is handled prior to normal kernel page: */
if (copy_to_user(buffer, (char *)start, tsz)) if (copy_to_user(buffer, (char *)start, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} else { } else {
if (kern_addr_valid(start)) { if (kern_addr_valid(start)) {
/* /*
...@@ -525,26 +526,35 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) ...@@ -525,26 +526,35 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
* hardened user copy kernel text checks. * hardened user copy kernel text checks.
*/ */
if (probe_kernel_read(buf, (void *) start, tsz)) { if (probe_kernel_read(buf, (void *) start, tsz)) {
if (clear_user(buffer, tsz)) if (clear_user(buffer, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} else { } else {
if (copy_to_user(buffer, buf, tsz)) if (copy_to_user(buffer, buf, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} }
} else { } else {
if (clear_user(buffer, tsz)) if (clear_user(buffer, tsz)) {
return -EFAULT; ret = -EFAULT;
goto out;
}
} }
} }
buflen -= tsz; buflen -= tsz;
*fpos += tsz; *fpos += tsz;
buffer += tsz; buffer += tsz;
acc += tsz;
start += tsz; start += tsz;
tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen); tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
} }
return acc; out:
up_read(&kclist_lock);
if (ret)
return ret;
return orig_buflen - buflen;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment