Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal
Linus Torvalds [Wed, 10 Oct 2012 03:02:25 +0000 (12:02 +0900)]
Pull generic execve() changes from Al Viro:
 "This introduces the generic kernel_thread() and kernel_execve()
  functions, and switches x86, arm, alpha, um and s390 over to them."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/signal: (26 commits)
  s390: convert to generic kernel_execve()
  s390: switch to generic kernel_thread()
  s390: fold kernel_thread_helper() into ret_from_fork()
  s390: fold execve_tail() into start_thread(), convert to generic sys_execve()
  um: switch to generic kernel_thread()
  x86, um/x86: switch to generic sys_execve and kernel_execve
  x86: split ret_from_fork
  alpha: introduce ret_from_kernel_execve(), switch to generic kernel_execve()
  alpha: switch to generic kernel_thread()
  alpha: switch to generic sys_execve()
  arm: get rid of execve wrapper, switch to generic execve() implementation
  arm: optimized current_pt_regs()
  arm: introduce ret_from_kernel_execve(), switch to generic kernel_execve()
  arm: split ret_from_fork, simplify kernel_thread() [based on patch by rmk]
  generic sys_execve()
  generic kernel_execve()
  new helper: current_pt_regs()
  preparation for generic kernel_thread()
  um: kill thread->forking
  um: let signal_delivered() do SIGTRAP on singlestepping into handler
  ...

57 files changed:
1  2 
arch/Kconfig
arch/alpha/include/asm/Kbuild
arch/alpha/kernel/process.c
arch/arm/Kconfig
arch/arm/include/asm/unistd.h
arch/arm/kernel/calls.S
arch/arm/kernel/entry-common.S
arch/arm/kernel/process.c
arch/avr32/include/asm/Kbuild
arch/c6x/include/asm/Kbuild
arch/cris/include/asm/Kbuild
arch/frv/include/asm/Kbuild
arch/h8300/include/asm/Kbuild
arch/ia64/include/asm/Kbuild
arch/m32r/include/asm/Kbuild
arch/m68k/include/asm/Kbuild
arch/microblaze/include/asm/Kbuild
arch/mn10300/include/asm/Kbuild
arch/parisc/include/asm/Kbuild
arch/s390/Kconfig
arch/s390/include/asm/processor.h
arch/s390/include/asm/unistd.h
arch/s390/kernel/compat_linux.c
arch/s390/kernel/compat_linux.h
arch/s390/kernel/compat_wrapper.S
arch/s390/kernel/entry64.S
arch/s390/kernel/process.c
arch/sparc/include/asm/Kbuild
arch/tile/include/asm/Kbuild
arch/um/include/asm/processor-generic.h
arch/um/kernel/exec.c
arch/um/kernel/process.c
arch/um/kernel/syscall.c
arch/unicore32/include/asm/Kbuild
arch/x86/Kconfig
arch/x86/ia32/ia32entry.S
arch/x86/ia32/sys_ia32.c
arch/x86/include/asm/processor.h
arch/x86/include/asm/sys_ia32.h
arch/x86/include/asm/thread_info.h
arch/x86/kernel/Makefile
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
arch/x86/kernel/signal.c
arch/x86/um/Kconfig
arch/xtensa/include/asm/Kbuild
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c
fs/exec.c
include/linux/binfmts.h
include/linux/compat.h
include/linux/ptrace.h
include/linux/sched.h
kernel/fork.c

diff --cc arch/Kconfig
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -1,5 -1,4 +1,6 @@@
  include include/asm-generic/Kbuild.asm
  
 +generic-y     += clkdev.h
+ generic-y     += exec.h
 +
  header-y      += cachectl.h
Simple merge
@@@ -8,4 -8,4 +8,5 @@@ header-y += etraxgpio.
  header-y += rs485.h
  header-y += sync_serial.h
  
 +generic-y += clkdev.h
+ generic-y += exec.h
@@@ -2,4 -2,4 +2,5 @@@ include include/asm-generic/Kbuild.as
  
  header-y += registers.h
  header-y += termios.h
 +generic-y += clkdev.h
