diff options
Diffstat (limited to 'fs/coredump.c')
| -rw-r--r-- | fs/coredump.c | 46 | 
1 files changed, 38 insertions, 8 deletions
diff --git a/fs/coredump.c b/fs/coredump.c index c5ecde6f3eed..a8f75640ac86 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -513,10 +513,10 @@ void do_coredump(const siginfo_t *siginfo)  	const struct cred *old_cred;  	struct cred *cred;  	int retval = 0; -	int flag = 0;  	int ispipe;  	struct files_struct *displaced; -	bool need_nonrelative = false; +	/* require nonrelative corefile path and be extra careful */ +	bool need_suid_safe = false;  	bool core_dumped = false;  	static atomic_t core_dump_count = ATOMIC_INIT(0);  	struct coredump_params cprm = { @@ -550,9 +550,8 @@ void do_coredump(const siginfo_t *siginfo)  	 */  	if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) {  		/* Setuid core dump mode */ -		flag = O_EXCL;		/* Stop rewrite attacks */  		cred->fsuid = GLOBAL_ROOT_UID;	/* Dump root private */ -		need_nonrelative = true; +		need_suid_safe = true;  	}  	retval = coredump_wait(siginfo->si_signo, &core_state); @@ -633,7 +632,7 @@ void do_coredump(const siginfo_t *siginfo)  		if (cprm.limit < binfmt->min_coredump)  			goto fail_unlock; -		if (need_nonrelative && cn.corename[0] != '/') { +		if (need_suid_safe && cn.corename[0] != '/') {  			printk(KERN_WARNING "Pid %d(%s) can only dump core "\  				"to fully qualified path!\n",  				task_tgid_vnr(current), current->comm); @@ -641,8 +640,35 @@ void do_coredump(const siginfo_t *siginfo)  			goto fail_unlock;  		} +		/* +		 * Unlink the file if it exists unless this is a SUID +		 * binary - in that case, we're running around with root +		 * privs and don't want to unlink another user's coredump. +		 */ +		if (!need_suid_safe) { +			mm_segment_t old_fs; + +			old_fs = get_fs(); +			set_fs(KERNEL_DS); +			/* +			 * If it doesn't exist, that's fine. If there's some +			 * other problem, we'll catch it at the filp_open(). +			 */ +			(void) sys_unlink((const char __user *)cn.corename); +			set_fs(old_fs); +		} + +		/* +		 * There is a race between unlinking and creating the +		 * file, but if that causes an EEXIST here, that's +		 * fine - another process raced with us while creating +		 * the corefile, and the other process won. To userspace, +		 * what matters is that at least one of the two processes +		 * writes its coredump successfully, not which one. +		 */  		cprm.file = filp_open(cn.corename, -				 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, +				 O_CREAT | 2 | O_NOFOLLOW | +				 O_LARGEFILE | O_EXCL,  				 0600);  		if (IS_ERR(cprm.file))  			goto fail_unlock; @@ -659,11 +685,15 @@ void do_coredump(const siginfo_t *siginfo)  		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. +		 * Don't dump core if the filesystem changed owner or mode +		 * of the file during file creation. This is an issue when +		 * a process dumps core while its cwd is e.g. on a vfat +		 * filesystem.  		 */  		if (!uid_eq(inode->i_uid, current_fsuid()))  			goto close_fail; +		if ((inode->i_mode & 0677) != 0600) +			goto close_fail;  		if (!(cprm.file->f_mode & FMODE_CAN_WRITE))  			goto close_fail;  		if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))  |