Commit 8133aaa1 authored by Jeff Dike's avatar Jeff Dike

A number of small fixes for the uaccess merge.

parent 3817da13
...@@ -33,9 +33,8 @@ extern int do_signal(int error); ...@@ -33,9 +33,8 @@ extern int do_signal(int error);
extern int is_stack_fault(unsigned long sp); extern int is_stack_fault(unsigned long sp);
extern unsigned long segv(unsigned long address, unsigned long ip, extern unsigned long segv(unsigned long address, unsigned long ip,
int is_write, int is_user, void *sc); int is_write, int is_user, void *sc);
extern unsigned long handle_page_fault(unsigned long address, unsigned long ip, extern int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int is_write, int is_user, int *code_out);
int *code_out);
extern void syscall_ready(void); extern void syscall_ready(void);
extern void set_tracing(void *t, int tracing); extern void set_tracing(void *t, int tracing);
extern int is_tracing(void *task); extern int is_tracing(void *task);
......
...@@ -54,7 +54,7 @@ static inline int clear_user(void *mem, int len) ...@@ -54,7 +54,7 @@ static inline int clear_user(void *mem, int len)
return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len)); return(CHOOSE_MODE_PROC(clear_user_tt, clear_user_skas, mem, len));
} }
static inline int strnlen_user(void *str, int len) static inline int strnlen_user(const void *str, int len)
{ {
return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len)); return(CHOOSE_MODE_PROC(strnlen_user_tt, strnlen_user_skas, str, len));
} }
......
...@@ -10,7 +10,7 @@ obj-y = config.o exec_kern.o exitcode.o frame_kern.o frame.o helper.o \ ...@@ -10,7 +10,7 @@ obj-y = config.o exec_kern.o exitcode.o frame_kern.o frame.o helper.o \
process_kern.o ptrace.o reboot.o resource.o sigio_user.o \ process_kern.o ptrace.o reboot.o resource.o sigio_user.o \
sigio_kern.o signal_kern.o signal_user.o smp.o syscall_kern.o \ sigio_kern.o signal_kern.o signal_user.o smp.o syscall_kern.o \
syscall_user.o sysrq.o sys_call_table.o tempfile.o time.o \ syscall_user.o sysrq.o sys_call_table.o tempfile.o time.o \
time_kern.o tlb.o trap_kern.o trap_user.o uaccess_user.o um_arch.o \ time_kern.o tlb.o trap_kern.o trap_user.o um_arch.o \
umid.o user_util.o umid.o user_util.o
obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o obj-$(CONFIG_BLK_DEV_INITRD) += initrd_kern.o initrd_user.o
......
...@@ -222,21 +222,25 @@ int page_mask(void) ...@@ -222,21 +222,25 @@ int page_mask(void)
return(PAGE_MASK); return(PAGE_MASK);
} }
unsigned long um_virt_to_phys(void *t, unsigned long addr) void *um_virt_to_phys(struct task_struct *task, unsigned long addr,
pte_t *pte_out)
{ {
struct task_struct *task;
pgd_t *pgd; pgd_t *pgd;
pmd_t *pmd; pmd_t *pmd;
pte_t *pte; pte_t *pte;
task = t; if(task->mm == NULL)
if(task->mm == NULL) return(0xffffffff); return(ERR_PTR(-EINVAL));
pgd = pgd_offset(task->mm, addr); pgd = pgd_offset(task->mm, addr);
pmd = pmd_offset(pgd, addr); pmd = pmd_offset(pgd, addr);
if(!pmd_present(*pmd)) return(0xffffffff); if(!pmd_present(*pmd))
return(ERR_PTR(-EINVAL));
pte = pte_offset_kernel(pmd, addr); pte = pte_offset_kernel(pmd, addr);
if(!pte_present(*pte)) return(0xffffffff); if(!pte_present(*pte))
return((pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK)); return(ERR_PTR(-EINVAL));
if(pte_out != NULL)
*pte_out = *pte;
return((void *) (pte_val(*pte) & PAGE_MASK) + (addr & ~PAGE_MASK));
} }
char *current_cmd(void) char *current_cmd(void)
...@@ -244,8 +248,8 @@ char *current_cmd(void) ...@@ -244,8 +248,8 @@ char *current_cmd(void)
#if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM) #if defined(CONFIG_SMP) || defined(CONFIG_HIGHMEM)
return("(Unknown)"); return("(Unknown)");
#else #else
unsigned long addr = um_virt_to_phys(current, current->mm->arg_start); void *addr = um_virt_to_phys(current, current->mm->arg_start, NULL);
return addr == 0xffffffff? "(Unknown)": __va(addr); return IS_ERR(addr) ? "(Unknown)": __va((unsigned long) addr);
#endif #endif
} }
......
...@@ -26,10 +26,6 @@ static inline int verify_area_skas(int type, const void * addr, ...@@ -26,10 +26,6 @@ static inline int verify_area_skas(int type, const void * addr,
return(access_ok_skas(type, addr, size) ? 0 : -EFAULT); return(access_ok_skas(type, addr, size) ? 0 : -EFAULT);
} }
extern unsigned long handle_page_fault(unsigned long address,
unsigned long ip, int is_write,
int is_user, int *code_out);
extern void *um_virt_to_phys(struct task_struct *task, unsigned long virt, extern void *um_virt_to_phys(struct task_struct *task, unsigned long virt,
pte_t *pte_out); pte_t *pte_out);
...@@ -212,7 +208,7 @@ static inline int strnlen_chunk(unsigned long str, int len, void *arg) ...@@ -212,7 +208,7 @@ static inline int strnlen_chunk(unsigned long str, int len, void *arg)
return(0); return(0);
} }
static inline int strnlen_user_skas(void *str, int len) static inline int strnlen_user_skas(const void *str, int len)
{ {
int count = 0, n; int count = 0, n;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
#include "linux/kernel.h" #include "linux/kernel.h"
#include "asm/errno.h"
#include "linux/sched.h" #include "linux/sched.h"
#include "linux/mm.h" #include "linux/mm.h"
#include "linux/spinlock.h" #include "linux/spinlock.h"
...@@ -19,41 +20,36 @@ ...@@ -19,41 +20,36 @@
#include "kern_util.h" #include "kern_util.h"
#include "kern.h" #include "kern.h"
#include "chan_kern.h" #include "chan_kern.h"
#include "debug.h"
#include "mconsole_kern.h" #include "mconsole_kern.h"
#include "2_5compat.h" #include "2_5compat.h"
extern int nsyscalls; int handle_page_fault(unsigned long address, unsigned long ip,
int is_write, int is_user, int *code_out)
unsigned long segv(unsigned long address, unsigned long ip, int is_write,
int is_user, void *sc)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct siginfo si;
void *catcher;
pgd_t *pgd; pgd_t *pgd;
pmd_t *pmd; pmd_t *pmd;
pte_t *pte; pte_t *pte;
unsigned long page; unsigned long page;
int err = -EFAULT;
if((address >= start_vm) && (address < end_vm)){ *code_out = SEGV_MAPERR;
flush_tlb_kernel_vm();
return(0);
}
if(mm == NULL) panic("Segfault with no mm");
catcher = current->thread.fault_catcher;
si.si_code = SEGV_MAPERR;
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
vma = find_vma(mm, address); vma = find_vma(mm, address);
if(!vma) goto bad; if(!vma)
else if(vma->vm_start <= address) goto good_area; goto out;
else if(!(vma->vm_flags & VM_GROWSDOWN)) goto bad; else if(vma->vm_start <= address)
else if(expand_stack(vma, address)) goto bad; goto good_area;
else if(!(vma->vm_flags & VM_GROWSDOWN))
goto out;
else if(expand_stack(vma, address))
goto out;
good_area: good_area:
si.si_code = SEGV_ACCERR; *code_out = SEGV_ACCERR;
if(is_write && !(vma->vm_flags & VM_WRITE)) goto bad; if(is_write && !(vma->vm_flags & VM_WRITE))
goto out;
page = address & PAGE_MASK; page = address & PAGE_MASK;
if(page == (unsigned long) current->thread_info + PAGE_SIZE) if(page == (unsigned long) current->thread_info + PAGE_SIZE)
panic("Kernel stack overflow"); panic("Kernel stack overflow");
...@@ -69,8 +65,10 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, ...@@ -69,8 +65,10 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
current->maj_flt++; current->maj_flt++;
break; break;
case VM_FAULT_SIGBUS: case VM_FAULT_SIGBUS:
goto do_sigbus; err = -EACCES;
goto out;
case VM_FAULT_OOM: case VM_FAULT_OOM:
err = -ENOMEM;
goto out_of_memory; goto out_of_memory;
default: default:
BUG(); BUG();
...@@ -80,43 +78,45 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, ...@@ -80,43 +78,45 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
*pte = pte_mkyoung(*pte); *pte = pte_mkyoung(*pte);
if(pte_write(*pte)) *pte = pte_mkdirty(*pte); if(pte_write(*pte)) *pte = pte_mkdirty(*pte);
flush_tlb_page(vma, page); flush_tlb_page(vma, page);
err = 0;
out:
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
return(0); return(err);
do_sigbus:
up_read(&mm->mmap_sem);
/*
* Send a sigbus, regardless of whether we were in kernel
* or user mode.
*/
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRERR;
si.si_addr = (void *)address;
force_sig_info(SIGBUS, &si, current);
if(!is_user) goto bad;
return(0);
/* /*
* We ran out of memory, or some other thing happened to us that made * We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully. * us unable to handle the page fault gracefully.
*/ */
out_of_memory: out_of_memory:
up_read(&mm->mmap_sem);
if (current->pid == 1) { if (current->pid == 1) {
up_read(&mm->mmap_sem);
yield(); yield();
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
goto survive; goto survive;
} }
printk("VM: killing process %s\n", current->comm); err = -ENOMEM;
if(is_user) goto out;
do_exit(SIGKILL); }
/* Fall through to bad */ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
int is_user, void *sc)
{
struct siginfo si;
void *catcher;
int err;
if(!is_user && (address >= start_vm) && (address < end_vm)){
flush_tlb_kernel_vm();
return(0);
}
if(current->mm == NULL) panic("Segfault with no mm");
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
bad: catcher = current->thread.fault_catcher;
if(catcher != NULL){ if(!err)
return(0);
else if(catcher != NULL){
current->thread.fault_addr = (void *) address; current->thread.fault_addr = (void *) address;
up_read(&mm->mmap_sem);
do_longjmp(catcher, 1); do_longjmp(catcher, 1);
} }
else if(current->thread.fault_addr != NULL){ else if(current->thread.fault_addr != NULL){
...@@ -128,12 +128,25 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write, ...@@ -128,12 +128,25 @@ unsigned long segv(unsigned long address, unsigned long ip, int is_write,
if(!is_user) if(!is_user)
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx", panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
address, ip); address, ip);
if(err == -EACCES){
si.si_signo = SIGBUS;
si.si_errno = 0;
si.si_code = BUS_ADRERR;
si.si_addr = (void *)address;
force_sig_info(SIGBUS, &si, current);
}
else if(err == -ENOMEM){
printk("VM: killing process %s\n", current->comm);
do_exit(SIGKILL);
}
else {
si.si_signo = SIGSEGV; si.si_signo = SIGSEGV;
si.si_addr = (void *) address; si.si_addr = (void *) address;
current->thread.cr2 = address; current->thread.cr2 = address;
current->thread.err = is_write; current->thread.err = is_write;
force_sig_info(SIGSEGV, &si, current); force_sig_info(SIGSEGV, &si, current);
up_read(&mm->mmap_sem); }
return(0); return(0);
} }
......
...@@ -98,7 +98,7 @@ static inline int clear_user_tt(void *mem, int len) ...@@ -98,7 +98,7 @@ static inline int clear_user_tt(void *mem, int len)
extern int __do_strnlen_user(const char *str, unsigned long n, extern int __do_strnlen_user(const char *str, unsigned long n,
void **fault_addr, void **fault_catcher); void **fault_addr, void **fault_catcher);
static inline int strnlen_user_tt(void *str, int len) static inline int strnlen_user_tt(const void *str, int len)
{ {
return(__do_strnlen_user(str, len, return(__do_strnlen_user(str, len,
&current->thread.fault_addr, &current->thread.fault_addr,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment