From:Suzuki K. Poulose <suz...@in.ibm.com>

Generate the "data" for the memory regions. Also write down the section header
if we have, number of phdrs > PN_XNUM.

The vma areas are read, page by page using access_process_vm() without an
mmap_sem. If there are active threads, then we may miss a vma if it is removed
while we are doing the read.

Signed-off-by: Suzuki K. Poulose <suz...@in.ibm.com>
Signed-off-by: Ananth N. Mavinakayanahalli <ana...@in.ibm.com>
---
 fs/proc/gencore-elf.c |   90 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 88 insertions(+), 2 deletions(-)

diff --git a/fs/proc/gencore-elf.c b/fs/proc/gencore-elf.c
index efd1722..244e5bb 100644
--- a/fs/proc/gencore-elf.c
+++ b/fs/proc/gencore-elf.c
@@ -314,10 +314,29 @@ static int create_elf_header(struct core_proc *cp)
        return 0;
 }
 
+/*
+ * Verify if the fpos asked for in read is valid.
+ * Returns the phdr corresponding to offset, else NULL.
+ */
+static struct elf_phdr *get_pos_elfphdr(struct core_proc *cp, loff_t pos)
+{
+       struct elfhdr *elf_hdr = (struct elfhdr *)cp->elf_buf;
+       struct elf_phdr *phdr = (struct elf_phdr*)(cp->elf_buf + 
elf_hdr->e_phoff);
+       int i;
+
+       for (i = 0; i < cp->nphdrs; i++, phdr++) {
+               unsigned long end = phdr->p_offset + phdr->p_filesz;
+               if ((pos >= phdr->p_offset) && (pos < end) && phdr->p_filesz)
+                       return phdr;
+       }
+       return NULL;
+}
+
 ssize_t elf_read_gencore(struct core_proc *cp, char __user *buffer,
                                size_t buflen, loff_t *fpos)
 {
-       ssize_t ret = 0;
+       ssize_t ret = 0, acc = 0;
+       struct elfhdr *elf_hdr = (struct elfhdr *)cp->elf_buf;
 
        if (!cp->notes_size) {
                if (!collect_notes(cp)) {
@@ -349,13 +368,80 @@ ssize_t elf_read_gencore(struct core_proc *cp, char 
__user *buffer,
                        goto out;
                } else {
                        ret = bcp;
+                       acc = bcp;
                        *fpos += bcp;
                        buflen -= bcp;
                        buffer += bcp;
                }
        }
+
        if (*fpos > cp->size)
-               goto out;
+               goto done;
+
+       /*
+        * Read from the vma segments
+        * a. verify if the *fpos is within a phdr
+        * b. Use access_process_vm() to get data page by page
+        * c. copy_to_user into user buffer
+        */
+
+       while (buflen) {
+               size_t bufsz, offset, bytes;
+               char *readbuf;
+               struct elf_phdr *phdr = get_pos_elfphdr(cp, *fpos);
+
+               if (!phdr)
+                       break;
+
+               bufsz = (buflen > PAGE_SIZE) ? PAGE_SIZE : buflen;
+               readbuf = kmalloc(bufsz, GFP_KERNEL);
+               if (!readbuf) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               offset = *fpos - phdr->p_offset;
+               bytes = access_process_vm(cp->task, (phdr->p_vaddr + offset),
+                                                       readbuf, bufsz, 0);     
                
+               if (!bytes) {
+                       ret = -EIO;
+                       goto out;
+               }
+               if (copy_to_user(buffer, readbuf, bytes)) {
+                       ret = -EFAULT;
+                       kfree(readbuf);
+                       goto out;
+               } else
+                       acc += bytes;
+
+               kfree(readbuf);
+               buflen -= bytes;
+               buffer += bytes;
+               *fpos += bytes;
+       }
+
+       /* Fill extnum section header if present */
+       if (buflen &&
+               elf_hdr->e_shoff &&
+               (*fpos >= elf_hdr->e_shoff) &&
+               (*fpos < (elf_hdr->e_shoff + sizeof(struct elf_shdr)))) {
+
+               off_t offset = *fpos - elf_hdr->e_shoff;
+               size_t shdrsz = sizeof(struct elf_shdr) - offset;
+
+               shdrsz = (buflen < shdrsz) ? buflen : shdrsz;
+               if (copy_to_user(buffer, ((char *)cp->shdr) + offset, shdrsz)) {
+                       ret = -EFAULT;
+                       goto out;
+               } else {
+                       acc += shdrsz;
+                       buflen -= shdrsz;
+                       buffer += shdrsz;
+               }
+       }
+ 
+done:
+       ret = acc;
   
 out:
        return ret;

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to