diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-27 01:05:20 +0000 |
commit | 546db14ee74118296f425f3b91634fb767d67290 (patch) | |
tree | 22b613a3da8d4bf663eec5e155af01b87fdf9094 /fs/proc | |
parent | 1e25e41c4f5474e14452094492dbc169b800e4c8 (diff) |
Merge with Linux 2.3.23. The new bootmem stuff has broken various
platforms. At this time I've only verified that IP22 support compiles
and IP27 actually works.
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/array.c | 316 | ||||
-rw-r--r-- | fs/proc/mem.c | 40 |
2 files changed, 157 insertions, 199 deletions
diff --git a/fs/proc/array.c b/fs/proc/array.c index d7f8ad9dd..249abd8cd 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -38,6 +38,7 @@ * * aeb@cwi.nl : /proc/partitions * + * * Alan Cox : security fixes. * <Alan.Cox@linux.org> * @@ -45,11 +46,6 @@ * * Gerhard Wichert : added BIGMEM support * Siemens AG <Gerhard.Wichert@pdb.siemens.de> - * - * Chuck Lever : safe handling of task_struct - * <cel@monkey.org> - * - * Andrea Arcangeli : SMP race/security fixes. */ #include <linux/types.h> @@ -71,7 +67,6 @@ #include <linux/slab.h> #include <linux/smp.h> #include <linux/signal.h> -#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -365,16 +360,24 @@ static int get_meminfo(char * buffer) struct sysinfo i; int len; +/* + * display in kilobytes. + */ +#define K(x) ((x) << (PAGE_SHIFT - 10)) + si_meminfo(&i); si_swapinfo(&i); len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" - "Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" + "Mem: %8lu %8lu %8lu %8lu %8lu %8u\n" "Swap: %8lu %8lu %8lu\n", - i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, (unsigned long) atomic_read(&page_cache_size)*PAGE_SIZE, - i.totalswap, i.totalswap-i.freeswap, i.freeswap); + K(i.totalram), K(i.totalram-i.freeram), K(i.freeram), + K(i.sharedram), K(i.bufferram), + K(atomic_read(&page_cache_size)), K(i.totalswap), + K(i.totalswap-i.freeswap), K(i.freeswap)); /* - * Tagged format, for easy grepping and expansion. The above will go away - * eventually, once the tools have been updated. + * Tagged format, for easy grepping and expansion. + * The above will go away eventually, once the tools + * have been updated. */ return len + sprintf(buffer+len, "MemTotal: %8lu kB\n" @@ -382,19 +385,20 @@ static int get_meminfo(char * buffer) "MemShared: %8lu kB\n" "Buffers: %8lu kB\n" "Cached: %8u kB\n" - "BigTotal: %8lu kB\n" - "BigFree: %8lu kB\n" + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n", - i.totalram >> 10, - i.freeram >> 10, - i.sharedram >> 10, - i.bufferram >> 10, - atomic_read(&page_cache_size) << (PAGE_SHIFT - 10), - i.totalbig >> 10, - i.freebig >> 10, - i.totalswap >> 10, - i.freeswap >> 10); + K(i.totalram), + K(i.freeram), + K(i.sharedram), + K(i.bufferram), + K(atomic_read(&page_cache_size)), + K(i.totalhigh), + K(i.freehigh), + K(i.totalswap), + K(i.freeswap)); +#undef K } static int get_version(char * buffer) @@ -412,69 +416,68 @@ static int get_cmdline(char * buffer) return sprintf(buffer, "%s\n", saved_command_line); } -static unsigned long get_phys_addr(struct mm_struct * mm, unsigned long ptr) +static struct page * get_phys_addr(struct mm_struct * mm, unsigned long ptr) { - pgd_t *page_dir; - pmd_t *page_middle; + pgd_t *pgd; + pmd_t *pmd; pte_t pte; if (ptr >= TASK_SIZE) return 0; - page_dir = pgd_offset(mm,ptr); - if (pgd_none(*page_dir)) + pgd = pgd_offset(mm,ptr); + if (pgd_none(*pgd)) return 0; - if (pgd_bad(*page_dir)) { - printk("bad page directory entry %08lx\n", pgd_val(*page_dir)); - pgd_clear(page_dir); + if (pgd_bad(*pgd)) { + pgd_ERROR(*pgd); + pgd_clear(pgd); return 0; } - page_middle = pmd_offset(page_dir,ptr); - if (pmd_none(*page_middle)) + pmd = pmd_offset(pgd,ptr); + if (pmd_none(*pmd)) return 0; - if (pmd_bad(*page_middle)) { - printk("bad page middle entry %08lx\n", pmd_val(*page_middle)); - pmd_clear(page_middle); + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); return 0; } - pte = *pte_offset(page_middle,ptr); + pte = *pte_offset(pmd,ptr); if (!pte_present(pte)) return 0; - return pte_page(pte) + (ptr & ~PAGE_MASK); + return pte_page(pte); } -#include <linux/bigmem.h> - static int get_array(struct mm_struct *mm, unsigned long start, unsigned long end, char * buffer) { - unsigned long addr; + struct page *page; + unsigned long kaddr; int size = 0, result = 0; char c; if (start >= end) return result; for (;;) { - addr = get_phys_addr(mm, start); - if (!addr) + page = get_phys_addr(mm, start); + if (!page) return result; - addr = kmap(addr, KM_READ); + kaddr = kmap(page, KM_READ) + (start & ~PAGE_MASK); do { - c = *(char *) addr; + c = *(char *) kaddr; if (!c) result = size; if (size < PAGE_SIZE) buffer[size++] = c; else { - kunmap(addr, KM_READ); + kunmap(kaddr, KM_READ); return result; } - addr++; + kaddr++; start++; if (!c && start >= end) { - kunmap(addr, KM_READ); + kunmap(kaddr, KM_READ); return result; } - } while (addr & ~PAGE_MASK); - kunmap(addr-1, KM_READ); + } while (kaddr & ~PAGE_MASK); + kunmap(kaddr, KM_READ); } return result; } @@ -483,9 +486,7 @@ static struct mm_struct *get_mm(int pid) { struct task_struct *p; struct mm_struct *mm = NULL; - - /* need kernel lock to avoid the tsk->mm to go away under us */ - lock_kernel(); + read_lock(&tasklist_lock); p = find_task_by_pid(pid); if (p) @@ -493,10 +494,10 @@ static struct mm_struct *get_mm(int pid) if (mm) atomic_inc(&mm->mm_users); read_unlock(&tasklist_lock); - unlock_kernel(); return mm; } + static int get_env(int pid, char * buffer) { struct mm_struct *mm = get_mm(pid); @@ -859,9 +860,6 @@ static inline char * task_mem(struct mm_struct *mm, char *buffer) return buffer; } -/* - * These next two assume that the task's sigmask_lock is held by the caller. - */ static void collect_sigign_sigcatch(struct task_struct *p, sigset_t *ign, sigset_t *catch) { @@ -914,115 +912,77 @@ extern inline char *task_cap(struct task_struct *p, char *buffer) cap_t(p->cap_effective)); } -/* - * This is somewhat safer than it was before. However... - * - * Embedded pointers in the task structure may reference data that - * can be changed or that is no longer valid after the tasklist - * lock is released, or that isn't even protected by the tasklist - * lock. Eg. tsk->tty, tsk->sig, and tsk->p_pptr can change after - * we make our own copy of the task structure. This doesn't matter - * unless we are trying to use the pointed-to data as an address. - * So there are still a few safety issues to be addressed here. - */ + static int get_status(int pid, char * buffer) { char * orig = buffer; struct task_struct *tsk; struct mm_struct *mm = NULL; - /* - * We lock the whole kernel here because p->files and p->mm are still - * protected by the global kernel lock. - */ - lock_kernel(); - read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); - if (tsk) { + if (tsk) mm = tsk->mm; - if (mm) - atomic_inc(&mm->mm_users); - - buffer = task_name(tsk, buffer); - buffer = task_state(tsk, buffer); - - spin_lock_irq(&tsk->sigmask_lock); - buffer = task_sig(tsk, buffer); - spin_unlock_irq(&tsk->sigmask_lock); - - buffer = task_cap(tsk, buffer); - } - read_unlock(&tasklist_lock); - - unlock_kernel(); - - /* - * We can't hold the tasklist_lock and jiggle the mmap_sem -- - * that can result in a deadlock. - */ - if (mm) { + if (mm) + atomic_inc(&mm->mm_users); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + if (!tsk) + return 0; + buffer = task_name(tsk, buffer); + buffer = task_state(tsk, buffer); + if (mm) buffer = task_mem(mm, buffer); + buffer = task_sig(tsk, buffer); + buffer = task_cap(tsk, buffer); + if (mm) mmput(mm); - } - - /* - * (buffer - orig) will be zero on an error exit. - */ return buffer - orig; } static int get_stat(int pid, char * buffer) { struct task_struct *tsk; - struct mm_struct *mm; + struct mm_struct *mm = NULL; unsigned long vsize, eip, esp, wchan; long priority, nice; - pid_t ppid = 0; + int tty_pgrp; sigset_t sigign, sigcatch; char state; - int res = 0; - unsigned int tty_device; - int tty_pgrp; + int res; read_lock(&tasklist_lock); tsk = find_task_by_pid(pid); - if (!tsk) - goto out_unlock; - /* avoid the task list to go away under us (security) */ - get_page(MAP_NR(tsk) + mem_map); - ppid = tsk->p_pptr->pid; - read_unlock(&tasklist_lock); - - /* we need the big kernel lock to avoid tsk->mm and tsk->tty - to change under us */ - lock_kernel(); - mm = tsk->mm; + if (tsk) + mm = tsk->mm; if (mm) atomic_inc(&mm->mm_users); - tty_device = tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0; - tty_pgrp = tsk->tty ? tsk->tty->pgrp : -1; - unlock_kernel(); - - spin_lock_irq(&tsk->sigmask_lock); - collect_sigign_sigcatch(tsk, &sigign, &sigcatch); - spin_unlock_irq(&tsk->sigmask_lock); - - eip = KSTK_EIP(tsk); - esp = KSTK_ESP(tsk); - wchan = get_wchan(tsk); - + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + if (!tsk) + return 0; state = *get_task_state(tsk); vsize = eip = esp = 0; - if (mm) - { + if (mm) { struct vm_area_struct *vma; down(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) + vma = mm->mmap; + while (vma) { vsize += vma->vm_end - vma->vm_start; + vma = vma->vm_next; + } + eip = KSTK_EIP(tsk); + esp = KSTK_ESP(tsk); up(&mm->mmap_sem); } + wchan = get_wchan(tsk); + + collect_sigign_sigcatch(tsk, &sigign, &sigcatch); + + if (tsk->tty) + tty_pgrp = tsk->tty->pgrp; + else + tty_pgrp = -1; + /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ priority = tsk->counter; @@ -1036,10 +996,10 @@ static int get_stat(int pid, char * buffer) pid, tsk->comm, state, - ppid, + tsk->p_pptr->pid, tsk->pgrp, tsk->session, - tty_device, + tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0, tty_pgrp, tsk->flags, tsk->min_flt, @@ -1076,16 +1036,9 @@ static int get_stat(int pid, char * buffer) tsk->cnswap, tsk->exit_signal, tsk->processor); - if (mm) mmput(mm); - free_task_struct(tsk); return res; - -out_unlock: - read_unlock(&tasklist_lock); - unlock_kernel(); - return 0; } static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, @@ -1097,7 +1050,7 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned if (pmd_none(*pmd)) return; if (pmd_bad(*pmd)) { - printk("statm_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); + pmd_ERROR(*pmd); pmd_clear(pmd); return; } @@ -1135,7 +1088,7 @@ static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned if (pgd_none(*pgd)) return; if (pgd_bad(*pgd)) { - printk("statm_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); + pgd_ERROR(*pgd); pgd_clear(pgd); return; } @@ -1233,11 +1186,11 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, size_t count, loff_t *ppos) { struct task_struct *p; - struct mm_struct *mm = NULL; struct vm_area_struct * map, * next; char * destptr = buf, * buffer; loff_t lineno; ssize_t column, i; + int volatile_task; long retval; /* @@ -1249,30 +1202,24 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, goto out; retval = -EINVAL; - lock_kernel(); read_lock(&tasklist_lock); p = find_task_by_pid(pid); - if (p) { - mm = p->mm; - if (mm) - atomic_inc(&mm->mm_users); - } - read_unlock(&tasklist_lock); - unlock_kernel(); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ if (!p) goto freepage_out; - /* nothing to map */ - if (!mm || count == 0) + if (!p->mm || count == 0) goto getlen_out; + /* Check whether the mmaps could change if we sleep */ + volatile_task = (p != current || atomic_read(&p->mm->mm_users) > 1); + /* decode f_pos */ lineno = *ppos >> MAPS_LINE_SHIFT; column = *ppos & (MAPS_LINE_LENGTH-1); - down(&mm->mmap_sem); - /* quickly go to line "lineno" */ - for (map = mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) + /* quickly go to line lineno */ + for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) continue; for ( ; map ; map = next ) { @@ -1343,13 +1290,17 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, /* done? */ if (count == 0) break; + + /* By writing to user space, we might have slept. + * Stop the loop, to avoid a race condition. + */ + if (volatile_task) + break; } - up(&mm->mmap_sem); /* encode f_pos */ *ppos = (lineno << MAPS_LINE_SHIFT) + column; - mmput(mm); getlen_out: retval = destptr - buf; @@ -1362,31 +1313,28 @@ out: #ifdef __SMP__ static int get_pidcpu(int pid, char * buffer) { - struct task_struct * tsk; + struct task_struct * tsk = current ; int i, len = 0; - /* - * Hold the tasklist_lock to guarantee that the task_struct - * address will remain valid while we examine its contents. - */ read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - if (tsk) - get_page(MAP_NR(tsk) + mem_map); - read_unlock(&tasklist_lock); - if (tsk) { - len = sprintf(buffer, - "cpu %lu %lu\n", - HZ_TO_STD(tsk->times.tms_utime), - HZ_TO_STD(tsk->times.tms_stime)); - - for (i = 0 ; i < smp_num_cpus; i++) - len += sprintf(buffer + len, "cpu%d %lu %lu\n", - i, - HZ_TO_STD(tsk->per_cpu_utime[cpu_logical_map(i)]), - HZ_TO_STD(tsk->per_cpu_stime[cpu_logical_map(i)])); - free_task_struct(tsk); - } + if (pid != tsk->pid) + tsk = find_task_by_pid(pid); + read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ + + if (tsk == NULL) + return 0; + + len = sprintf(buffer, + "cpu %lu %lu\n", + HZ_TO_STD(tsk->times.tms_utime), + HZ_TO_STD(tsk->times.tms_stime)); + + for (i = 0 ; i < smp_num_cpus; i++) + len += sprintf(buffer + len, "cpu%d %lu %lu\n", + i, + HZ_TO_STD(tsk->per_cpu_utime[cpu_logical_map(i)]), + HZ_TO_STD(tsk->per_cpu_stime[cpu_logical_map(i)])); + return len; } #endif @@ -1519,6 +1467,12 @@ static int process_unauthorized(int type, int pid) int ok = 0; read_lock(&tasklist_lock); + + /* + * Grab the lock, find the task, save the uid and + * check it has an mm still (ie its not dead) + */ + p = find_task_by_pid(pid); if (p) { euid=p->euid; @@ -1526,7 +1480,9 @@ static int process_unauthorized(int type, int pid) if(!cap_issubset(p->cap_permitted, current->cap_permitted)) ok=0; } + read_unlock(&tasklist_lock); + if (!p) return 1; diff --git a/fs/proc/mem.c b/fs/proc/mem.c index f9fcb0970..90cd79722 100644 --- a/fs/proc/mem.c +++ b/fs/proc/mem.c @@ -10,7 +10,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/proc_fs.h> -#include <linux/bigmem.h> +#include <linux/highmem.h> #include <asm/page.h> #include <asm/uaccess.h> @@ -79,9 +79,10 @@ static ssize_t mem_read(struct file * file, char * buf, pgd_t *page_dir; pmd_t *page_middle; pte_t pte; - char * page; + struct page * page; struct task_struct * tsk; unsigned long addr; + unsigned long maddr; /* temporary mapped address */ char *tmp; ssize_t scount, i; @@ -102,7 +103,7 @@ static ssize_t mem_read(struct file * file, char * buf, if (pgd_none(*page_dir)) break; if (pgd_bad(*page_dir)) { - printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_ERROR(*page_dir); pgd_clear(page_dir); break; } @@ -110,20 +111,20 @@ static ssize_t mem_read(struct file * file, char * buf, if (pmd_none(*page_middle)) break; if (pmd_bad(*page_middle)) { - printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_ERROR(*page_middle); pmd_clear(page_middle); break; } pte = *pte_offset(page_middle,addr); if (!pte_present(pte)) break; - page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); + page = pte_page(pte); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > scount) i = scount; - page = (char *) kmap((unsigned long) page, KM_READ); - copy_to_user(tmp, page, i); - kunmap((unsigned long) page, KM_READ); + maddr = kmap(page, KM_READ); + copy_to_user(tmp, (char *)maddr + (addr & ~PAGE_MASK), i); + kunmap(maddr, KM_READ); addr += i; tmp += i; scount -= i; @@ -141,9 +142,10 @@ static ssize_t mem_write(struct file * file, char * buf, pgd_t *page_dir; pmd_t *page_middle; pte_t pte; - char * page; + struct page * page; struct task_struct * tsk; unsigned long addr; + unsigned long maddr; /* temporary mapped address */ char *tmp; long i; @@ -159,7 +161,7 @@ static ssize_t mem_write(struct file * file, char * buf, if (pgd_none(*page_dir)) break; if (pgd_bad(*page_dir)) { - printk("Bad page dir entry %08lx\n", pgd_val(*page_dir)); + pgd_ERROR(*page_dir); pgd_clear(page_dir); break; } @@ -167,7 +169,7 @@ static ssize_t mem_write(struct file * file, char * buf, if (pmd_none(*page_middle)) break; if (pmd_bad(*page_middle)) { - printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); + pmd_ERROR(*page_middle); pmd_clear(page_middle); break; } @@ -176,13 +178,13 @@ static ssize_t mem_write(struct file * file, char * buf, break; if (!pte_write(pte)) break; - page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); + page = pte_page(pte); i = PAGE_SIZE-(addr & ~PAGE_MASK); if (i > count) i = count; - page = (unsigned long) kmap((unsigned long) page, KM_WRITE); - copy_from_user(page, tmp, i); - kunmap((unsigned long) page, KM_WRITE); + maddr = kmap(page, KM_WRITE); + copy_from_user((char *)maddr + (addr & ~PAGE_MASK), tmp, i); + kunmap(maddr, KM_WRITE); addr += i; tmp += i; count -= i; @@ -248,14 +250,14 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) if (pgd_none(*src_dir)) return -EINVAL; if (pgd_bad(*src_dir)) { - printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir)); + pgd_ERROR(*src_dir); return -EINVAL; } src_middle = pmd_offset(src_dir, stmp); if (pmd_none(*src_middle)) return -EINVAL; if (pmd_bad(*src_middle)) { - printk("Bad source page middle entry %08lx\n", pmd_val(*src_middle)); + pmd_ERROR(*src_middle); return -EINVAL; } src_table = pte_offset(src_middle, stmp); @@ -301,9 +303,9 @@ int mem_mmap(struct file * file, struct vm_area_struct * vma) set_pte(src_table, pte_mkdirty(*src_table)); set_pte(dest_table, *src_table); - mapnr = MAP_NR(pte_page(*src_table)); + mapnr = pte_pagenr(*src_table); if (mapnr < max_mapnr) - get_page(mem_map + MAP_NR(pte_page(*src_table))); + get_page(mem_map + pte_pagenr(*src_table)); stmp += PAGE_SIZE; dtmp += PAGE_SIZE; |