Brad Spengler <spender@grsecurity>:
Due to Linux kernel developers continuing to silently fix exploitable bugs (in particular, trivially exploitable NULL ptr dereference bugs continue to be fixed without any mention of their security implications) we continue to suggest that the 2.6 kernels be avoided if possible.
______
As one of the easy ways of static audit of known misuses of C, known for ages security bugs, kernel (or any other call/return) interfaces to userspace, etc., i am proposing text processing.
Main concept is annotation (much like as sparse annotations for types/memory regions):
* call traces,
* parameters
* and return values useage.
Checking if function/functionality (macro, module, etc.) is uses as annotated, seems like ordinary multiline `grep`, which is `sed`.
Also, annotations can help in porting big patches, gresecurity is. In general, i define annotation as another way of expression of rules, functionality, changes or whatever. While patching with classic `diff`&&`patch` is text based, it has nothing to do with semantic load of that text (what code changes, how it does it, what it adds, what it removes, relations, etc). Another text processing can check semantics:
* variable addition, its usage
* function parameters, its usage
* whole modules with multiple functions and data structures
* addition, usage, etc.
Yea, it's just a handwaving. But to check at least something, i need someone as a partner (i have social problem with very deep longliness). This is why i've started this publication here. Even better thing will be, if this group (if created) will be paid for at least research in/try+implement of this ideas.
There are problems in the Linux as in whole handcrafted programming. But as experimentation with LKML shows, there is a big mismatch of text processing skills (read first post here). Even in sed users list, which i finally have subscribed. Mature member didn't get question/task right. Now he seems to understood that and provided a solution, i've posted already ~5 hour ago (i's yahoo list, whicj is closed to non yahoo users). Thus, maybe no good GSoC mentor can handle proposition and whole that money thing for this research...
cargo cult security: "that vmsplice() fuzz" (thanks to PaX Team)
leave stupid vmsplice() hole alone finally (GCC 4.3.0 exposes a
more to avertlabs' comments
My big respect to PaX Team,
My big respect to PaX Team, even this is one person, maintaining a dead horse i386 for nearly 8 years... It will be very upsetting, if all wark will be lost in the dust of sky rocketing cracking easy-git-merging-Linux.
Maybe IDL ideas can be aplied here as well: main change log syntax *is* an IDL for automatic text processing, ordinary comments are for humans.
Re: maintaining patches (Re: grsecurity 2.1.11 released for Linu
review 40% from bottom of pax-linux-2.6.24.4-test42.patch
From olecom Fri Apr 18 07:52:29 2008 Date: Fri, 18 Apr 2008 07:52:29 +0200 To: PaX Subject: review 40% from bottom of pax-linux-2.6.24.4-test42.patch Message-ID: <20080418055229.GK24008@flower.upol.cz> Hallo. Just started to look and see :) Some easy-to-do things i'll do soon, so code itself will the main target. == repeated patterns are for `sed` == - struct nfs4_exception exception = { }; + struct nfs4_exception exception = {0, 0}; int err; do { err = nfs4_handle_exception(server, @@ -2345,7 +2345,7 @@ static int _nfs4_do_fsinfo(struct nfs_se static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) { - struct nfs4_exception exception = { }; + struct nfs4_exception exception = {0, 0}; int err; do { @@ -2388,7 +2388,7 @@ static int _nfs4_proc_pathconf(struct nf static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_pathconf *pathconf) { - struct nfs4_exception exception = { }; + struct nfs4_exception exception = {0, 0}; == $ARCH code copies == --- linux-2.6.24.4/include/asm-*/pgtable.h 2008-01-24 23:58:37.000000000 +0100 +++ linux-2.6.24.4-pax/include/asm-*/pgtable.h 2008-02-29 18:07:50.000000000 +0100 huge chunks of same asm-$ARCH/* can be copied by `sed` and thing like this: -D(14) KM_TYPE_NR +D(14) KM_CLEARPAGE, +D(15) KM_TYPE_NR is very easy job for `sed` also == typo? == - sprintf(path, "/sys/block/%s/dev", name); - fd = sys_open(path, 0, 0); + if (sizeof path <= snprintf(path, sizeof path, "/sys/block/%s/dev", name)) ^^^^^^ typo or OK? == loads of annotations/phony do {} while == + fd = sys_open((char __user *)path, 0, 0); if (fd < 0) goto fail; - len = sys_read(fd, buf, 32); + len = sys_read(fd, (char __user *)buf, 32); i can give it for free with `sed`, as well as -#define SCTP_DEBUG_PRINTK(whatever...) -#define SCTP_DEBUG_PRINTK_IPADDR(whatever...) +#define SCTP_DEBUG_PRINTK(whatever...) do {} while (0) +#define SCTP_DEBUG_PRINTK_IPADDR(whatever...) do {} while (0) OK, this is the most easy part, so script will be in today or quite soon == +#ifdef CONFIG_PAX_SEGMEXEC + if (write_access || !pax_find_mirror_vma(vma)) +#endif + unlock_page(page); if it's OK, maybe a macro will be better? == no indent == +#ifdef CONFIG_PAX_SEGMEXEC + if (mm->pax_flags & MF_PAX_SEGMEXEC) { + if (end > SEGMEXEC_TASK_SIZE) + return -EINVAL; + } else +#endif + + if (end > TASK_SIZE) + return -EINVAL; no indent is OK? == macro? == + +#ifdef CONFIG_PAX_SEGMEXEC + if ((vma->vm_mm->pax_flags & MF_PAX_SEGMEXEC) && vma->vm_start == SEGMEXEC_TASK_SIZE) + return 0; +#endif + if (is_mergeable_vma(vma, file, vm_flags) && is_mergeable_anon_vma(anon_vma, vma->anon_vma)) { if (vma->vm_pgoff == vm_pgoff) @@ -695,6 +724,12 @@ static int can_vma_merge_after(struct vm_area_struct *vma, unsigned long vm_flags, struct anon_vma *anon_vma, struct file *file, pgoff_t vm_pgoff) { + +#ifdef CONFIG_PAX_SEGMEXEC + if ((vma->vm_mm->pax_flags & MF_PAX_SEGMEXEC) && vma->vm_end == SEGMEXEC_TASK_SIZE) + return 0; +#endif + macro? == whilespace == - struct anon_vma *anon_vma, struct file *file, + struct anon_vma *anon_vma, struct file *file, sometimes it seems, like somebody does whitespace cleanup, but it's not fully automated or done by emacs... ==== void vm_stat_account(struct mm_struct *mm, unsigned long flags, struct file *file, long pages) { - const unsigned long stack_flags - = VM_STACK_FLAGS & (VM_GROWSUP|VM_GROWSDOWN); - if (file) { mm->shared_vm += pages; if ((flags & (VM_EXEC|VM_WRITE)) == VM_EXEC) mm->exec_vm += pages; - } else if (flags & stack_flags) + } else if (flags & (VM_GROWSUP|VM_GROWSDOWN)) mm->stack_vm += pages; real crap or just optimization? - error = arch_mmap_check(addr, len, flags); - if (error) - return error; - /* Careful about overflows.. */ len = PAGE_ALIGN(len); if (!len || len > TASK_SIZE) return -ENOMEM; + error = arch_mmap_check(addr, len, flags); + if (error) + return error; + 4mainline fix, or whatever? - unsigned int vm_flags = vma->vm_flags; + unsigned long vm_flags = vma->vm_flags; i always wonder about all those int/longs dance in 32/64 bits. What is that this time? Maybe this is pure 4mainline stuff? /* If it was private or non-writable, the write bit is already clear */ - if ((vm_flags & (VM_WRITE|VM_SHARED)) != ((VM_WRITE|VM_SHARED))) + if ((vm_flags & (VM_WRITE|VM_SHARED)) != (VM_WRITE|VM_SHARED)) too LISPy ?-) == +#ifdef CONFIG_PAX_RANDMMAP + if (!(mm->pax_flags & MF_PAX_RANDMMAP)) +#endif + /* requesting a specific address */ if (addr) { addr = PAGE_ALIGN(addr); is this a way of patching with small impact or error? == + charged = len >> PAGE_SHIFT; [...] seems like good performance and size optimization == + error = __do_munmap(mm, start_m, end_m - start_m); + if (error) + return -ENOMEM; here is just a return, but + } +#endif + /* * If we make a private mapping writable we increase our commit; * but (without finer accounting) cannot reduce our commit if we @@ -186,6 +264,25 @@ mprotect_fixup(struct vm_area_struct *vm goto fail; } +#ifdef CONFIG_PAX_SEGMEXEC + if ((mm->pax_flags & MF_PAX_SEGMEXEC) && !(oldflags & VM_EXEC) && (newflags & VM_EXEC)) { + struct mempolicy *pol; + + vma_m = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); + if (!vma_m) { + error = -ENOMEM; + goto fail; here is goto... == +static inline void pax_handle_maywrite(struct vm_area_struct *vma, unsigned long start) +{ + struct elfhdr elf_h; + struct elf_phdr elf_p; + elf_addr_t dyn_offset = 0UL; + elf_dyn dyn; + unsigned long i, j = 65536UL / sizeof(struct elf_phdr); i wonder why there's 0UL (zero UL), 65536 instead of (1UL << 16) +#ifndef CONFIG_PAX_NOELFRELOCS + if ((vma->vm_start != start) || + !vma->vm_file || + !(vma->vm_flags & VM_MAYEXEC) || + (vma->vm_flags & VM_MAYNOTWRITE)) +#endif + + return; this return seem out of #ifndef block + + if (sizeof(elf_h) != kernel_read(vma->vm_file, 0UL, (char *)&elf_h, sizeof(elf_h)) || + memcmp(elf_h.e_ident, ELFMAG, SELFMAG) || + +#ifdef CONFIG_PAX_ETEXECRELOCS + (elf_h.e_type != ET_DYN && elf_h.e_type != ET_EXEC) || +#else + elf_h.e_type != ET_DYN || +#endif + + !elf_check_arch(&elf_h) || + elf_h.e_phentsize != sizeof(struct elf_phdr) || + elf_h.e_phnum > j) + return; so ugly; maybe to set up some #defines for this? + for (i = 0UL; i < elf_h.e_phnum; i++) { + if (sizeof(elf_p) != kernel_read(vma->vm_file, elf_h.e_phoff + i*sizeof(elf_p), (char *)&elf_p, sizeof(elf_p))) + return; coding style thing (screen width) + if (elf_p.p_type == PT_DYNAMIC) { + dyn_offset = elf_p.p_offset; + j = i; + } + } + if (elf_h.e_phnum <= j) + return; + + i = 0UL; + do { + if (sizeof(dyn) != kernel_read(vma->vm_file, dyn_offset + i*sizeof(dyn), (char *)&dyn, sizeof(dyn))) + return; this kernel_read() and above can be a macro for sure, or whole if() even + if (dyn.d_tag == DT_TEXTREL || (dyn.d_tag == DT_FLAGS && (dyn.d_un.d_val & DF_TEXTREL))) { + vma->vm_flags |= VM_MAYWRITE | VM_MAYNOTWRITE; + return; + } + i++; + } while (dyn.d_tag != DT_NULL); + return; +} ______more text-related propositions
From olecom Fri Apr 18 11:25:00 2008 Date: Fri, 18 Apr 2008 11:25:00 +0200 To: PaX Subject: more text-related propositions (Re: review 40% from bottom of pax-linux-2.6.24.4-test42.patch) My view of another #ifdef'ery. main goals: + readability of main C code + finding semantic units + code (this units) reuse - syntax of meta-code is kind of unusual for most programmers input: +unsigned long +arch_get_unmapped_area(struct file *filp, unsigned long addr, + unsigned long len, unsigned long pgoff, unsigned long flags) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long start_addr, pax_task_size = TASK_SIZE; + +#ifdef CONFIG_PAX_SEGMEXEC + if (mm->pax_flags & MF_PAX_SEGMEXEC) + pax_task_size = SEGMEXEC_TASK_SIZE; +#endif + + if (len > pax_task_size) + return -ENOMEM; + + if (flags & MAP_FIXED) + return addr; + +#ifdef CONFIG_PAX_RANDMMAP + if (!(mm->pax_flags & MF_PAX_RANDMMAP) || !filp) +#endif + + if (addr) { + addr = PAGE_ALIGN(addr); + vma = find_vma(mm, addr); + if (pax_task_size - len >= addr && + (!vma || addr + len <= vma->vm_start)) + return addr; + } output: + unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags) { struct mm_struct *mm = current->mm; struct vm_area_struct *vma; unsigned long start_addr, pax_task_size; if (flags & MAP_FIXED) return addr; set_pax_task_size(); if (len > pax_task_size) return -ENOMEM; if (CHECK_PAX_RANDMMAP && addr) { addr = PAGE_ALIGN(addr); vma = find_vma(mm, addr); if (pax_task_size - len >= addr && (!vma || addr + len <= vma->vm_start)) return addr; } + where below are meta constructs in shell, ugly? non maintainable? (even this can be generated, but i'm not sure about more readable syntax) set_pax_task_size(){ local out=' pax_task_size = TASK_SIZE; ' [ "$PAX_SEGMEXEC" ] && out=' if (mm->pax_flags & MF_PAX_SEGMEXEC) pax_task_size = SEGMEXEC_TASK_SIZE; else '$out echo "$out" } CHECK_PAX_RANDMMAP(){ local out=1 [ "PAX_RANDMMAP" ] && out=' (!(mm->pax_flags & MF_PAX_RANDMMAP) || !filp)' echo "$out" }non diff-like example: subsitute copy_* macro
From olecom Fri Apr 18 13:06:40 2008 Date: Fri, 18 Apr 2008 13:06:40 +0200 To: PaX Subject: non diff-like example: subsitute copy_* macro (Re: more text-related propositions...) #!/bin/sh WRAP_ASM=' /^[[:blank:]]*__asm__/{ :_asm n s " \\" g s ^.*$ "&\\\\n"\\ ; /^"\/\* asm end \*\//!b_asm d } ' pax_copy_to_user(){ sed '$!s $ \\ ;'"$WRAP_ASM" << '# pax_copy_end' static unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long size) { int __d0, __d1, __d2; __asm__ __volatile__( movw %w8,%%es cmp $7,%0 jbe 1f movl %1,%0 negl %0 andl $7,%0 subl %0,%3 4: rep; movsb movl %3,%0 shrl $2,%0 andl $3,%3 .align 2,0x90 0: rep; movsl movl %3,%0 1: rep; movsb 2: pushl %%ss popl %%es .section .fixup,"ax" 5: addl %3,%0 jmp 2b 3: lea 0(%3,%0,4),%0 jmp 2b .previous .section __ex_table,"a" .align 4 .long 4b,5b .long 0b,3b .long 1b,2b .previous /* asm end */ : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) : "3"(size), "0"(size), "1"(to), "2"(from), "r"(__USER_DS) : "memory"); return size; } static unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long size) { int __d0, __d1, __d2; __asm__ __volatile__( movw %w8,%%ds cmp $7,%0 jbe 1f movl %1,%0 negl %0 andl $7,%0 subl %0,%3 4: rep; movsb movl %3,%0 shrl $2,%0 andl $3,%3 .align 2,0x90 0: rep; movsl movl %3,%0 1: rep; movsb 2: pushl %%ss popl %%ds .section .fixup,"ax" 5: addl %3,%0 jmp 2b 3: lea 0(%3,%0,4),%0 jmp 2b .previous .section __ex_table,"a" .align 4 .long 4b,5b .long 0b,3b .long 1b,2b .previous /* asm end */ : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) : "3"(size), "0"(size), "1"(to), "2"(from), "r"(__USER_DS) : "memory"); return size; } static unsigned long __copy_user_zeroing(void *to, const void __user *from, unsigned long size) { int __d0, __d1, __d2; __asm__ __volatile__( movw %w8,%%ds cmp $7,%0 jbe 1f movl %1,%0 negl %0 andl $7,%0 subl %0,%3 4: rep; movsb movl %3,%0 shrl $2,%0 andl $3,%3 .align 2,0x90 0: rep; movsl movl %3,%0 1: rep; movsb 2: pushl %%ss popl %%ds .section .fixup,"ax" 5: addl %3,%0 jmp 6f 3: lea 0(%3,%0,4),%0 6: pushl %0 pushl %%eax xorl %%eax,%%eax rep; stosb popl %%eax popl %0 jmp 2b .previous .section __ex_table,"a" .align 4 .long 4b,5b .long 0b,3b .long 1b,6b .previous /* asm end */ : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) : "3"(size), "0"(size), "1"(to), "2"(from), "r"(__USER_DS) : "memory"); return size; } /* voila */ # pax_copy_end } RM_C_MACRO=':_line;n;/\\$/b_line;' sed -n ' /^#define __copy_user(to,from,size)/{'"$RM_C_MACRO"'d;} /^#define __copy_user_zeroing(to,from,size)/{'"$RM_C_MACRO"'n;c \ '"`pax_copy_to_user`"' ; } p ' << "EOF" /* test: original code start */ #endif /* CONFIG_X86_INTEL_USERCOPY */ /* Generic arbitrary sized copy. */ #define __copy_user(to,from,size) \ do { \ int __d0, __d1, __d2; \ __asm__ __volatile__( \ " cmp $7,%0\n" \ " jbe 1f\n" \ " movl %1,%0\n" \ " negl %0\n" \ " andl $7,%0\n" \ " subl %0,%3\n" \ "4: rep; movsb\n" \ " movl %3,%0\n" \ " shrl $2,%0\n" \ " andl $3,%3\n" \ " .align 2,0x90\n" \ "0: rep; movsl\n" \ " movl %3,%0\n" \ "1: rep; movsb\n" \ "2:\n" \ ".section .fixup,\"ax\"\n" \ "5: addl %3,%0\n" \ " jmp 2b\n" \ "3: lea 0(%3,%0,4),%0\n" \ " jmp 2b\n" \ ".previous\n" \ ".section __ex_table,\"a\"\n" \ " .align 4\n" \ " .long 4b,5b\n" \ " .long 0b,3b\n" \ " .long 1b,2b\n" \ ".previous" \ : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) \ : "3"(size), "0"(size), "1"(to), "2"(from) \ : "memory"); \ } while (0) #define __copy_user_zeroing(to,from,size) \ do { \ int __d0, __d1, __d2; \ __asm__ __volatile__( \ " cmp $7,%0\n" \ " jbe 1f\n" \ " movl %1,%0\n" \ " negl %0\n" \ " andl $7,%0\n" \ " subl %0,%3\n" \ "4: rep; movsb\n" \ " movl %3,%0\n" \ " shrl $2,%0\n" \ " andl $3,%3\n" \ " .align 2,0x90\n" \ "0: rep; movsl\n" \ " movl %3,%0\n" \ "1: rep; movsb\n" \ "2:\n" \ ".section .fixup,\"ax\"\n" \ "5: addl %3,%0\n" \ " jmp 6f\n" \ "3: lea 0(%3,%0,4),%0\n" \ "6: pushl %0\n" \ " pushl %%eax\n" \ " xorl %%eax,%%eax\n" \ " rep; stosb\n" \ " popl %%eax\n" \ " popl %0\n" \ " jmp 2b\n" \ ".previous\n" \ ".section __ex_table,\"a\"\n" \ " .align 4\n" \ " .long 4b,5b\n" \ " .long 0b,3b\n" \ " .long 1b,6b\n" \ ".previous" \ : "=&c"(size), "=&D" (__d0), "=&S" (__d1), "=r"(__d2) \ : "3"(size), "0"(size), "1"(to), "2"(from) \ : "memory"); \ } while (0) /* original code end */ EOFbatch conversion: phony macro && asm !volatile
From olecom Fri Apr 18 23:24:11 2008 Date: Fri, 18 Apr 2008 23:24:11 +0200 To: PaX Subject: batch conversion: phony macro && asm !volatile #!/bin/sh # space and tab, it's short [[:blank:]] S=`printf '[ \t]'` sed " # phony macro -> do {} while (0) /^#$S*define$S*[^ (]*([^)]*)$S*\(\/\*.*\*\/$S*\)*$/{ s-)$S*\(/[*].*[*]/\)*.*-)\tdo {} while (0)- } # asm -> asm volatile /$S*_*asm_*$S*(/{ s-\(asm\(_*\)\)$S*-\1 \2volatile\2 - } " << '___test' #define LIST_POISON1 ((void *) 0xFF1001FFFF1001FFULL) #define LIST_POISON2 ((void *) 0xFF2002FFFF2002FFULL) #define MF_PAX_PAGEEXEC 0x01000000 /* Paging based non-executable pages */ #define MF_PAX_EMUTRAMP 0x02000000 /* Emulate trampolines */ #define jbd_debug(f, a...) /**/ #define snd_BUG() /* nothing */ #define snd_pcm_oss_proc_init(pcm) #define snd_pcm_oss_proc_done(pcm) #define snd_use_lock_init(lockp) /**/ #define snd_use_lock_use(lockp) /**/ #define snd_use_lock_free(lockp) /**/ #define snd_use_lock_sync(lockp) /**/ asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx)); asm("wrmsr" : : "a" (~0), "d" (edx), "c" (ecx)); asm("cpuid" __asm__ __volatile ( __asm__ ( ___testjust a bright day