+ generic-y += exec.h
@@@ -1,3 -1,2 +1,4 @@@
  include include/asm-generic/Kbuild.asm
 +
- generic-y     += clkdev.h
++generic-y += clkdev.h
+ generic-y += exec.h
@@@ -13,4 -13,4 +13,5 @@@ header-y += ptrace_offsets.
  header-y += rse.h
  header-y += ucontext.h
  header-y += ustack.h
 +generic-y += clkdev.h
+ generic-y += exec.h
@@@ -1,3 -1,3 +1,4 @@@
  include include/asm-generic/Kbuild.asm
  
- generic-y     += clkdev.h
++generic-y += clkdev.h
+ generic-y += exec.h
Simple merge
@@@ -1,4 -1,4 +1,5 @@@
  include include/asm-generic/Kbuild.asm
  
  header-y  += elf.h
 +generic-y += clkdev.h
+ generic-y += exec.h
@@@ -1,3 -1,3 +1,4 @@@
  include include/asm-generic/Kbuild.asm
  
 +generic-y += clkdev.h
+ generic-y += exec.h
@@@ -1,5 -1,5 +1,6 @@@
  include include/asm-generic/Kbuild.asm
  
  header-y += pdc.h
 +generic-y += clkdev.h
  generic-y += word-at-a-time.h
+ generic-y += exec.h
Simple merge
@@@ -142,15 -133,8 +145,14 @@@ struct task_struct
  struct mm_struct;
  struct seq_file;
  
 +#ifdef CONFIG_64BIT
 +extern void show_cacheinfo(struct seq_file *m);
 +#else
 +static inline void show_cacheinfo(struct seq_file *m) { }
 +#endif
 +
  /* Free all resources held by a thread. */
  extern void release_thread(struct task_struct *);
- extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
  
  /*
   * Return saved PC of a blocked thread.
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -169,15 -146,25 +149,30 @@@ int copy_thread(unsigned long clone_fla
        /* fake return stack for resume(), don't go back to schedule */
        frame->sf.gprs[9] = (unsigned long) frame;
  
-       /* Save access registers to new thread structure. */
-       save_access_regs(&p->thread.acrs[0]);
+       /* Store access registers to kernel stack of new process. */
+       if (unlikely(!regs)) {
+               /* kernel thread */
+               memset(&frame->childregs, 0, sizeof(struct pt_regs));
+               frame->childregs.psw.mask = psw_kernel_bits | PSW_MASK_DAT |
+                               PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
+               frame->childregs.psw.addr = PSW_ADDR_AMODE |
+                               (unsigned long) kernel_thread_starter;
+               frame->childregs.gprs[9] = new_stackp; /* function */
+               frame->childregs.gprs[10] = arg;
+               frame->childregs.gprs[11] = (unsigned long) do_exit;
+               frame->childregs.orig_gpr2 = -1;
+               return 0;
+       }
+       frame->childregs = *regs;
+       frame->childregs.gprs[2] = 0;   /* child returns 0 on fork. */
+       frame->childregs.gprs[15] = new_stackp;
  
 +      /* Don't copy runtime instrumentation info */
 +      p->thread.ri_cb = NULL;
 +      p->thread.ri_signum = 0;
 +      frame->childregs.psw.mask &= ~PSW_MASK_RI;
 +
  #ifndef CONFIG_64BIT
        /*
         * save fprs to current->thread.fp_regs to merge them with
@@@ -1,8 -1,24 +1,9 @@@
  # User exported sparc header files
 -include include/asm-generic/Kbuild.asm
  
 -header-y += apc.h
 -header-y += asi.h
 -header-y += display7seg.h
 -header-y += envctrl.h
 -header-y += fbio.h
 -header-y += jsflash.h
 -header-y += openpromio.h
 -header-y += perfctr.h
 -header-y += psrcompat.h
 -header-y += psr.h
 -header-y += pstate.h
 -header-y += traps.h
 -header-y += uctx.h
 -header-y += utrap.h
 -header-y += watchdog.h
  
 +generic-y += clkdev.h
  generic-y += div64.h
+ generic-y += exec.h
  generic-y += local64.h
  generic-y += irq_regs.h
  generic-y += local.h
Simple merge
  #include <asm/current.h>
  #include <asm/processor.h>
  #include <asm/uaccess.h>
 -#include "as-layout.h"
 -#include "mem_user.h"
 -#include "skas.h"
 -#include "os.h"
 +#include <as-layout.h>
 +#include <mem_user.h>
 +#include <skas.h>
 +#include <os.h>
- #include "internal.h"
  
  void flush_thread(void)
  {
Simple merge
@@@ -3,17 -3,16 +3,16 @@@
   * Licensed under the GPL
   */
  
 -#include "linux/file.h"
 -#include "linux/fs.h"
 -#include "linux/mm.h"
 -#include "linux/sched.h"
 -#include "linux/utsname.h"
 -#include "linux/syscalls.h"
 -#include "asm/current.h"
 -#include "asm/mman.h"
 -#include "asm/uaccess.h"
 -#include "asm/unistd.h"
 +#include <linux/file.h>
 +#include <linux/fs.h>
 +#include <linux/mm.h>
 +#include <linux/sched.h>
 +#include <linux/utsname.h>
 +#include <linux/syscalls.h>
 +#include <asm/current.h>
 +#include <asm/mman.h>
 +#include <asm/uaccess.h>
 +#include <asm/unistd.h>
