Skip to contentSkip to conversation

Fixing /proc/kcore

Linux-ia64 Andi Kleen 3 participants 4 messages Oct 25, 2002 – Oct 28, 2002

lore
Share & tools
lore

Andi Kleen

author
#0 lore raw
Quoted 11 lines · Tony

This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible.

------_=_NextPart_000_01C27C4E.CD496DB0

Content-Type: text/plain;
	charset="utf-7"

/proc/kcore is what you need, but it is broken on ia64 (and
has been since the dawn of time for access to region 5) because it assumes that all kernel virtual addresses are above PAGE+AF8-OFFSET. This isn't true on ia64, VMALLOC+AF8-START is smaller than PAGE+AF8-OFFSET.

I recently fixed a similar problem on x86-64. There the modules are outside the vmalloc area, but also not in the direct mapping. I just changed the module mapping and the kernel mapping to put themselves into the vmlist. kcore checks vmlist first and then afterwards tries direct addresses.

Also the kernel addresses are negative, which needed some more changes in seek.

Here is the 2.4.19 patch which should to 2.5 too.

I think it's a bit cleaner than yours and will probably help you too. Just put everything special into vmlist too.

-Andi

Index: linux/fs/proc/inode.c
=================================RCS file: /home/cvs/Repository/linux/fs/proc/inode.c,v retrieving revision 1.4
retrieving revision 1.5
diff -u -u -r1.4 -r1.5
--- linux/fs/proc/inode.c 2002/01/15 10:09:21 1.4
--- linux/fs/proc/inode.c 2002/10/17 13:02:13 1.5
@@ -186,6 +186,7 @@
s->s_blocksize_bits = 10;
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
+ s->s_maxbytes = ~0ULL;

root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
if (!root_inode)
Index: linux/fs/proc/kcore.c
=================================RCS file: /home/cvs/Repository/linux/fs/proc/kcore.c,v retrieving revision 1.3
retrieving revision 1.4
diff -u -u -r1.3 -r1.4
--- linux/fs/proc/kcore.c 2002/03/21 11:54:59 1.3
--- linux/fs/proc/kcore.c 2002/10/17 13:02:13 1.4
@@ -27,11 +27,14 @@
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}

+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin); +
static ssize_t read_kcore(struct file *, char *, size_t, loff_t *);

struct file_operations proc_kcore_operations = {
read: read_kcore,
open: open_kcore,
+ lseek: lseek_kcore,
};

#ifdef CONFIG_KCORE_AOUT
@@ -112,9 +115,9 @@

extern char saved_command_line[];

-static size_t get_kcore_size(int *num_vma, size_t *elf_buflen) +static unsigned long get_kcore_size(int *num_vma, size_t *elf_buflen)
{
- size_t try, size;
+ unsigned long try, size;
struct vm_struct *m;

*num_vma = 0;
@@ -125,7 +128,7 @@
}

for (m=vmlist; m; m=m->next) {
- try = (size_t)m->addr + m->size;
+ try = (unsigned long)m->addr + m->size;
if (try > size)
size = try;
*num_vma = *num_vma + 1;
@@ -313,14 +316,14 @@
static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t *fpos) {
ssize_t acc = 0;
- size_t size, tsz;
+ unsigned long size, tsz;
size_t elf_buflen;
int num_vma;
unsigned long start;

read_lock(&vmlist_lock);
proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen);

-	if (buflen = 0 || *fpos >= size) {
+	if (buflen = 0 || (unsigned long long)*fpos >= size) {

read_unlock(&vmlist_lock);
return 0;
}
@@ -390,9 +393,16 @@
start = PAGE_OFFSET + (*fpos - elf_buflen);
if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
tsz = buflen;
-
while (buflen) {
- if ((start >= VMALLOC_START) && (start < VMALLOC_END)) {
+ int err;
+
+ if ((start > PAGE_OFFSET) && (start < (unsigned long)high_memory)) {
+ if (kern_addr_valid(start)) {
+ err = copy_to_user(buffer, (char *)start, tsz);
+ } else {
+ err = clear_user(buffer, tsz);
+ }
+ } else {
char * elf_buf;
struct vm_struct *m;
unsigned long curstart = start;
@@ -432,24 +442,11 @@
(char *)vmstart, vmsize);
}
read_unlock(&vmlist_lock);
- if (copy_to_user(buffer, elf_buf, tsz)) {
- kfree(elf_buf);
- return -EFAULT;
- }
+ err = copy_to_user(buffer, elf_buf, tsz);
kfree(elf_buf);
- } else if ((start > PAGE_OFFSET) && (start <
- (unsigned long)high_memory)) {
- if (kern_addr_valid(start)) {
- if (copy_to_user(buffer, (char *)start, tsz))
- return -EFAULT;
- } else {
- if (clear_user(buffer, tsz))
- return -EFAULT;
- }
- } else {
- if (clear_user(buffer, tsz))
- return -EFAULT;
- }
+ }
+ if (err)
+ return -EFAULT;
buflen -= tsz;
*fpos += tsz;
buffer += tsz;
@@ -461,3 +458,19 @@
return acc;
}
#endif /* CONFIG_KCORE_AOUT */
+
+static loff_t lseek_kcore(struct file * file, loff_t offset, int origin) +{
+ long long retval;
+
+ switch (origin) {
+ case 2:
+ offset += file->f_dentry->d_inode->i_size;
+ break;
+ case 1:
+ offset += file->f_pos;
+ }
+ /* RED-PEN user can fake an error here by setting offset to >=-4095 && <0 */
+ file->f_pos = offset;
+ return offset;
+}

