Commit 7196d9df authored by Ingo Molnar's avatar Ingo Molnar Committed by Linus Torvalds

[PATCH] fix do_fork() return value

Noticed by Julie DeWandel <jdewand@redhat.com>.

do_fork() needs to return the pid (or error), not the pointer to the
resulting process structure.  The process structure may not even be
valid any more, since do_fork() has already woken the process up (and as
a result it might already have done its thing and gone away).

Besides, doing it this way cleans up the users, which all really just
wanted the pid or error number _anyway_.

This fixes the x86 users, other architectures need to be fixed up as
well.
parent c6b523ab
...@@ -212,7 +212,6 @@ __asm__(".align 4\n" ...@@ -212,7 +212,6 @@ __asm__(".align 4\n"
*/ */
int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
{ {
struct task_struct *p;
struct pt_regs regs; struct pt_regs regs;
memset(&regs, 0, sizeof(regs)); memset(&regs, 0, sizeof(regs));
...@@ -228,8 +227,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) ...@@ -228,8 +227,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
regs.eflags = 0x286; regs.eflags = 0x286;
/* Ok, create the new process.. */ /* Ok, create the new process.. */
p = do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL); return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
/* /*
...@@ -518,15 +516,11 @@ struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct ...@@ -518,15 +516,11 @@ struct task_struct * __switch_to(struct task_struct *prev_p, struct task_struct
asmlinkage int sys_fork(struct pt_regs regs) asmlinkage int sys_fork(struct pt_regs regs)
{ {
struct task_struct *p; return do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
p = do_fork(SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
asmlinkage int sys_clone(struct pt_regs regs) asmlinkage int sys_clone(struct pt_regs regs)
{ {
struct task_struct *p;
unsigned long clone_flags; unsigned long clone_flags;
unsigned long newsp; unsigned long newsp;
int __user *parent_tidptr, *child_tidptr; int __user *parent_tidptr, *child_tidptr;
...@@ -537,8 +531,7 @@ asmlinkage int sys_clone(struct pt_regs regs) ...@@ -537,8 +531,7 @@ asmlinkage int sys_clone(struct pt_regs regs)
child_tidptr = (int __user *)regs.edi; child_tidptr = (int __user *)regs.edi;
if (!newsp) if (!newsp)
newsp = regs.esp; newsp = regs.esp;
p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, parent_tidptr, child_tidptr); return do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, parent_tidptr, child_tidptr);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
/* /*
...@@ -553,10 +546,7 @@ asmlinkage int sys_clone(struct pt_regs regs) ...@@ -553,10 +546,7 @@ asmlinkage int sys_clone(struct pt_regs regs)
*/ */
asmlinkage int sys_vfork(struct pt_regs regs) asmlinkage int sys_vfork(struct pt_regs regs)
{ {
struct task_struct *p; return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, &regs, 0, NULL, NULL);
return IS_ERR(p) ? PTR_ERR(p) : p->pid;
} }
/* /*
......
...@@ -493,7 +493,7 @@ static struct task_struct * __init fork_by_hand(void) ...@@ -493,7 +493,7 @@ static struct task_struct * __init fork_by_hand(void)
* don't care about the eip and regs settings since * don't care about the eip and regs settings since
* we'll never reschedule the forked task. * we'll never reschedule the forked task.
*/ */
return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL, NULL); return copy_process(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL, NULL);
} }
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
...@@ -793,6 +793,7 @@ static int __init do_boot_cpu(int apicid) ...@@ -793,6 +793,7 @@ static int __init do_boot_cpu(int apicid)
idle = fork_by_hand(); idle = fork_by_hand();
if (IS_ERR(idle)) if (IS_ERR(idle))
panic("failed fork for CPU %d", cpu); panic("failed fork for CPU %d", cpu);
wake_up_forked_process(idle);
/* /*
* We remove it from the pidhash and the runqueue * We remove it from the pidhash and the runqueue
......
...@@ -531,7 +531,7 @@ fork_by_hand(void) ...@@ -531,7 +531,7 @@ fork_by_hand(void)
struct pt_regs regs; struct pt_regs regs;
/* don't care about the eip and regs settings since we'll /* don't care about the eip and regs settings since we'll
* never reschedule the forked task. */ * never reschedule the forked task. */
return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL, NULL); return copy_process(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL, NULL);
} }
......
...@@ -637,7 +637,8 @@ extern int allow_signal(int); ...@@ -637,7 +637,8 @@ extern int allow_signal(int);
extern task_t *child_reaper; extern task_t *child_reaper;
extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *);
extern struct task_struct *do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
extern struct task_struct * copy_process(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern void wait_task_inactive(task_t * p); extern void wait_task_inactive(task_t * p);
......
...@@ -752,12 +752,12 @@ asmlinkage long sys_set_tid_address(int __user *tidptr) ...@@ -752,12 +752,12 @@ asmlinkage long sys_set_tid_address(int __user *tidptr)
* parts of the process environment (as per the clone * parts of the process environment (as per the clone
* flags). The actual kick-off is left to the caller. * flags). The actual kick-off is left to the caller.
*/ */
static struct task_struct *copy_process(unsigned long clone_flags, struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start, unsigned long stack_start,
struct pt_regs *regs, struct pt_regs *regs,
unsigned long stack_size, unsigned long stack_size,
int __user *parent_tidptr, int __user *parent_tidptr,
int __user *child_tidptr) int __user *child_tidptr)
{ {
int retval; int retval;
struct task_struct *p = NULL; struct task_struct *p = NULL;
...@@ -1067,15 +1067,16 @@ static inline int fork_traceflag (unsigned clone_flags) ...@@ -1067,15 +1067,16 @@ static inline int fork_traceflag (unsigned clone_flags)
* It copies the process, and if successful kick-starts * It copies the process, and if successful kick-starts
* it and waits for it to finish using the VM if required. * it and waits for it to finish using the VM if required.
*/ */
struct task_struct *do_fork(unsigned long clone_flags, long do_fork(unsigned long clone_flags,
unsigned long stack_start, unsigned long stack_start,
struct pt_regs *regs, struct pt_regs *regs,
unsigned long stack_size, unsigned long stack_size,
int __user *parent_tidptr, int __user *parent_tidptr,
int __user *child_tidptr) int __user *child_tidptr)
{ {
struct task_struct *p; struct task_struct *p;
int trace = 0; int trace = 0;
long pid;
if (unlikely(current->ptrace)) { if (unlikely(current->ptrace)) {
trace = fork_traceflag (clone_flags); trace = fork_traceflag (clone_flags);
...@@ -1084,6 +1085,12 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -1084,6 +1085,12 @@ struct task_struct *do_fork(unsigned long clone_flags,
} }
p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr); p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr);
/*
* Do this prior waking up the new thread - the thread pointer
* might get invalid after that point, if the thread exits quickly.
*/
pid = IS_ERR(p) ? PTR_ERR(p) : p->pid;
if (!IS_ERR(p)) { if (!IS_ERR(p)) {
struct completion vfork; struct completion vfork;
...@@ -1104,7 +1111,7 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -1104,7 +1111,7 @@ struct task_struct *do_fork(unsigned long clone_flags,
++total_forks; ++total_forks;
if (unlikely (trace)) { if (unlikely (trace)) {
current->ptrace_message = (unsigned long) p->pid; current->ptrace_message = pid;
ptrace_notify ((trace << 8) | SIGTRAP); ptrace_notify ((trace << 8) | SIGTRAP);
} }
...@@ -1119,7 +1126,7 @@ struct task_struct *do_fork(unsigned long clone_flags, ...@@ -1119,7 +1126,7 @@ struct task_struct *do_fork(unsigned long clone_flags,
*/ */
set_need_resched(); set_need_resched();
} }
return p; return pid;
} }
/* SLAB cache for signal_struct structures (tsk->signal) */ /* SLAB cache for signal_struct structures (tsk->signal) */
......
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