- #include "internal.h"
  
  long sys_fork(void)
  {
Simple merge
@@@ -106,12 -97,10 +106,13 @@@ config X8
        select KTIME_SCALAR if X86_32
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
 +      select HAVE_RCU_USER_QS if X86_64
 +      select HAVE_IRQ_TIME_ACCOUNTING
+       select GENERIC_KERNEL_THREAD
  
  config INSTRUCTION_DECODER
 -      def_bool (KPROBES || PERF_EVENTS || UPROBES)
 +      def_bool y
 +      depends on KPROBES || PERF_EVENTS || UPROBES
  
  config OUTPUT_FORMAT
        string
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -190,6 -207,16 +207,12 @@@ start_thread(struct pt_regs *regs, unsi
        regs->cs                = __USER_CS;
        regs->ip                = new_ip;
        regs->sp                = new_sp;
+       regs->flags             = X86_EFLAGS_IF;
+       /*
 -       * Free the old FP and other extended state
 -       */
 -      free_thread_xstate(current);
 -      /*
+        * force it to the iret return path by making it look as if there was
+        * some work pending.
+        */
+       set_thread_flag(TIF_NOTIFY_RESUME);
  }
  EXPORT_SYMBOL_GPL(start_thread);
  
Simple merge
@@@ -839,12 -800,6 +839,8 @@@ do_notify_resume(struct pt_regs *regs, 
        }
        if (thread_info_flags & _TIF_USER_RETURN_NOTIFY)
                fire_user_return_notifiers();
 +
- #ifdef CONFIG_X86_32
-       clear_thread_flag(TIF_IRET);
- #endif /* CONFIG_X86_32 */
 +      rcu_user_enter();
  }
  
  void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
Simple merge
@@@ -1,3 -1,3 +1,4 @@@
  include include/asm-generic/Kbuild.asm
  
 +generic-y += clkdev.h
+ generic-y += exec.h
diff --cc fs/binfmt_elf.c
  #include <asm/uaccess.h>
  #include <asm/param.h>
  #include <asm/page.h>
- #include <asm/exec.h>
  
 +#ifndef user_long_t
 +#define user_long_t long
 +#endif
 +#ifndef user_siginfo_t
 +#define user_siginfo_t siginfo_t
 +#endif
 +
  static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs);
  static int load_elf_library(struct file *);
  static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
