summaryrefslogtreecommitdiffstats
path: root/fs/proc
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-01-27 01:05:20 +0000
commit546db14ee74118296f425f3b91634fb767d67290 (patch)
tree22b613a3da8d4bf663eec5e155af01b87fdf9094 /fs/proc
parent1e25e41c4f5474e14452094492dbc169b800e4c8 (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.c316
-rw-r--r--fs/proc/mem.c40
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;