Skip to content

Commit

Permalink
fs/proc/kcore: convert read_kcore() to read_kcore_iter()
Browse files Browse the repository at this point in the history
Now we have eliminated spinlocks from the vread() case, convert
read_kcore() to read_kcore_iter().

For the time being we still use a bounce buffer for vread(), however in
the next patch we will convert this to interact directly with the iterator
and eliminate the bounce buffer altogether.

Link: https://lkml.kernel.org/r/32f8fad50500d0cd0927a66638c5890533725d30.1679209395.git.lstoakes@gmail.com
Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Liu Shixin <liushixin2@huawei.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Uladzislau Rezki (Sony) <urezki@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
  • Loading branch information
Lorenzo Stoakes authored and Andrew Morton committed Mar 19, 2023
1 parent 8f4977b commit 11f1bdc
Showing 1 changed file with 29 additions and 29 deletions.
58 changes: 29 additions & 29 deletions fs/proc/kcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <linux/memblock.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/uio.h>
#include <asm/io.h>
#include <linux/list.h>
#include <linux/ioport.h>
Expand Down Expand Up @@ -308,16 +308,20 @@ static void append_kcore_note(char *notes, size_t *i, const char *name,
}

static ssize_t
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
read_kcore_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
char *buf = file->private_data;
loff_t *ppos = &iocb->ki_pos;

size_t phdrs_offset, notes_offset, data_offset;
size_t page_offline_frozen = 1;
size_t phdrs_len, notes_len;
struct kcore_list *m;
size_t tsz;
int nphdr;
unsigned long start;
size_t buflen = iov_iter_count(iter);
size_t orig_buflen = buflen;
int ret = 0;

Expand All @@ -333,7 +337,7 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
notes_offset = phdrs_offset + phdrs_len;

/* ELF file header. */
if (buflen && *fpos < sizeof(struct elfhdr)) {
if (buflen && *ppos < sizeof(struct elfhdr)) {
struct elfhdr ehdr = {
.e_ident = {
[EI_MAG0] = ELFMAG0,
Expand All @@ -355,19 +359,18 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
.e_phnum = nphdr,
};

tsz = min_t(size_t, buflen, sizeof(struct elfhdr) - *fpos);
if (copy_to_user(buffer, (char *)&ehdr + *fpos, tsz)) {
tsz = min_t(size_t, buflen, sizeof(struct elfhdr) - *ppos);
if (copy_to_iter((char *)&ehdr + *ppos, tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}

buffer += tsz;
buflen -= tsz;
*fpos += tsz;
*ppos += tsz;
}

/* ELF program headers. */
if (buflen && *fpos < phdrs_offset + phdrs_len) {
if (buflen && *ppos < phdrs_offset + phdrs_len) {
struct elf_phdr *phdrs, *phdr;

phdrs = kzalloc(phdrs_len, GFP_KERNEL);
Expand Down Expand Up @@ -397,22 +400,21 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
phdr++;
}

tsz = min_t(size_t, buflen, phdrs_offset + phdrs_len - *fpos);
if (copy_to_user(buffer, (char *)phdrs + *fpos - phdrs_offset,
tsz)) {
tsz = min_t(size_t, buflen, phdrs_offset + phdrs_len - *ppos);
if (copy_to_iter((char *)phdrs + *ppos - phdrs_offset, tsz,
iter) != tsz) {
kfree(phdrs);
ret = -EFAULT;
goto out;
}
kfree(phdrs);

buffer += tsz;
buflen -= tsz;
*fpos += tsz;
*ppos += tsz;
}

/* ELF note segment. */
if (buflen && *fpos < notes_offset + notes_len) {
if (buflen && *ppos < notes_offset + notes_len) {
struct elf_prstatus prstatus = {};
struct elf_prpsinfo prpsinfo = {
.pr_sname = 'R',
Expand Down Expand Up @@ -447,24 +449,23 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
vmcoreinfo_data,
min(vmcoreinfo_size, notes_len - i));

tsz = min_t(size_t, buflen, notes_offset + notes_len - *fpos);
if (copy_to_user(buffer, notes + *fpos - notes_offset, tsz)) {
tsz = min_t(size_t, buflen, notes_offset + notes_len - *ppos);
if (copy_to_iter(notes + *ppos - notes_offset, tsz, iter) != tsz) {
kfree(notes);
ret = -EFAULT;
goto out;
}
kfree(notes);

buffer += tsz;
buflen -= tsz;
*fpos += tsz;
*ppos += tsz;
}

/*
* Check to see if our file offset matches with any of
* the addresses in the elf_phdr on our list.
*/
start = kc_offset_to_vaddr(*fpos - data_offset);
start = kc_offset_to_vaddr(*ppos - data_offset);
if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
tsz = buflen;

Expand Down Expand Up @@ -497,7 +498,7 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
}

if (!m) {
if (clear_user(buffer, tsz)) {
if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
Expand All @@ -508,14 +509,14 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
case KCORE_VMALLOC:
vread(buf, (char *)start, tsz);
/* we have to zero-fill user buffer even if no read */
if (copy_to_user(buffer, buf, tsz)) {
if (copy_to_iter(buf, tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
break;
case KCORE_USER:
/* User page is handled prior to normal kernel page: */
if (copy_to_user(buffer, (char *)start, tsz)) {
if (copy_to_iter((char *)start, tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
Expand All @@ -531,7 +532,7 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
*/
if (!page || PageOffline(page) ||
is_page_hwpoison(page) || !pfn_is_ram(pfn)) {
if (clear_user(buffer, tsz)) {
if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
Expand All @@ -541,25 +542,24 @@ read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
case KCORE_VMEMMAP:
case KCORE_TEXT:
/*
* We use _copy_to_user() to bypass usermode hardening
* We use _copy_to_iter() to bypass usermode hardening
* which would otherwise prevent this operation.
*/
if (_copy_to_user(buffer, (char *)start, tsz)) {
if (_copy_to_iter((char *)start, tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
break;
default:
pr_warn_once("Unhandled KCORE type: %d\n", m->type);
if (clear_user(buffer, tsz)) {
if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT;
goto out;
}
}
skip:
buflen -= tsz;
*fpos += tsz;
buffer += tsz;
*ppos += tsz;
start += tsz;
tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
}
Expand Down Expand Up @@ -603,7 +603,7 @@ static int release_kcore(struct inode *inode, struct file *file)
}

static const struct proc_ops kcore_proc_ops = {
.proc_read = read_kcore,
.proc_read_iter = read_kcore_iter,
.proc_open = open_kcore,
.proc_release = release_kcore,
.proc_lseek = default_llseek,
Expand Down

0 comments on commit 11f1bdc

Please sign in to comment.