Simple merge
diff --cc fs/exec.c
+++ b/fs/exec.c
@@@ -1658,3 -2031,342 +1657,55 @@@ int get_dumpable(struct mm_struct *mm
  {
        return __get_dumpable(mm->flags);
  }
 -static void wait_for_dump_helpers(struct file *file)
 -{
 -      struct pipe_inode_info *pipe;
 -
 -      pipe = file->f_path.dentry->d_inode->i_pipe;
 -
 -      pipe_lock(pipe);
 -      pipe->readers++;
 -      pipe->writers--;
 -
 -      while ((pipe->readers > 1) && (!signal_pending(current))) {
 -              wake_up_interruptible_sync(&pipe->wait);
 -              kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 -              pipe_wait(pipe);
 -      }
 -
 -      pipe->readers--;
 -      pipe->writers++;
 -      pipe_unlock(pipe);
 -
 -}
 -
 -
 -/*
 - * umh_pipe_setup
 - * helper function to customize the process used
 - * to collect the core in userspace.  Specifically
 - * it sets up a pipe and installs it as fd 0 (stdin)
 - * for the process.  Returns 0 on success, or
 - * PTR_ERR on failure.
 - * Note that it also sets the core limit to 1.  This
 - * is a special value that we use to trap recursive
 - * core dumps
 - */
 -static int umh_pipe_setup(struct subprocess_info *info, struct cred *new)
 -{
 -      struct file *files[2];
 -      struct fdtable *fdt;
 -      struct coredump_params *cp = (struct coredump_params *)info->data;
 -      struct files_struct *cf = current->files;
 -      int err = create_pipe_files(files, 0);
 -      if (err)
 -              return err;
 -
 -      cp->file = files[1];
 -
 -      sys_close(0);
 -      fd_install(0, files[0]);
 -      spin_lock(&cf->file_lock);
 -      fdt = files_fdtable(cf);
 -      __set_open_fd(0, fdt);
 -      __clear_close_on_exec(0, fdt);
 -      spin_unlock(&cf->file_lock);
 -
 -      /* and disallow core files too */
 -      current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
 -
 -      return 0;
 -}
 -
 -void do_coredump(long signr, int exit_code, struct pt_regs *regs)
 -{
 -      struct core_state core_state;
 -      struct core_name cn;
 -      struct mm_struct *mm = current->mm;
 -      struct linux_binfmt * binfmt;
 -      const struct cred *old_cred;
 -      struct cred *cred;
 -      int retval = 0;
 -      int flag = 0;
 -      int ispipe;
 -      bool need_nonrelative = false;
 -      static atomic_t core_dump_count = ATOMIC_INIT(0);
 -      struct coredump_params cprm = {
 -              .signr = signr,
 -              .regs = regs,
 -              .limit = rlimit(RLIMIT_CORE),
 -              /*
 -               * We must use the same mm->flags while dumping core to avoid
 -               * inconsistency of bit flags, since this flag is not protected
 -               * by any locks.
 -               */
 -              .mm_flags = mm->flags,
 -      };
 -
 -      audit_core_dumps(signr);
 -
 -      binfmt = mm->binfmt;
 -      if (!binfmt || !binfmt->core_dump)
 -              goto fail;
 -      if (!__get_dumpable(cprm.mm_flags))
 -              goto fail;
 -
 -      cred = prepare_creds();
 -      if (!cred)
 -              goto fail;
 -      /*
 -       * We cannot trust fsuid as being the "true" uid of the process
 -       * nor do we know its entire history. We only know it was tainted
 -       * so we dump it as root in mode 2, and only into a controlled
 -       * environment (pipe handler or fully qualified path).
 -       */
 -      if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) {
 -              /* Setuid core dump mode */
 -              flag = O_EXCL;          /* Stop rewrite attacks */
 -              cred->fsuid = GLOBAL_ROOT_UID;  /* Dump root private */
 -              need_nonrelative = true;
 -      }
 -
 -      retval = coredump_wait(exit_code, &core_state);
 -      if (retval < 0)
 -              goto fail_creds;
 -
 -      old_cred = override_creds(cred);
 -
 -      /*
 -       * Clear any false indication of pending signals that might
 -       * be seen by the filesystem code called to write the core file.
 -       */
 -      clear_thread_flag(TIF_SIGPENDING);
 -
 -      ispipe = format_corename(&cn, signr);
 -
 -      if (ispipe) {
 -              int dump_count;
 -              char **helper_argv;
 -
 -              if (ispipe < 0) {
 -                      printk(KERN_WARNING "format_corename failed\n");
 -                      printk(KERN_WARNING "Aborting core\n");
 -                      goto fail_corename;
 -              }
 -
 -              if (cprm.limit == 1) {
 -                      /* See umh_pipe_setup() which sets RLIMIT_CORE = 1.
 -                       *
 -                       * Normally core limits are irrelevant to pipes, since
 -                       * we're not writing to the file system, but we use
 -                       * cprm.limit of 1 here as a speacial value, this is a
 -                       * consistent way to catch recursive crashes.
 -                       * We can still crash if the core_pattern binary sets
 -                       * RLIM_CORE = !1, but it runs as root, and can do
 -                       * lots of stupid things.
 -                       *
 -                       * Note that we use task_tgid_vnr here to grab the pid
 -                       * of the process group leader.  That way we get the
 -                       * right pid if a thread in a multi-threaded
 -                       * core_pattern process dies.
 -                       */
 -                      printk(KERN_WARNING
 -                              "Process %d(%s) has RLIMIT_CORE set to 1\n",
 -                              task_tgid_vnr(current), current->comm);
 -                      printk(KERN_WARNING "Aborting core\n");
 -                      goto fail_unlock;
 -              }
 -              cprm.limit = RLIM_INFINITY;
 -
 -              dump_count = atomic_inc_return(&core_dump_count);
 -              if (core_pipe_limit && (core_pipe_limit < dump_count)) {
 -                      printk(KERN_WARNING "Pid %d(%s) over core_pipe_limit\n",
 -                             task_tgid_vnr(current), current->comm);
 -                      printk(KERN_WARNING "Skipping core dump\n");
 -                      goto fail_dropcount;
 -              }
 -
 -              helper_argv = argv_split(GFP_KERNEL, cn.corename+1, NULL);
 -              if (!helper_argv) {
 -                      printk(KERN_WARNING "%s failed to allocate memory\n",
 -                             __func__);
 -                      goto fail_dropcount;
 -              }
 -
 -              retval = call_usermodehelper_fns(helper_argv[0], helper_argv,
 -                                      NULL, UMH_WAIT_EXEC, umh_pipe_setup,
 -                                      NULL, &cprm);
 -              argv_free(helper_argv);
 -              if (retval) {
 -                      printk(KERN_INFO "Core dump to %s pipe failed\n",
 -                             cn.corename);
 -                      goto close_fail;
 -              }
 -      } else {
 -              struct inode *inode;
 -
 -              if (cprm.limit < binfmt->min_coredump)
 -                      goto fail_unlock;
 -
 -              if (need_nonrelative && cn.corename[0] != '/') {
 -                      printk(KERN_WARNING "Pid %d(%s) can only dump core "\
 -                              "to fully qualified path!\n",
 -                              task_tgid_vnr(current), current->comm);
 -                      printk(KERN_WARNING "Skipping core dump\n");
 -                      goto fail_unlock;
 -              }
 -
 -              cprm.file = filp_open(cn.corename,
 -                               O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
 -                               0600);
 -              if (IS_ERR(cprm.file))
 -                      goto fail_unlock;
 -
 -              inode = cprm.file->f_path.dentry->d_inode;
 -              if (inode->i_nlink > 1)
 -                      goto close_fail;
 -              if (d_unhashed(cprm.file->f_path.dentry))
 -                      goto close_fail;
 -              /*
 -               * AK: actually i see no reason to not allow this for named
 -               * pipes etc, but keep the previous behaviour for now.
 -               */
 -              if (!S_ISREG(inode->i_mode))
 -                      goto close_fail;
 -              /*
 -               * Dont allow local users get cute and trick others to coredump
 -               * into their pre-created files.
 -               */
 -              if (!uid_eq(inode->i_uid, current_fsuid()))
 -                      goto close_fail;
 -              if (!cprm.file->f_op || !cprm.file->f_op->write)
 -                      goto close_fail;
 -              if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
 -                      goto close_fail;
 -      }
 -
 -      retval = binfmt->core_dump(&cprm);
 -      if (retval)
 -              current->signal->group_exit_code |= 0x80;
 -
 -      if (ispipe && core_pipe_limit)
 -              wait_for_dump_helpers(cprm.file);
 -close_fail:
 -      if (cprm.file)
 -              filp_close(cprm.file, NULL);
 -fail_dropcount:
 -      if (ispipe)
 -              atomic_dec(&core_dump_count);
 -fail_unlock:
 -      kfree(cn.corename);
 -fail_corename:
 -      coredump_finish(mm);
 -      revert_creds(old_cred);
 -fail_creds:
 -      put_cred(cred);
 -fail:
 -      return;
 -}
 -
 -/*
 - * Core dumping helper functions.  These are the only things you should
 - * do on a core-file: use only these functions to write out all the
 - * necessary info.
 - */
 -int dump_write(struct file *file, const void *addr, int nr)
 -{
 -      return access_ok(VERIFY_READ, addr, nr) && file->f_op->write(file, addr, nr, &file->f_pos) == nr;
 -}
 -EXPORT_SYMBOL(dump_write);
 -
 -int dump_seek(struct file *file, loff_t off)
 -{
 -      int ret = 1;
 -
 -      if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
 -              if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
 -                      return 0;
 -      } else {
 -              char *buf = (char *)get_zeroed_page(GFP_KERNEL);
 -
 -              if (!buf)
 -                      return 0;
 -              while (off > 0) {
 -                      unsigned long n = off;
 -
 -                      if (n > PAGE_SIZE)
 -                              n = PAGE_SIZE;
 -                      if (!dump_write(file, buf, n)) {
 -                              ret = 0;
 -                              break;
 -                      }
 -                      off -= n;
 -              }
 -              free_page((unsigned long)buf);
 -      }
 -      return ret;
 -}
 -EXPORT_SYMBOL(dump_seek);
 -
