Skip to content
Snippets Groups Projects
exec.c 54.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • void set_dumpable(struct mm_struct *mm, int value)
    {
    	switch (value) {
    
    	case SUID_DUMPABLE_DISABLED:
    
    		clear_bit(MMF_DUMPABLE, &mm->flags);
    		smp_wmb();
    		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
    		break;
    
    	case SUID_DUMPABLE_ENABLED:
    
    		set_bit(MMF_DUMPABLE, &mm->flags);
    		smp_wmb();
    		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
    		break;
    
    		set_bit(MMF_DUMP_SECURELY, &mm->flags);
    		smp_wmb();
    		set_bit(MMF_DUMPABLE, &mm->flags);
    		break;
    	}
    }
    
    
    static int __get_dumpable(unsigned long mm_flags)
    
    	ret = mm_flags & MMF_DUMPABLE_MASK;
    
    	return (ret > SUID_DUMPABLE_ENABLED) ? SUID_DUMPABLE_SAFE : ret;
    
    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)
    
    Al Viro's avatar
    Al Viro committed
    	struct file *files[2];
    
    	struct fdtable *fdt;
    	struct coredump_params *cp = (struct coredump_params *)info->data;
    	struct files_struct *cf = current->files;
    
    Al Viro's avatar
    Al Viro committed
    	int err = create_pipe_files(files, 0);
    	if (err)
    		return err;
    
    Al Viro's avatar
    Al Viro committed
    	cp->file = files[1];
    
    Al Viro's avatar
    Al Viro committed
    	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)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	struct core_state core_state;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	struct mm_struct *mm = current->mm;
    	struct linux_binfmt * binfmt;
    
    	const struct cred *old_cred;
    	struct cred *cred;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int retval = 0;
    
    Alan Cox's avatar
    Alan Cox committed
    	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,
    
    Jiri Slaby's avatar
    Jiri Slaby committed
    		.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,
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	audit_core_dumps(signr);
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (!binfmt || !binfmt->core_dump)
    		goto fail;
    
    	if (!__get_dumpable(cprm.mm_flags))
    		goto fail;
    
    
    	cred = prepare_creds();
    
    		goto fail;
    
    Alan Cox's avatar
    Alan Cox committed
    	/*
    
    	 * 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).
    
    Alan Cox's avatar
    Alan Cox committed
    	 */
    
    	if (__get_dumpable(cprm.mm_flags) == SUID_DUMPABLE_SAFE) {
    
    Alan Cox's avatar
    Alan Cox committed
    		flag = O_EXCL;		/* Stop rewrite attacks */
    
    		cred->fsuid = GLOBAL_ROOT_UID;	/* Dump root private */
    
    		need_nonrelative = true;
    
    Alan Cox's avatar
    Alan Cox committed
    	}
    
    	retval = coredump_wait(exit_code, &core_state);
    
    	if (retval < 0)
    		goto fail_creds;
    
    
    	old_cred = override_creds(cred);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	/*
    	 * 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);
    
    
    		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;
    		}
    
    
    			/* 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__);
    
    		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",
    
    			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;
    		}
    
    
    				 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
    				 0600);
    
    		if (IS_ERR(cprm.file))
    			goto fail_unlock;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    		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;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	retval = binfmt->core_dump(&cprm);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (retval)
    		current->signal->group_exit_code |= 0x80;
    
    		wait_for_dump_helpers(cprm.file);
    
    close_fail:
    	if (cprm.file)
    		filp_close(cprm.file, NULL);
    
    	if (ispipe)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    fail_unlock:
    
    	coredump_finish(mm);
    
    	revert_creds(old_cred);
    
    	put_cred(cred);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    fail:
    
    	return;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    
    /*
     * 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);
    
    Al Viro's avatar
    Al Viro committed
    
    
    Al Viro's avatar
    Al Viro committed
    #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
    
    
    Al Viro's avatar
    Al Viro committed
    #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