From 8c71ce4cf416e03c6093e3399a3738737260b381 Mon Sep 17 00:00:00 2001 From: Fedor Date: Fri, 20 Mar 2009 00:26:36 -0400 Subject: [PATCH] Procfx bug fixes ported from later 2.4.x kernel tree. --- release/src/linux/linux/drivers/pci/proc.c | 10 ++-- release/src/linux/linux/fs/proc/array.c | 6 +++ release/src/linux/linux/fs/proc/base.c | 29 +++++++---- release/src/linux/linux/fs/proc/generic.c | 37 ++++++++++---- release/src/linux/linux/fs/proc/inode.c | 5 +- release/src/linux/linux/fs/proc/kcore.c | 76 +++++++++++++++++------------ release/src/linux/linux/fs/proc/proc_misc.c | 30 +++++++++++- 7 files changed, 137 insertions(+), 56 deletions(-) diff --git a/release/src/linux/linux/drivers/pci/proc.c b/release/src/linux/linux/drivers/pci/proc.c index e814f151c7..5a160dbbfb 100644 --- a/release/src/linux/linux/drivers/pci/proc.c +++ b/release/src/linux/linux/drivers/pci/proc.c @@ -47,7 +47,8 @@ proc_bus_pci_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) const struct inode *ino = file->f_dentry->d_inode; const struct proc_dir_entry *dp = ino->u.generic_ip; struct pci_dev *dev = dp->data; - unsigned int pos = *ppos; + loff_t n = *ppos; + unsigned pos = n; unsigned int cnt, size; /* @@ -63,7 +64,7 @@ proc_bus_pci_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) else size = 64; - if (pos >= size) + if (pos != n || pos >= size) return 0; if (nbytes >= size) nbytes = size; @@ -129,10 +130,11 @@ proc_bus_pci_write(struct file *file, const char *buf, size_t nbytes, loff_t *pp const struct inode *ino = file->f_dentry->d_inode; const struct proc_dir_entry *dp = ino->u.generic_ip; struct pci_dev *dev = dp->data; - int pos = *ppos; + loff_t n = *ppos; + unsigned pos = n; int cnt; - if (pos >= PCI_CFG_SPACE_SIZE) + if (pos != n || pos >= PCI_CFG_SPACE_SIZE) return 0; if (nbytes >= PCI_CFG_SPACE_SIZE) nbytes = PCI_CFG_SPACE_SIZE; diff --git a/release/src/linux/linux/fs/proc/array.c b/release/src/linux/linux/fs/proc/array.c index 0c09078b8c..c69d62a166 100644 --- a/release/src/linux/linux/fs/proc/array.c +++ b/release/src/linux/linux/fs/proc/array.c @@ -568,6 +568,8 @@ static int proc_pid_maps_get_line (char *buf, struct vm_area_struct *map) line = d_path(map->vm_file->f_dentry, map->vm_file->f_vfsmnt, buf, PAGE_SIZE); + if (IS_ERR(line)) + return PTR_ERR(line); buf[PAGE_SIZE-1] = '\n'; line -= MAPS_LINE_MAX; if(line < buf) @@ -641,6 +643,8 @@ ssize_t proc_pid_read_maps (struct task_struct *task, struct file * file, char * goto next; } len = proc_pid_maps_get_line(tmp, map); + if (len < 0) + goto out_unlock; len -= off; if (len > 0) { if (retval+len > count) { @@ -662,6 +666,8 @@ next: if (loff) BUG(); map = map->vm_next; } + +out_unlock: up_read(&mm->mmap_sem); mmput(mm); diff --git a/release/src/linux/linux/fs/proc/base.c b/release/src/linux/linux/fs/proc/base.c index 10d9a1adf3..fc4f1c3ecd 100644 --- a/release/src/linux/linux/fs/proc/base.c +++ b/release/src/linux/linux/fs/proc/base.c @@ -298,6 +298,7 @@ static ssize_t proc_info_read(struct file * file, char * buf, ssize_t length; ssize_t end; struct task_struct *task = inode->u.proc_i.task; + loff_t pos = *ppos; if (count > PROC_BLOCK_SIZE) count = PROC_BLOCK_SIZE; @@ -311,14 +312,14 @@ static ssize_t proc_info_read(struct file * file, char * buf, return length; } /* Static 4kB (or whatever) block capacity */ - if (*ppos >= length) { + if (pos < 0 || pos >= length) { free_page(page); return 0; } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - copy_to_user(buf, (char *) page + *ppos, count); + if (count > length - pos) + count = length - pos; + end = count + pos; + copy_to_user(buf, (char *) page + pos, count); *ppos = end; free_page(page); return count; @@ -360,12 +361,15 @@ static ssize_t mem_read(struct file * file, char * buf, if (mm) atomic_inc(&mm->mm_users); task_unlock(task); - if (!mm) - return 0; + if (!mm){ + copied = 0; + goto out_free; + } if (file->private_data != (void*)((long)current->self_exec_id) ) { mmput(mm); - return -EIO; + copied = -EIO; + goto out_free; } @@ -390,6 +394,8 @@ static ssize_t mem_read(struct file * file, char * buf, } *ppos = src; mmput(mm); + +out_free: free_page((unsigned long) page); return copied; } @@ -480,6 +486,10 @@ static int do_proc_readlink(struct dentry *dentry, struct vfsmount *mnt, inode = dentry->d_inode; path = d_path(dentry, mnt, tmp, PAGE_SIZE); + if (IS_ERR(path)) { + free_page((unsigned long)tmp); + return PTR_ERR(path); + } len = tmp + PAGE_SIZE - 1 - path; if (len < buflen) @@ -720,6 +730,7 @@ out: return inode; out_unlock: + inode->u.generic_ip = NULL; iput(inode); return NULL; } @@ -824,8 +835,8 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) return NULL; out_unlock2: - put_files_struct(files); read_unlock(&files->file_lock); + put_files_struct(files); out_unlock: iput(inode); out: diff --git a/release/src/linux/linux/fs/proc/generic.c b/release/src/linux/linux/fs/proc/generic.c index 0e29800152..c6aad05fe1 100644 --- a/release/src/linux/linux/fs/proc/generic.c +++ b/release/src/linux/linux/fs/proc/generic.c @@ -56,6 +56,7 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) ssize_t n, count; char *start; struct proc_dir_entry * dp; + loff_t pos = *ppos; dp = (struct proc_dir_entry *) inode->u.generic_ip; if (!(page = (char*) __get_free_page(GFP_KERNEL))) @@ -64,6 +65,8 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) while ((nbytes > 0) && !eof) { count = MIN(PROC_BLOCK_SIZE, nbytes); + if ((unsigned)pos > INT_MAX) + break; start = NULL; if (dp->get_info) { @@ -71,11 +74,11 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) * Handle backwards compatibility with the old net * routines. */ - n = dp->get_info(page, &start, *ppos, count); + n = dp->get_info(page, &start, pos, count); if (n < count) eof = 1; } else if (dp->read_proc) { - n = dp->read_proc(page, &start, *ppos, + n = dp->read_proc(page, &start, pos, count, &eof, dp->data); } else break; @@ -84,8 +87,8 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) /* * For proc files that are less than 4k */ - start = page + *ppos; - n -= *ppos; + start = page + pos; + n -= pos; if (n <= 0) break; if (n > count) @@ -111,12 +114,13 @@ proc_file_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) break; } - *ppos += start < page ? (long)start : n; /* Move down the file */ + pos += start < page ? (long)start : n; /* Move down the file */ nbytes -= n; buf += n; retval += n; } free_page((unsigned long) page); + *ppos = pos; return retval; } @@ -149,7 +153,7 @@ proc_file_lseek(struct file * file, loff_t offset, int origin) offset += file->f_pos; } retval = -EINVAL; - if (offset>=0 && offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) { + if (offset>=0 && (unsigned long long)offset<=file->f_dentry->d_inode->i_sb->s_maxbytes) { if (offset != file->f_pos) { file->f_pos = offset; file->f_reada = 0; @@ -455,7 +459,11 @@ struct proc_dir_entry *proc_symlink(const char *name, ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL); if (ent->data) { strcpy((char*)ent->data,dest); - proc_register(parent, ent); + if (proc_register(parent, ent) < 0) { + kfree(ent->data); + kfree(ent); + ent = NULL; + } } else { kfree(ent); ent = NULL; @@ -472,7 +480,10 @@ struct proc_dir_entry *proc_mknod(const char *name, mode_t mode, ent = proc_create(&parent,name,mode,1); if (ent) { ent->rdev = rdev; - proc_register(parent, ent); + if (proc_register(parent, ent) < 0) { + kfree(ent); + ent = NULL; + } } return ent; } @@ -487,7 +498,10 @@ struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *paren ent->proc_fops = &proc_dir_operations; ent->proc_iops = &proc_dir_inode_operations; - proc_register(parent, ent); + if (proc_register(parent, ent) < 0) { + kfree(ent); + ent = NULL; + } } return ent; } @@ -516,7 +530,10 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, ent->proc_fops = &proc_dir_operations; ent->proc_iops = &proc_dir_inode_operations; } - proc_register(parent, ent); + if (proc_register(parent, ent) < 0) { + kfree(ent); + ent = NULL; + } } return ent; } diff --git a/release/src/linux/linux/fs/proc/inode.c b/release/src/linux/linux/fs/proc/inode.c index b0f3e9807b..83efeb57dd 100644 --- a/release/src/linux/linux/fs/proc/inode.c +++ b/release/src/linux/linux/fs/proc/inode.c @@ -22,7 +22,7 @@ extern void free_proc_entry(struct proc_dir_entry *); -struct proc_dir_entry * de_get(struct proc_dir_entry *de) +static inline struct proc_dir_entry * de_get(struct proc_dir_entry *de) { if (de) atomic_inc(&de->count); @@ -32,7 +32,7 @@ struct proc_dir_entry * de_get(struct proc_dir_entry *de) /* * Decrements the use count and checks for deferred deletion. */ -void de_put(struct proc_dir_entry *de) +static void de_put(struct proc_dir_entry *de) { if (de) { lock_kernel(); @@ -184,6 +184,7 @@ struct super_block *proc_read_super(struct super_block *s,void *data, s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; + s->s_maxbytes = ~0UL; root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); if (!root_inode) diff --git a/release/src/linux/linux/fs/proc/kcore.c b/release/src/linux/linux/fs/proc/kcore.c index 23a8241371..cfd5102c69 100644 --- a/release/src/linux/linux/fs/proc/kcore.c +++ b/release/src/linux/linux/fs/proc/kcore.c @@ -27,11 +27,14 @@ static int open_kcore(struct inode * inode, struct file * filp) 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, + llseek: lseek_kcore, }; #ifdef CONFIG_KCORE_AOUT @@ -42,7 +45,7 @@ static ssize_t read_kcore(struct file *file, char *buf, size_t count, loff_t *pp ssize_t count1; char * pnt; struct user dump; -#if defined(__i386__) || defined(__mc68000__) || defined(__x86_64__) +#if defined (__i386__) || defined (__mc68000__) || defined(__x86_64__) # define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */ #else # define FIRST_MAPPED 0 @@ -51,7 +54,7 @@ static ssize_t read_kcore(struct file *file, char *buf, size_t count, loff_t *pp memset(&dump, 0, sizeof(struct user)); dump.magic = CMAGIC; dump.u_dsize = (virt_to_phys(high_memory) >> PAGE_SHIFT); -#if defined(__i386__) || defined(__x86_64__) +#if defined (__i386__) || defined(__x86_64__) dump.start_code = PAGE_OFFSET; #endif #ifdef __alpha__ @@ -93,8 +96,9 @@ static ssize_t read_kcore(struct file *file, char *buf, size_t count, loff_t *pp if (copy_to_user(buf, (void *) (PAGE_OFFSET+p-PAGE_SIZE), count)) return -EFAULT; read += count; + p += count; } - *ppos += read; + *ppos = p; return read; } #else /* CONFIG_KCORE_AOUT */ @@ -112,9 +116,9 @@ struct memelfnote 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,14 +129,17 @@ static size_t get_kcore_size(int *num_vma, size_t *elf_buflen) } 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; } *elf_buflen = sizeof(struct elfhdr) + (*num_vma + 2)*sizeof(struct elf_phdr) + - 3 * sizeof(struct memelfnote); + 3 * (sizeof(struct elf_note) + 4) + + sizeof(struct elf_prstatus) + + sizeof(struct elf_prpsinfo) + + sizeof(struct task_struct); *elf_buflen = PAGE_ALIGN(*elf_buflen); return (size - PAGE_OFFSET + *elf_buflen); } @@ -170,6 +177,7 @@ static char *storenote(struct memelfnote *men, char *bufp) DUMP_WRITE(&en, sizeof(en)); DUMP_WRITE(men->name, en.n_namesz); + /* XXX - cast from long long to long to avoid need for libgcc.a */ bufp = (char*) roundup((unsigned long)bufp,4); DUMP_WRITE(men->data, men->datasz); bufp = (char*) roundup((unsigned long)bufp,4); @@ -274,7 +282,7 @@ static void elf_kcore_store_hdr(char *bufp, int num_vma, int dataoff) memset(&prstatus, 0, sizeof(struct elf_prstatus)); - nhdr->p_filesz = notesize(¬es[0]); + nhdr->p_filesz += notesize(¬es[0]); bufp = storenote(¬es[0], bufp); /* set up the process info */ @@ -291,7 +299,7 @@ static void elf_kcore_store_hdr(char *bufp, int num_vma, int dataoff) strcpy(prpsinfo.pr_fname, "vmlinux"); strncpy(prpsinfo.pr_psargs, saved_command_line, ELF_PRARGSZ); - nhdr->p_filesz = notesize(¬es[1]); + nhdr->p_filesz += notesize(¬es[1]); bufp = storenote(¬es[1], bufp); /* set up the task structure */ @@ -300,7 +308,7 @@ static void elf_kcore_store_hdr(char *bufp, int num_vma, int dataoff) notes[2].datasz = sizeof(struct task_struct); notes[2].data = current; - nhdr->p_filesz = notesize(¬es[2]); + nhdr->p_filesz += notesize(¬es[2]); bufp = storenote(¬es[2], bufp); } /* end elf_kcore_store_hdr() */ @@ -312,14 +320,14 @@ static void elf_kcore_store_hdr(char *bufp, int num_vma, int dataoff) 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; } @@ -360,7 +368,7 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t read_unlock(&vmlist_lock); /* where page 0 not mapped, write zeros into buffer */ -#if defined(__i386__) || defined(__mc68000__) || defined(__x86_64__) +#if defined (__i386__) || defined (__mc68000__) || defined(__x86_64__) if (*fpos < PAGE_SIZE + elf_buflen) { /* work out how much to clear */ tsz = PAGE_SIZE + elf_buflen - *fpos; @@ -389,9 +397,16 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t 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; @@ -431,24 +446,11 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t (char *)vmstart, vmsize); } read_unlock(&vmlist_lock); - if (copy_to_user(buffer, elf_buf, tsz)) { + err = copy_to_user(buffer, elf_buf, tsz); kfree(elf_buf); - return -EFAULT; } - 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)) + if (err) return -EFAULT; - } else { - if (clear_user(buffer, tsz)) - return -EFAULT; - } - } else { - if (clear_user(buffer, tsz)) - return -EFAULT; - } buflen -= tsz; *fpos += tsz; buffer += tsz; @@ -460,3 +462,17 @@ static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, loff_t return acc; } #endif /* CONFIG_KCORE_AOUT */ + +static loff_t lseek_kcore(struct file * file, loff_t offset, int origin) +{ + 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; +} diff --git a/release/src/linux/linux/fs/proc/proc_misc.c b/release/src/linux/linux/fs/proc/proc_misc.c index 7fabe1ea6a..9dabee70de 100644 --- a/release/src/linux/linux/fs/proc/proc_misc.c +++ b/release/src/linux/linux/fs/proc/proc_misc.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -344,7 +345,7 @@ static int kstat_read_proc(char *page, char **start, off_t off, kstat.pswpout, sum ); -#if !defined(CONFIG_ARCH_S390) +#if !defined(CONFIG_ARCH_S390) && !defined(CONFIG_ALPHA) for (i = 0 ; i < NR_IRQS ; i++) proc_sprintf(page, &off, &len, " %u", kstat_irqs(i)); @@ -469,6 +470,28 @@ static int memory_read_proc(char *page, char **start, off_t off, return proc_calc_metrics(page, start, off, count, eof, len); } +#ifdef CONFIG_MAGIC_SYSRQ +/* + * writing 'C' to /proc/sysrq-trigger is like sysrq-C + */ +static ssize_t write_sysrq_trigger(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + if (count) { + char c; + + if (get_user(c, buf)) + return -EFAULT; + handle_sysrq(c, NULL, NULL, NULL); + } + return count; +} + +static struct file_operations proc_sysrq_trigger_operations = { + .write = write_sysrq_trigger, +}; +#endif + struct proc_dir_entry *proc_root_kcore; static void create_seq_entry(char *name, mode_t mode, struct file_operations *f) @@ -538,6 +561,11 @@ void __init proc_misc_init(void) proc_root_kcore->size = (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE; } +#ifdef CONFIG_MAGIC_SYSRQ + entry = create_proc_entry("sysrq-trigger", S_IWUSR, NULL); + if (entry) + entry->proc_fops = &proc_sysrq_trigger_operations; +#endif #ifdef CONFIG_PPC32 { extern struct file_operations ppc_htab_operations; -- 2.11.4.GIT