+ #ifdef __ARCH_WANT_SYS_EXECVE
+ SYSCALL_DEFINE3(execve,
+               const char __user *, filename,
+               const char __user *const __user *, argv,
+               const char __user *const __user *, envp)
+ {
+       const char *path = getname(filename);
+       int error = PTR_ERR(path);
+       if (!IS_ERR(path)) {
+               error = do_execve(path, argv, envp, current_pt_regs());
+               putname(path);
+       }
+       return error;
+ }
+ #ifdef CONFIG_COMPAT
+ asmlinkage long compat_sys_execve(const char __user * filename,
+       const compat_uptr_t __user * argv,
+       const compat_uptr_t __user * envp)
+ {
+       const char *path = getname(filename);
+       int error = PTR_ERR(path);
+       if (!IS_ERR(path)) {
+               error = compat_do_execve(path, argv, envp, current_pt_regs());
+               putname(path);
+       }
+       return error;
+ }
+ #endif
+ #endif
+ #ifdef __ARCH_WANT_KERNEL_EXECVE
+ int kernel_execve(const char *filename,
+                 const char *const argv[],
+                 const char *const envp[])
+ {
+       struct pt_regs *p = current_pt_regs();
+       int ret;
+       ret = do_execve(filename,
+                       (const char __user *const __user *)argv,
+                       (const char __user *const __user *)envp, p);
+       if (ret < 0)
+               return ret;
+       /*
+        * We were successful.  We won't be returning to our caller, but
+        * instead to user space by manipulating the kernel stack.
+        */
+       ret_from_kernel_execve(p);
+ }
+ #endif
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc kernel/fork.c
Simple merge