#1 lore raw

Tony, Andi thanks!

- mario

Quoted 172 lines · Andi Kleen
Quoted 11 lines · Tony

This message is in MIME format. Since your mail reader does not understand this format, some or all of this message may not be legible.

------_=_NextPart_000_01C27C4E.CD496DB0

Content-Type: text/plain;
      charset="utf-7"

/proc/kcore is what you need, but it is broken on ia64 (and has been since the dawn of time for access to region 5) because it assumes that all kernel virtual addresses are above PAGE_OFFSET. This isn't true on ia64, VMALLOC_START is smaller than PAGE_OFFSET.

I recently fixed a similar problem on x86-64. There the modules are outside the vmalloc area, but also not in the direct mapping. I just changed the module mapping and the kernel mapping to put themselves into the vmlist. kcore checks vmlist first and then afterwards tries direct addresses.

Also the kernel addresses are negative, which needed some more changes in seek.

Here is the 2.4.19 patch which should to 2.5 too.

I think it's a bit cleaner than yours and will probably help you too. Just put everything special into vmlist too.

-Andi

Index: linux/fs/proc/inode.c
=================================> RCS file: /home/cvs/Repository/linux/fs/proc/inode.c,v retrieving revision 1.4
retrieving revision 1.5
diff -u -u -r1.4 -r1.5

--- linux/fs/proc/inode.c       2002/01/15 10:09:21     1.4
--- linux/fs/proc/inode.c       2002/10/17 13:02:13     1.5
@@ -186,6 +184-,7 @@
        s->s_blocksize_bits = 10;
        s->s_magic = PROC_SUPER_MAGIC;
        s->s_op = &proc_sops;
        s->s_maxbytes = +AH4-0ULL;

        root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root);
        if (!root_inode)

Index: linux/fs/proc/kcore.c
=================================> RCS file: /home/cvs/Repository/linux/fs/proc/kcore.c,v retrieving revision 1.3
retrieving revision 1.4
diff -u -u -r1.3 -r1.4

--- linux/fs/proc/kcore.c       2002/03/21 11:54:59     1.3
--- linux/fs/proc/kcore.c       2002/10/17 13:02:13     1.4
@@ -27,11 ,14 @@
        return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;

}

+statiQ- loff_t lseek_kcore(struct file * file, loff_t offset, int origin);

static ssize_t read_kcore(struct file *, char *, size_t, loff_t *);

struct file_operations proc_kcore_operations = {

        read:           read_kcore,
        open:           open_kcore,
        lseek:          lseek_kcore,

};

#ifdef CONFIG_KCORE_AOUT
@@ -112,9 +114-,9 @@

extern char saved_command_line[];

(quoted diff) +1 -2
-static size_t get_kcore_size(int *num_vma, size_t *elf_buflen)
+statiQ- unsigned long get_kcore_size(int *num_vma, size_t *elf_buflen)
 {
-       size_t try, size;
        unsigned long try, size;
        struct vm_struct *m;
 
        *num_vma = 0;

@@ -125,7 +128-,7 @@

        }

        for (m=vmlist; m; m=m->next) {
-               try = (size_t)m->addr  m->size;
                try = (unsigned long)m->addr  m->size;
                if (try > size)
                        size = try;
                *num_vma = *num_vma  1;
@@ -313,14 +314-,14 @@
 static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t *fpos)
 {
        ssize_t acc = 0;
(quoted diff) +0 -2
-       size_t size, tsz;
        unsigned long size, tsz;
        size_t elf_buflen;
        int num_vma;
        unsigned long start;
 
        read_lock(&vmlist_lock);
        proc_root_kcore->size = size = get_kcore_size(&num_vma, &elf_buflen);
-       if (buflen = 0 || *fpos >= size) {
        if (buflen = 0 || (unsigned long long)*fpos >= size) {
                read_unlock(&vmlist_lock);
                return 0;
        }

@@ -390,9 +390-,16 @@

        start = PAGE_OFFSET  (*fpos - elf_buflen);
        if ((tsz = (PAGE_SIZE - (start & +AH4-PAGE_MASK))) > buflen)
                tsz = buflen;
(quoted diff) +0 -2
-
        while (buflen) {
-               if ((start >= VMALLOC_START) && (start < VMALLOC_END)) {
                int err;
 
                if ((start > PAGE_OFFSET) && (start < (unsigned long)high_memory)) {
                        if (kern_addr_valid(start)) {
                                err = copy_to_user(buffer, (char *)start, tsz);
                        } else {
                                err = clear_user(buffer, tsz);
                        }
                } else {
                        char * elf_buf;
                        struct vm_struct *m;
                        unsigned long curstart = start;

@@ -432,24 +440-,11 @@

                                        (char *)vmstart, vmsize);
                        }
                        read_unlock(&vmlist_lock);
(quoted diff) +0 -17
-                       if (copy_to_user(buffer, elf_buf, tsz)) {
-                               kfree(elf_buf);
-                               return -EFAULT;
-                       }
                        err = copy_to_user(buffer, elf_buf, tsz);
                        kfree(elf_buf);
-               } else if ((start > PAGE_OFFSET) && (start <
-                                               (unsigned long)high_memory)) {
-                       if (kern_addr_valid(start)) {
-                               if (copy_to_user(buffer, (char *)start, tsz))
-                                       return -EFAULT;
-                       } else {
-                               if (clear_user(buffer, tsz))
-                                       return -EFAULT;
-                       }
-               } else {
-                       if (clear_user(buffer, tsz))
-                               return -EFAULT;
-               }
                }
                if (err)
                        return -EFAULT;
                buflen -= tsz;
                *fpos = tsz;
                buffer = tsz;

@@ -461,3 +458-,19 @@

        return acc;

}
#endif /* CONFIG_KCORE_AOUT */

+statiQ- loff_t lseek_kcore(struct file * file, loff_t offset, int origin)

{
        long long retval;

        switch (origin) {
                case 2:
                        offset = file->f_dentry->d_inode->i_size;
                        break;
                case 1:
                        offset = file->f_pos;
        }
        /* RED-PEN user can fake an error here by setting offset to >=-4095 && <0  */
        file->f_pos = offset;
        return offset;
}
#2 lore raw
In reply to Andi Kleen
Quoted 7 lines · Andi Kleen
Quoted 2 lines · Tony

Putting everything into the vmlist looks to be a good idea. Perhaps there should be an entry for the "direct addresses" too?

Yes that would make sense.

Quoted 1 line
Quoted 8 lines
Quoted 5 lines

So what does:

	# objdump -p /proc/kcore

look like for you?

Very messy because of the negative addresses and still some sign problems in binutils :-)

Oops, I didn't mean to take this discussion off the mailing list.

What about a combined approach ... architecture dependent code should add all the interesting stuff to the vmlist, so kcore just needs to walk the list to cover everything. We could also keep the KCORE_BASE concept from my patch, but turn it into a variable that the architecture dependent code can set to some suitable offset to keep all the offsets in /proc/kcore positive. This will avoid having to fixup binutils (at least until someone comes up with an architecture where kernel space scatters across a wide enough range that we can't keep the offsets positive).

-Tony

author
#3 lore raw
In reply to Andi Kleen
Quoted 3 lines · Tony

What about a combined approach ... architecture dependent code should add all the interesting stuff to the vmlist, so kcore just needs to walk the list to cover everything. We could also keep the

Works for me. You just have to make sure to keep vmlist ordered and make sure all users of vmlist check for their correct address range (vmalloc.c does, but some arch specific code may not)

Quoted 6 lines

KCORE_BASE concept from my patch, but turn it into a variable that the architecture dependent code can set to some suitable offset to keep all the offsets in /proc/kcore positive. This will avoid having to fixup binutils (at least until someone comes up with an architecture where kernel space scatters across a wide enough range that we can't keep the offsets positive).

x86-64 is such an architecture, so binutils will need to be fixed anyways.

-andi