# From: Bodo Stroesser # # This patch adds vm_area structs for stub-code and stub-data. # So, stub-area is displayed in /proc/XXX/maps. Also, stub-pages # are accessible for debuggers via ptrace now. # # Linux has a gate-vma concept, that unfortunately supports one # gate-vma only. Thus, there need to be done some changes in # mm/memory.c and fs/proc/task_mmu.c. # This patch avoids the mainline changes by using some dirty tricks. # So, the patch is for testing only, mainline should be changed # to support more than one gate-vma. Index: linux-2.6.17/arch/um/kernel/mem.c =================================================================== --- linux-2.6.17.orig/arch/um/kernel/mem.c 2007-11-19 10:40:56.000000000 -0500 +++ linux-2.6.17/arch/um/kernel/mem.c 2007-11-19 10:59:36.000000000 -0500 @@ -13,6 +13,7 @@ #include "asm/page.h" #include "asm/fixmap.h" #include "asm/pgalloc.h" +#include "asm/elf.h" #include "kern_util.h" #include "as-layout.h" #include "kern.h" @@ -23,6 +24,9 @@ #include "linux/string.h" #include "init.h" #include "kern_constants.h" +#ifdef CONFIG_MODE_SKAS +#include "skas.h" +#endif /* allocated in paging_init, zeroed in mem_init, and unchanged thereafter */ unsigned long *empty_zero_page = NULL; @@ -189,14 +193,30 @@ static void __init init_highmem(void) } #endif /* CONFIG_HIGHMEM */ +extern int __syscall_stub_start; + static void __init fixaddr_user_init( void) { -#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA - long size = FIXADDR_USER_END - FIXADDR_USER_START; pgd_t *pgd; pud_t *pud; pmd_t *pmd; pte_t *pte; + +#ifdef CONFIG_MODE_SKAS + if(skas_needs_stub){ + fixrange_init(CONFIG_STUB_CODE, CONFIG_STUB_CODE + PAGE_SIZE, + swapper_pg_dir); + pgd = swapper_pg_dir + pgd_index(CONFIG_STUB_CODE); + pud = pud_offset(pgd, CONFIG_STUB_CODE); + pmd = pmd_offset(pud, CONFIG_STUB_CODE); + pte = pte_offset_kernel(pmd, CONFIG_STUB_CODE); + pte_set_val((*pte), __pa((unsigned long) &__syscall_stub_start), + PAGE_READONLY); + } +#endif + +#ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA + long size = FIXADDR_USER_END - FIXADDR_USER_START; unsigned long paddr, vaddr = FIXADDR_USER_START; if ( ! size ) Index: linux-2.6.17/arch/um/kernel/skas/mmu.c =================================================================== --- linux-2.6.17.orig/arch/um/kernel/skas/mmu.c 2007-10-24 13:05:10.000000000 -0400 +++ linux-2.6.17/arch/um/kernel/skas/mmu.c 2007-11-19 11:07:52.000000000 -0500 @@ -11,8 +11,6 @@ #include "os.h" #include "skas.h" -extern int __syscall_stub_start; - static int init_stub_pte(struct mm_struct *mm, unsigned long proc, unsigned long kernel) { @@ -84,11 +82,6 @@ int init_new_context(struct task_struct */ mm->pgd[USER_PTRS_PER_PGD] = __pgd(0); - ret = init_stub_pte(mm, STUB_CODE, - (unsigned long) &__syscall_stub_start); - if (ret) - goto out_free; - ret = init_stub_pte(mm, STUB_DATA, stack); if (ret) goto out_free; Index: linux-2.6.17/arch/um/sys-i386/Makefile =================================================================== --- linux-2.6.17.orig/arch/um/sys-i386/Makefile 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/sys-i386/Makefile 2007-11-19 11:09:45.000000000 -0500 @@ -2,9 +2,9 @@ # Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) # -obj-y = bug.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ - ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o sysrq.o \ - sys_call_table.o tls.o +obj-y = bug.o bugs.o checksum.o delay.o fault.o gate_vma.o ksyms.o ldt.o \ + ptrace.o ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o \ + sysrq.o sys_call_table.o tls.o subarch-obj-y = lib/bitops_32.o lib/semaphore_32.o lib/string_32.o subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem_32.o Index: linux-2.6.17/arch/um/sys-i386/gate_vma.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.17/arch/um/sys-i386/gate_vma.c 2007-11-19 10:59:36.000000000 -0500 @@ -0,0 +1,281 @@ +#include "linux/fs.h" +#include "linux/kdev_t.h" +#include "linux/mm.h" +#include "linux/sched.h" +#include "linux/seq_file.h" +#ifdef CONFIG_MODE_SKAS +#include "skas.h" +#endif + +static struct vm_area_struct gate_vmas[3]; + +static int vma_count; + +/* Dummy to make fs/proc/task_mmu.c happy */ +struct vm_area_struct *get_gate_vma(struct task_struct *tsk) +{ + if(vma_count) return gate_vmas; + + return NULL; +} + +int in_gate_area(struct task_struct *task, unsigned long addr) +{ + int i; + + for(i = 0; i < vma_count; i++) + if(gate_vmas[i].vm_start <= addr && gate_vmas[i].vm_end > addr) + return 1; + + return 0; +} + +struct vm_area_struct *uml_get_gate_vma(struct task_struct *task, + unsigned long addr) +{ + int i; + + for(i = 0; i < vma_count; i++) + if(gate_vmas[i].vm_start <= addr && gate_vmas[i].vm_end > addr) + return gate_vmas + i; + + return NULL; +} + +struct vm_area_struct * get_next_gate_vma(struct task_struct *task, + struct vm_area_struct *vma) +{ + int i; + + if(vma == NULL) + return vma_count ? gate_vmas : NULL; + + for(i = 0; i < vma_count-1; i++) + if(gate_vmas + i == vma) + return gate_vmas + i + 1; + + return NULL; +} + +int is_gate_vma(struct task_struct *task, struct vm_area_struct *vma) +{ + int i; + + for(i = 0; i < vma_count; i++) + if(gate_vmas + i == vma) return 1; + + return 0; +} + +/* sysbols in vsyscall only, not in stub-pages */ +int in_gate_area_no_task(unsigned long addr) +{ + return (addr >= FIXADDR_USER_START) && (addr < FIXADDR_USER_END); +} + +extern struct seq_operations proc_pid_maps_op; +static int uml_map_show(struct seq_file *m, void *v); +static void *uml_map_start(struct seq_file *m, loff_t *pos); +static void uml_map_stop(struct seq_file *m, void *v); +static void *uml_map_next(struct seq_file *m, void *v, loff_t *pos); + +static int __init gate_vma_init(void) +{ + struct vm_area_struct * vma = gate_vmas; + +#ifdef CONFIG_MODE_SKAS + if(skas_needs_stub){ + vma->vm_mm = NULL; + vma->vm_start = CONFIG_STUB_CODE; + vma->vm_end = CONFIG_STUB_CODE + PAGE_SIZE; + vma->vm_page_prot = PAGE_READONLY; + vma->vm_flags = VM_READ | VM_EXEC; + vma->vm_private_data = "[stub-code]"; + vma_count++; + vma++; + + vma->vm_mm = NULL; + vma->vm_start = CONFIG_STUB_DATA; + vma->vm_end = CONFIG_STUB_DATA + PAGE_SIZE; + vma->vm_page_prot = PAGE_SHARED; + vma->vm_flags = VM_READ | VM_WRITE; + vma->vm_private_data = "[stub-stack]"; + vma_count++; + vma++; + } +#endif + if(FIXADDR_USER_START){ + vma->vm_mm = NULL; + vma->vm_start = FIXADDR_USER_START; + vma->vm_end = FIXADDR_USER_END; + vma->vm_page_prot = PAGE_READONLY; + vma->vm_flags = 0; + vma_count++; + vma++; + } + + proc_pid_maps_op.start = uml_map_start; + proc_pid_maps_op.next = uml_map_next; + proc_pid_maps_op.stop = uml_map_stop; + proc_pid_maps_op.show = uml_map_show; + + return 0; +} +__initcall(gate_vma_init); + +/* + * The following routines are stolen from fs/proc/task_mmu.c + */ +static void pad_len_spaces(struct seq_file *m, int len) +{ + len = 25 + sizeof(void*) * 6 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c", len, ' '); +} + +static int uml_map_show(struct seq_file *m, void *v) +{ + struct task_struct *task = m->private; + struct vm_area_struct *map = v; + struct mm_struct *mm = map->vm_mm; + struct file *file = map->vm_file; + int flags = map->vm_flags; + unsigned long ino = 0; + dev_t dev = 0; + int len; + + if (file) { + struct inode *inode = map->vm_file->f_dentry->d_inode; + dev = inode->i_sb->s_dev; + ino = inode->i_ino; + } + + seq_printf(m, "%08lx-%08lx %c%c%c%c %08lx %02x:%02x %lu %n", + map->vm_start, + map->vm_end, + flags & VM_READ ? 'r' : '-', + flags & VM_WRITE ? 'w' : '-', + flags & VM_EXEC ? 'x' : '-', + flags & VM_MAYSHARE ? 's' : 'p', + map->vm_pgoff << PAGE_SHIFT, + MAJOR(dev), MINOR(dev), ino, &len); + + /* + * Print the dentry name for named mappings, and a + * special [heap] marker for the heap: + */ + if (map->vm_file) { + pad_len_spaces(m, len); + seq_path(m, file->f_vfsmnt, file->f_dentry, ""); + } else { + if (mm) { + if (map->vm_start <= mm->start_brk && + map->vm_end >= mm->brk) { + pad_len_spaces(m, len); + seq_puts(m, "[heap]"); + } else { + if (map->vm_start <= mm->start_stack && + map->vm_end >= mm->start_stack) { + + pad_len_spaces(m, len); + seq_puts(m, "[stack]"); + } + } + } else { + pad_len_spaces(m, len); + if(map->vm_private_data) + seq_puts(m, map->vm_private_data); + else seq_puts(m, "[vdso]"); + } + } + seq_putc(m, '\n'); + if (m->count < m->size) /* map is copied successfully */ + m->version = is_gate_vma(task, map)? 0: map->vm_start; + return 0; +} + +static void *uml_map_start(struct seq_file *m, loff_t *pos) +{ + struct task_struct *task = m->private; + unsigned long last_addr = m->version; + struct mm_struct *mm; + struct vm_area_struct *map; + loff_t l = *pos; + + /* + * We remember last_addr rather than next_addr to hit with + * mmap_cache most of the time. We have zero last_addr at + * the begining and also after lseek. We will have -1 last_addr + * after the end of the maps. + */ + + if (last_addr == -1UL) + return NULL; + + mm = get_task_mm(task); + if (!mm) + return NULL; + + down_read(&mm->mmap_sem); + + m->version = 0; + + /* Start with last addr hint */ + if (last_addr && (map = find_vma(mm, last_addr))) { + map = map->vm_next; + goto out; + } + + /* + * Check the map index is within the range and do + * sequential scan until m_index. + */ + map = NULL; + if ((unsigned long)l < mm->map_count) { + map = mm->mmap; + while (l-- && map) + map = map->vm_next; + goto out; + } + + /* End of maps has reached */ + up_read(&mm->mmap_sem); + mmput(mm); + + l -= mm->map_count; + do { + map = get_next_gate_vma(task, map); + } while(l-- && map); + + m->version = (map != NULL)? 0: -1UL; + +out: + return map; +} + +static void uml_map_stop(struct seq_file *m, void *v) +{ + struct task_struct *task = m->private; + struct vm_area_struct *map = v; + if (map && !is_gate_vma(task, map)) { + struct mm_struct *mm = map->vm_mm; + up_read(&mm->mmap_sem); + mmput(mm); + } +} + +static void *uml_map_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct task_struct *task = m->private; + struct vm_area_struct *map = v; + int is_gate = is_gate_vma(task, map); + + (*pos)++; + if (map && !is_gate && map->vm_next) + return map->vm_next; + uml_map_stop(m, v); + if (map && !is_gate) + map = NULL; + return get_next_gate_vma(task, map); +} Index: linux-2.6.17/arch/um/sys-x86_64/Makefile =================================================================== --- linux-2.6.17.orig/arch/um/sys-x86_64/Makefile 2007-10-24 10:04:50.000000000 -0400 +++ linux-2.6.17/arch/um/sys-x86_64/Makefile 2007-11-19 11:10:16.000000000 -0500 @@ -4,9 +4,9 @@ # Licensed under the GPL # -obj-y = bug.o bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \ - setjmp.o signal.o stub.o stub_segv.o syscalls.o syscall_table.o \ - sysrq.o ksyms.o tls.o +obj-y = bug.o bugs.o delay.o fault.o gate_vma.o ldt.o mem.o ptrace.o \ + ptrace_user.o setjmp.o signal.o stub.o stub_segv.o syscalls.o \ + syscall_table.o sysrq.o ksyms.o tls.o obj-$(CONFIG_MODULES) += um_module.o @@ -14,6 +14,7 @@ subarch-obj-y = lib/bitops_64.o lib/csum subarch-obj-$(CONFIG_MODULES) += kernel/module_64.o ldt-y = ../sys-i386/ldt.o +gate_vma-y = ../sys-i386/gate_vma.o USER_OBJS := ptrace_user.o Index: linux-2.6.17/include/asm-um/page.h =================================================================== --- linux-2.6.17.orig/include/asm-um/page.h 2007-11-19 10:52:01.000000000 -0500 +++ linux-2.6.17/include/asm-um/page.h 2007-11-19 10:59:36.000000000 -0500 @@ -121,4 +121,6 @@ extern void arch_free_page(struct page * #include #include +#define __HAVE_ARCH_GATE_AREA + #endif Index: linux-2.6.17/include/asm-um/pgalloc.h =================================================================== --- linux-2.6.17.orig/include/asm-um/pgalloc.h 2007-10-24 10:05:09.000000000 -0400 +++ linux-2.6.17/include/asm-um/pgalloc.h 2007-11-19 10:59:36.000000000 -0500 @@ -54,6 +54,48 @@ static inline void pmd_free(pmd_t *pmd) #endif + +/* + * This normally should be in asm/page.h, where + * "#define __HAVE_ARCH_GATE_AREA" + * is defined. But for our extension to support more than + * one gate-vma without changing mainline sources, we need to + * redefine the call to get_gate_vma() in mm/memory.c to + * 1) have the address as second parameter + * 2) add a check for returned vma's flags, because our + * stub-stack is writable. If the returned vma is stub-stack, + * we reset the write flag to not return error. This is safe, + * since after having processed stub-stack's vma, the loop in + * get_user_space will end. So the changed "write"-flag has + * no further effect. + * 3) make TASK_SIZE in get_user_pages-loop depend on + * skas_needs_stub: our stub pages are above TASK_SIZE, but + * we need to have at least the pte of stub-stack in user-mm, + * not init_mm, because each user-mm has it's own writable + * stub-stack-page. We override global task_size with a local + * one for this purpose. + * Unfortunately linux/mm.h has a prototype for get_gate_vma(), + * so we need to do redefinition by macro in a file, that is + * included after linux/mm.h + * Anyway, the method we are using to implement "multi-gate-vma" + * is quite unclean and should be replaced by a mainline change. + */ +#define get_gate_vma(task) \ + uml_get_gate_vma(task, start); /* start is the current address */ \ + /* Prepare to override task_size */ \ + unsigned long my_task_size = TASK_SIZE; \ + /* Now check for allowed write access */ \ + /* and adjust task_size to get the right mm */ \ + if(gate_vma->vm_flags & VM_WRITE) { \ + my_task_size = (unsigned long)-1L; \ + if(write) write = 0; \ + } \ + unsigned long TASK_SIZE = my_task_size; + +extern struct vm_area_struct *uml_get_gate_vma(struct task_struct *task, + unsigned long start); + + /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically Index: linux-2.6.17/fs/proc/task_mmu.c =================================================================== --- linux-2.6.17.orig/fs/proc/task_mmu.c 2007-10-17 12:11:52.000000000 -0400 +++ linux-2.6.17/fs/proc/task_mmu.c 2007-11-19 10:59:36.000000000 -0500 @@ -468,7 +468,7 @@ static void m_stop(struct seq_file *m, v put_task_struct(priv->task); } -static struct seq_operations proc_pid_maps_op = { +struct seq_operations proc_pid_maps_op = { .start = m_start, .next = m_next, .stop = m_stop,