diff -purN linux-2.4.20/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- linux-2.4.20/arch/i386/kernel/process.c 2003-04-10 12:08:41.000000000 +0200 +++ linux/arch/i386/kernel/process.c 2003-04-10 13:19:32.000000000 +0200 @@ -480,7 +480,7 @@ void show_regs(struct pt_regs * regs) /* * Create a kernel thread */ -int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { long retval, d0; diff -purN linux-2.4.20/arch/x86_64/kernel/entry.S linux/arch/x86_64/kernel/entry.S --- linux-2.4.20/arch/x86_64/kernel/entry.S 2003-04-10 12:08:41.000000000 +0200 +++ linux/arch/x86_64/kernel/entry.S 2003-04-10 13:25:04.000000000 +0200 @@ -542,12 +542,12 @@ bad_gs: * Create a kernel thread. * * C extern interface: - * extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) + * extern long arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) * * asm input arguments: * rdi: fn, rsi: arg, rdx: flags */ -ENTRY(kernel_thread) +ENTRY(arch_kernel_thread) FAKE_STACK_FRAME $child_rip SAVE_ALL diff -purN linux-2.4.20/fs/exec.c linux/fs/exec.c --- linux-2.4.20/fs/exec.c 2003-04-10 12:08:41.000000000 +0200 +++ linux/fs/exec.c 2003-04-10 13:22:39.000000000 +0200 @@ -576,8 +576,10 @@ int flush_old_exec(struct linux_binprm * current->sas_ss_sp = current->sas_ss_size = 0; - if (current->euid == current->uid && current->egid == current->gid) + if (current->euid == current->uid && current->egid == current->gid) { current->mm->dumpable = 1; + current->task_dumpable = 1; + } name = bprm->filename; for (i=0; (ch = *(name++)) != '\0';) { if (ch == '/') @@ -1085,8 +1087,8 @@ int do_coredump(long signr, struct pt_re binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) goto fail; - if (!current->mm->dumpable) - goto fail; + if (!is_dumpable(current)) + goto fail; current->mm->dumpable = 0; if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump) goto fail; diff -purN linux-2.4.20/include/asm-i386/processor.h linux/include/asm-i386/processor.h --- linux-2.4.20/include/asm-i386/processor.h 2003-04-10 12:08:41.000000000 +0200 +++ linux/include/asm-i386/processor.h 2003-04-10 13:19:32.000000000 +0200 @@ -433,7 +433,7 @@ extern void release_thread(struct task_s /* * create a kernel thread without removing it from tasklists */ -extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +extern int arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); /* Copy and release all segment info associated with a VM * Unusable due to lack of error handling, use {init_new,destroy}_context diff -purN linux-2.4.20/include/asm-x86_64/processor.h linux/include/asm-x86_64/processor.h --- linux-2.4.20/include/asm-x86_64/processor.h 2003-04-10 12:08:41.000000000 +0200 +++ linux/include/asm-x86_64/processor.h 2003-04-10 13:24:31.000000000 +0200 @@ -361,7 +361,7 @@ extern void release_thread(struct task_s /* * create a kernel thread without removing it from tasklists */ -extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); +extern long arch_kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); /* Copy and release all segment info associated with a VM */ extern void copy_segments(struct task_struct *p, struct mm_struct * mm); diff -purN linux-2.4.20/include/linux/sched.h linux/include/linux/sched.h --- linux-2.4.20/include/linux/sched.h 2003-04-10 12:08:42.000000000 +0200 +++ linux/include/linux/sched.h 2003-04-10 13:19:32.000000000 +0200 @@ -341,6 +341,7 @@ struct task_struct { /* ??? */ unsigned long personality; int did_exec:1; + unsigned task_dumpable:1; pid_t pid; pid_t pgrp; pid_t tty_old_pgrp; @@ -472,6 +473,8 @@ extern void yield(void); */ extern struct exec_domain default_exec_domain; +#define is_dumpable(tsk) ((tsk)->task_dumpable && (tsk)->mm && (tsk)->mm->dumpable) + /* * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x1fffff (=2MB) @@ -523,6 +526,8 @@ extern struct exec_domain default_exec_d #ifndef INIT_TASK_SIZE # define INIT_TASK_SIZE 2048*sizeof(long) #endif +extern long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); + union task_union { struct task_struct task; diff -purN linux-2.4.20/kernel/fork.c linux/kernel/fork.c --- linux-2.4.20/kernel/fork.c 2003-04-10 12:08:41.000000000 +0200 +++ linux/kernel/fork.c 2003-04-10 13:19:32.000000000 +0200 @@ -27,6 +27,7 @@ #include #include #include +#include /* The idle threads do not count.. */ int nr_threads; @@ -565,6 +566,31 @@ static inline void copy_flags(unsigned l p->flags = new_flags; } +long kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + struct task_struct *task = current; + unsigned old_task_dumpable; + long ret; + + /* lock out any potential ptracer */ + task_lock(task); + if (task->ptrace) { + task_unlock(task); + return -EPERM; + } + + old_task_dumpable = task->task_dumpable; + task->task_dumpable = 0; + task_unlock(task); + + ret = arch_kernel_thread(fn, arg, flags); + + /* never reached in child process, only in parent */ + current->task_dumpable = old_task_dumpable; + + return ret; +} + /* * Ok, this is the main fork-routine. It copies the system process * information (task[nr]) and sets up the necessary registers. It also diff -purN linux-2.4.20/kernel/ptrace.c linux/kernel/ptrace.c --- linux-2.4.20/kernel/ptrace.c 2002-08-03 02:39:46.000000000 +0200 +++ linux/kernel/ptrace.c 2003-04-10 13:24:10.000000000 +0200 @@ -21,6 +21,11 @@ */ int ptrace_check_attach(struct task_struct *child, int kill) { + mb(); + + if (!child->task_dumpable) + return -EPERM; + if (!(child->ptrace & PT_PTRACED)) return -ESRCH; @@ -70,7 +75,7 @@ int ptrace_attach(struct task_struct *ta (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) goto bad; rmb(); - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) + if (!is_dumpable(task) && !capable(CAP_SYS_PTRACE)) goto bad; /* the same process cannot be attached many times */ if (task->ptrace & PT_PTRACED) @@ -128,14 +133,15 @@ int ptrace_detach(struct task_struct *ch int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write) { - struct mm_struct *mm; + struct mm_struct *mm = NULL; struct vm_area_struct *vma; struct page *page; void *old_buf = buf; /* Worry about races with exit() */ task_lock(tsk); - mm = tsk->mm; + if (tsk->task_dumpable && (&init_mm != tsk->mm)) + mm = tsk->mm; if (mm) atomic_inc(&mm->mm_users); task_unlock(tsk); diff -purN linux-2.4.20/kernel/sys.c linux/kernel/sys.c --- linux-2.4.20/kernel/sys.c 2003-04-10 12:08:42.000000000 +0200 +++ linux/kernel/sys.c 2003-04-10 13:19:32.000000000 +0200 @@ -1228,7 +1228,7 @@ asmlinkage long sys_prctl(int option, un error = put_user(current->pdeath_signal, (int *)arg2); break; case PR_GET_DUMPABLE: - if (current->mm->dumpable) + if (is_dumpable(current)) error = 1; break; case PR_SET_DUMPABLE: @@ -1236,7 +1236,8 @@ asmlinkage long sys_prctl(int option, un error = -EINVAL; break; } - current->mm->dumpable = arg2; + if (is_dumpable(current)) + current->mm->dumpable = arg2; break; case PR_SET_UNALIGN: #ifdef SET_UNALIGN_CTL