Unverified Commit a6ed5691 authored by Christian Brauner's avatar Christian Brauner
Browse files

Merge patch series "coredump: further cleanups"

Christian Brauner <brauner@kernel.org> says:

Continue reworking the coredump code so it's easier to follow and modify
in the future.

* Each method is moved into a separate helper.
* The cleanup code is simplified and unified.
* Entangle the dependency between the pipe coredump rate limiting and
  the common exit path.

It's likely that there'll be more.

* patches from https://lore.kernel.org/20250612-work-coredump-massage-v1-0-315c0c34ba94@kernel.org: (24 commits)
  coredump: add coredump_skip() helper
  coredump: avoid pointless variable
  coredump: order auto cleanup variables at the top
  coredump: add coredump_cleanup()
  coredump: auto cleanup prepare_creds()
  cred: add auto cleanup method
  coredump: directly return
  coredump: auto cleanup argv
  coredump: add coredump_write()
  coredump: use a single helper for the socket
  coredump: move pipe specific file check into coredump_pipe()
  coredump: split pipe coredumping into coredump_pipe()
  coredump: move core_pipe_count to global variable
  coredump: prepare to simplify exit paths
  coredump: split file coredumping into coredump_file()
  coredump: rename do_coredump() to vfs_coredump()
  selftests/coredump: make sure invalid paths are rejected
  coredump: validate socket path in coredump_parse()
  coredump: don't allow ".." in coredump socket path
  fs: move name_contains_dotdot() to header
  ...

Link: https://lore.kernel.org/20250612-work-coredump-massage-v1-0-315c0c34ba94@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parents e04f97c8 da9029b4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -555,5 +555,5 @@ the VFS, and that can be done by calling into such as ``vfs_mkdir()`` with a
different set of credentials.  This is done in the following places:

 * ``sys_faccessat()``.
 * ``do_coredump()``.
 * ``vfs_coredump()``.
 * nfs4recover.c.
+1 −1
Original line number Diff line number Diff line
@@ -475,5 +475,5 @@ const指针上操作,因此不需要进行类型转换,但需要临时放弃
如 ``vfs_mkdir()`` 来实现。以下是一些进行此操作的位置:

 * ``sys_faccessat()``.
 * ``do_coredump()``.
 * ``vfs_coredump()``.
 * nfs4recover.c.
+11 −20
Original line number Diff line number Diff line
@@ -822,26 +822,6 @@ static void fw_log_firmware_info(const struct firmware *fw, const char *name,
{}
#endif

/*
 * Reject firmware file names with ".." path components.
 * There are drivers that construct firmware file names from device-supplied
 * strings, and we don't want some device to be able to tell us "I would like to
 * be sent my firmware from ../../../etc/shadow, please".
 *
 * Search for ".." surrounded by either '/' or start/end of string.
 *
 * This intentionally only looks at the firmware name, not at the firmware base
 * directory or at symlink contents.
 */
static bool name_contains_dotdot(const char *name)
{
	size_t name_len = strlen(name);

	return strcmp(name, "..") == 0 || strncmp(name, "../", 3) == 0 ||
	       strstr(name, "/../") != NULL ||
	       (name_len >= 3 && strcmp(name+name_len-3, "/..") == 0);
}

/* called from request_firmware() and request_firmware_work_func() */
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
@@ -862,6 +842,17 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
		goto out;
	}


	/*
	 * Reject firmware file names with ".." path components.
	 * There are drivers that construct firmware file names from
	 * device-supplied strings, and we don't want some device to be
	 * able to tell us "I would like to be sent my firmware from
	 * ../../../etc/shadow, please".
	 *
	 * This intentionally only looks at the firmware name, not at
	 * the firmware base directory or at symlink contents.
	 */
	if (name_contains_dotdot(name)) {
		dev_warn(device,
			 "Firmware load for '%s' refused, path contains '..' component\n",
+306 −248
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ static unsigned int core_sort_vma;
static char core_pattern[CORENAME_MAX_SIZE] = "core";
static int core_name_size = CORENAME_MAX_SIZE;
unsigned int core_file_note_size_limit = CORE_FILE_NOTE_SIZE_DEFAULT;
static atomic_t core_pipe_count = ATOMIC_INIT(0);

enum coredump_type_t {
	COREDUMP_FILE		= 1,
@@ -93,6 +94,8 @@ enum coredump_type_t {
struct core_name {
	char *corename;
	int used, size;
	unsigned int core_pipe_limit;
	bool core_dumped;
	enum coredump_type_t core_type;
	u64 mask;
};
@@ -225,11 +228,12 @@ static int cn_print_exe_file(struct core_name *cn, bool name_only)
	return ret;
}

/* format_corename will inspect the pattern parameter, and output a
 * name into corename, which must have space for at least
 * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
/*
 * coredump_parse will inspect the pattern parameter, and output a name
 * into corename, which must have space for at least CORENAME_MAX_SIZE
 * bytes plus one byte for the zero terminator.
 */
static int format_corename(struct core_name *cn, struct coredump_params *cprm,
static bool coredump_parse(struct core_name *cn, struct coredump_params *cprm,
			   size_t **argv, int *argc)
{
	const struct cred *cred = current_cred();
@@ -243,6 +247,8 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
		cn->mask |= COREDUMP_WAIT;
	cn->used = 0;
	cn->corename = NULL;
	cn->core_pipe_limit = 0;
	cn->core_dumped = false;
	if (*pat_ptr == '|')
		cn->core_type = COREDUMP_PIPE;
	else if (*pat_ptr == '@')
@@ -250,7 +256,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
	else
		cn->core_type = COREDUMP_FILE;
	if (expand_corename(cn, core_name_size))
		return -ENOMEM;
		return false;
	cn->corename[0] = '\0';

	switch (cn->core_type) {
@@ -258,33 +264,33 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
		int argvs = sizeof(core_pattern) / 2;
		(*argv) = kmalloc_array(argvs, sizeof(**argv), GFP_KERNEL);
		if (!(*argv))
			return -ENOMEM;
			return false;
		(*argv)[(*argc)++] = 0;
		++pat_ptr;
		if (!(*pat_ptr))
			return -ENOMEM;
			return false;
		break;
	}
	case COREDUMP_SOCK: {
		/* skip the @ */
		pat_ptr++;
		if (!(*pat_ptr))
			return -ENOMEM;
			return false;
		if (*pat_ptr == '@') {
			pat_ptr++;
			if (!(*pat_ptr))
				return -ENOMEM;
				return false;

			cn->core_type = COREDUMP_SOCK_REQ;
		}

		err = cn_printf(cn, "%s", pat_ptr);
		if (err)
			return err;
			return false;

		/* Require absolute paths. */
		if (cn->corename[0] != '/')
			return -EINVAL;
			return false;

		/*
		 * Ensure we can uses spaces to indicate additional
@@ -292,7 +298,18 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
		 */
		if (strchr(cn->corename, ' ')) {
			coredump_report_failure("Coredump socket may not %s contain spaces", cn->corename);
			return -EINVAL;
			return false;
		}

		/* Must not contain ".." in the path. */
		if (name_contains_dotdot(cn->corename)) {
			coredump_report_failure("Coredump socket may not %s contain '..' spaces", cn->corename);
			return false;
		}

		if (strlen(cn->corename) >= UNIX_PATH_MAX) {
			coredump_report_failure("Coredump socket path %s too long", cn->corename);
			return false;
		}

		/*
@@ -302,13 +319,13 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
		 * via /proc/<pid>, using the SO_PEERPIDFD to guard
		 * against pid recycling when opening /proc/<pid>.
		 */
		return 0;
		return true;
	}
	case COREDUMP_FILE:
		break;
	default:
		WARN_ON_ONCE(true);
		return -EINVAL;
		return false;
	}

	/* Repeat as long as we have more pattern to process and more output
@@ -446,7 +463,7 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
		}

		if (err)
			return err;
			return false;
	}

out:
@@ -456,9 +473,9 @@ static int format_corename(struct core_name *cn, struct coredump_params *cprm,
	 * and core_uses_pid is set, then .%pid will be appended to
	 * the filename. Do not do this for piped commands. */
	if (cn->core_type == COREDUMP_FILE && !pid_in_pattern && core_uses_pid)
		return cn_printf(cn, ".%d", task_tgid_vnr(current));
		return cn_printf(cn, ".%d", task_tgid_vnr(current)) == 0;

	return 0;
	return true;
}

static int zap_process(struct signal_struct *signal, int exit_code)
@@ -840,96 +857,41 @@ static bool coredump_sock_request(struct core_name *cn, struct coredump_params *
	cn->mask = ack.mask;
	return coredump_sock_mark(cprm->file, COREDUMP_MARK_REQACK);
}
#else
static bool coredump_sock_connect(struct core_name *cn,
				  struct coredump_params *cprm)

static bool coredump_socket(struct core_name *cn, struct coredump_params *cprm)
{
	coredump_report_failure("Core dump socket support %s disabled", cn->corename);
	if (!coredump_sock_connect(cn, cprm))
		return false;

	return coredump_sock_request(cn, cprm);
}
static bool coredump_sock_request(struct core_name *cn,
				  struct coredump_params *cprm) { return false; }
#else
static inline void coredump_sock_wait(struct file *file) { }
static inline void coredump_sock_shutdown(struct file *file) { }
static inline bool coredump_socket(struct core_name *cn, struct coredump_params *cprm) { return false; }
#endif

void do_coredump(const kernel_siginfo_t *siginfo)
/* cprm->mm_flags contains a stable snapshot of dumpability flags. */
static inline bool coredump_force_suid_safe(const struct coredump_params *cprm)
{
	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;
	size_t *argv = NULL;
	int argc = 0;
	/* 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 = {
		.siginfo = siginfo,
		.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,
		.vma_meta = NULL,
		.cpu = raw_smp_processor_id(),
	};

	audit_core_dumps(siginfo->si_signo);

	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_DUMP_ROOT) {
		/* Setuid core dump mode */
		cred->fsuid = GLOBAL_ROOT_UID;	/* Dump root private */
		need_suid_safe = true;
	}

	retval = coredump_wait(siginfo->si_signo, &core_state);
	if (retval < 0)
		goto fail_creds;

	old_cred = override_creds(cred);

	retval = format_corename(&cn, &cprm, &argv, &argc);
	if (retval < 0) {
		coredump_report_failure("format_corename failed, aborting core");
		goto fail_unlock;
	/* Require nonrelative corefile path and be extra careful. */
	return __get_dumpable(cprm->mm_flags) == SUID_DUMP_ROOT;
}

	switch (cn.core_type) {
	case COREDUMP_FILE: {
static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
			  const struct linux_binfmt *binfmt)
{
	struct mnt_idmap *idmap;
	struct inode *inode;
		int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW |
				 O_LARGEFILE | O_EXCL;
	struct file *file __free(fput) = NULL;
	int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW | O_LARGEFILE | O_EXCL;

		if (cprm.limit < binfmt->min_coredump)
			goto fail_unlock;
	if (cprm->limit < binfmt->min_coredump)
		return false;

		if (need_suid_safe && cn.corename[0] != '/') {
			coredump_report_failure(
				"this process can only dump core to a fully qualified path, skipping core dump");
			goto fail_unlock;
	if (coredump_force_suid_safe(cprm) && cn->corename[0] != '/') {
		coredump_report_failure("this process can only dump core to a fully qualified path, skipping core dump");
		return false;
	}

	/*
@@ -937,12 +899,12 @@ void do_coredump(const kernel_siginfo_t *siginfo)
	 * 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) {
	if (!coredump_force_suid_safe(cprm)) {
		/*
		 * If it doesn't exist, that's fine. If there's some
		 * other problem, we'll catch it at the filp_open().
		 */
			do_unlinkat(AT_FDCWD, getname_kernel(cn.corename));
		do_unlinkat(AT_FDCWD, getname_kernel(cn->corename));
	}

	/*
@@ -953,7 +915,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
	 * what matters is that at least one of the two processes
	 * writes its coredump successfully, not which one.
	 */
		if (need_suid_safe) {
	if (coredump_force_suid_safe(cprm)) {
		/*
		 * Using user namespaces, normal user tasks can change
		 * their current->fs->root to point to arbitrary
@@ -968,58 +930,57 @@ void do_coredump(const kernel_siginfo_t *siginfo)
		task_lock(&init_task);
		get_fs_root(init_task.fs, &root);
		task_unlock(&init_task);
			cprm.file = file_open_root(&root, cn.corename,
						   open_flags, 0600);
		file = file_open_root(&root, cn->corename, open_flags, 0600);
		path_put(&root);
	} else {
			cprm.file = filp_open(cn.corename, open_flags, 0600);
		file = filp_open(cn->corename, open_flags, 0600);
	}
		if (IS_ERR(cprm.file))
			goto fail_unlock;
	if (IS_ERR(file))
		return false;

		inode = file_inode(cprm.file);
	inode = file_inode(file);
	if (inode->i_nlink > 1)
			goto close_fail;
		if (d_unhashed(cprm.file->f_path.dentry))
			goto close_fail;
		return false;
	if (d_unhashed(file->f_path.dentry))
		return false;
	/*
	 * 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;
		return false;
	/*
	 * 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.
	 */
		idmap = file_mnt_idmap(cprm.file);
		if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode),
				    current_fsuid())) {
			coredump_report_failure("Core dump to %s aborted: "
				"cannot preserve file owner", cn.corename);
			goto close_fail;
	idmap = file_mnt_idmap(file);
	if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) {
		coredump_report_failure("Core dump to %s aborted: cannot preserve file owner", cn->corename);
		return false;
	}
	if ((inode->i_mode & 0677) != 0600) {
			coredump_report_failure("Core dump to %s aborted: "
				"cannot preserve file permissions", cn.corename);
			goto close_fail;
		coredump_report_failure("Core dump to %s aborted: cannot preserve file permissions", cn->corename);
		return false;
	}
		if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
			goto close_fail;
		if (do_truncate(idmap, cprm.file->f_path.dentry,
				0, 0, cprm.file))
			goto close_fail;
		break;
	if (!(file->f_mode & FMODE_CAN_WRITE))
		return false;
	if (do_truncate(idmap, file->f_path.dentry, 0, 0, file))
		return false;

	cprm->file = no_free_ptr(file);
	return true;
}
	case COREDUMP_PIPE: {

static bool coredump_pipe(struct core_name *cn, struct coredump_params *cprm,
			  size_t *argv, int argc)
{
	int argi;
		int dump_count;
		char **helper_argv;
	char **helper_argv __free(kfree) = NULL;
	struct subprocess_info *sub_info;

		if (cprm.limit == 1) {
	if (cprm->limit == 1) {
		/* See umh_coredump_setup() which sets RLIMIT_CORE = 1.
		 *
		 * Normally core limits are irrelevant to pipes, since
@@ -1036,48 +997,163 @@ void do_coredump(const kernel_siginfo_t *siginfo)
		 * core_pattern process dies.
		 */
		coredump_report_failure("RLIMIT_CORE is set to 1, aborting core");
			goto fail_unlock;
		return false;
	}
		cprm.limit = RLIM_INFINITY;
	cprm->limit = RLIM_INFINITY;

		dump_count = atomic_inc_return(&core_dump_count);
		if (core_pipe_limit && (core_pipe_limit < dump_count)) {
	cn->core_pipe_limit = atomic_inc_return(&core_pipe_count);
	if (core_pipe_limit && (core_pipe_limit < cn->core_pipe_limit)) {
		coredump_report_failure("over core_pipe_limit, skipping core dump");
			goto fail_dropcount;
		return false;
	}

		helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv),
					    GFP_KERNEL);
	helper_argv = kmalloc_array(argc + 1, sizeof(*helper_argv), GFP_KERNEL);
	if (!helper_argv) {
		coredump_report_failure("%s failed to allocate memory", __func__);
			goto fail_dropcount;
		return false;
	}
	for (argi = 0; argi < argc; argi++)
			helper_argv[argi] = cn.corename + argv[argi];
		helper_argv[argi] = cn->corename + argv[argi];
	helper_argv[argi] = NULL;

		retval = -ENOMEM;
		sub_info = call_usermodehelper_setup(helper_argv[0],
						helper_argv, NULL, GFP_KERNEL,
						umh_coredump_setup, NULL, &cprm);
		if (sub_info)
			retval = call_usermodehelper_exec(sub_info,
							  UMH_WAIT_EXEC);
	sub_info = call_usermodehelper_setup(helper_argv[0], helper_argv, NULL,
					     GFP_KERNEL, umh_coredump_setup,
					     NULL, cprm);
	if (!sub_info)
		return false;

		kfree(helper_argv);
		if (retval) {
			coredump_report_failure("|%s pipe failed", cn.corename);
	if (call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC)) {
		coredump_report_failure("|%s pipe failed", cn->corename);
		return false;
	}

	/*
	 * umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would
	 * have this set to NULL.
	 */
	if (!cprm->file) {
		coredump_report_failure("Core dump to |%s disabled", cn->corename);
		return false;
	}

	return true;
}

static bool coredump_write(struct core_name *cn,
			  struct coredump_params *cprm,
			  struct linux_binfmt *binfmt)
{

	if (dump_interrupted())
		return true;

	if (!dump_vma_snapshot(cprm))
		return false;

	file_start_write(cprm->file);
	cn->core_dumped = binfmt->core_dump(cprm);
	/*
	 * Ensures that file size is big enough to contain the current
	 * file postion. This prevents gdb from complaining about
	 * a truncated file if the last "write" to the file was
	 * dump_skip.
	 */
	if (cprm->to_skip) {
		cprm->to_skip--;
		dump_emit(cprm, "", 1);
	}
	file_end_write(cprm->file);
	free_vma_snapshot(cprm);
	return true;
}

static void coredump_cleanup(struct core_name *cn, struct coredump_params *cprm)
{
	if (cprm->file)
		filp_close(cprm->file, NULL);
	if (cn->core_pipe_limit) {
		VFS_WARN_ON_ONCE(cn->core_type != COREDUMP_PIPE);
		atomic_dec(&core_pipe_count);
	}
	kfree(cn->corename);
	coredump_finish(cn->core_dumped);
}

static inline bool coredump_skip(const struct coredump_params *cprm,
				 const struct linux_binfmt *binfmt)
{
	if (!binfmt)
		return true;
	if (!binfmt->core_dump)
		return true;
	if (!__get_dumpable(cprm->mm_flags))
		return true;
	return false;
}

void vfs_coredump(const kernel_siginfo_t *siginfo)
{
	struct cred *cred __free(put_cred) = NULL;
	size_t *argv __free(kfree) = NULL;
	struct core_state core_state;
	struct core_name cn;
	struct mm_struct *mm = current->mm;
	struct linux_binfmt *binfmt = mm->binfmt;
	const struct cred *old_cred;
	int argc = 0;
	struct coredump_params cprm = {
		.siginfo = siginfo,
		.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,
		.vma_meta = NULL,
		.cpu = raw_smp_processor_id(),
	};

	audit_core_dumps(siginfo->si_signo);

	if (coredump_skip(&cprm, binfmt))
		return;

	cred = prepare_creds();
	if (!cred)
		return;
	/*
	 * 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 (coredump_force_suid_safe(&cprm))
		cred->fsuid = GLOBAL_ROOT_UID;

	if (coredump_wait(siginfo->si_signo, &core_state) < 0)
		return;

	old_cred = override_creds(cred);

	if (!coredump_parse(&cn, &cprm, &argv, &argc)) {
		coredump_report_failure("format_corename failed, aborting core");
		goto close_fail;
	}

	switch (cn.core_type) {
	case COREDUMP_FILE:
		if (!coredump_file(&cn, &cprm, binfmt))
			goto close_fail;
		break;
	case COREDUMP_PIPE:
		if (!coredump_pipe(&cn, &cprm, argv, argc))
			goto close_fail;
		break;
	}
	case COREDUMP_SOCK_REQ:
		fallthrough;
	case COREDUMP_SOCK:
		if (!coredump_sock_connect(&cn, &cprm))
			goto close_fail;

		if (!coredump_sock_request(&cn, &cprm))
		if (!coredump_socket(&cn, &cprm))
			goto close_fail;
		break;
	default:
@@ -1091,43 +1167,17 @@ void do_coredump(const kernel_siginfo_t *siginfo)

	/* get us an unshared descriptor table; almost always a no-op */
	/* The cell spufs coredump code reads the file descriptor tables */
	retval = unshare_files();
	if (retval)
	if (unshare_files())
		goto close_fail;

	if ((cn.mask & COREDUMP_KERNEL) && !dump_interrupted()) {
		/*
		 * umh disabled with CONFIG_STATIC_USERMODEHELPER_PATH="" would
		 * have this set to NULL.
		 */
		if (!cprm.file) {
			coredump_report_failure("Core dump to |%s disabled", cn.corename);
			goto close_fail;
		}
		if (!dump_vma_snapshot(&cprm))
	if ((cn.mask & COREDUMP_KERNEL) && !coredump_write(&cn, &cprm, binfmt))
		goto close_fail;

		file_start_write(cprm.file);
		core_dumped = binfmt->core_dump(&cprm);
		/*
		 * Ensures that file size is big enough to contain the current
		 * file postion. This prevents gdb from complaining about
		 * a truncated file if the last "write" to the file was
		 * dump_skip.
		 */
		if (cprm.to_skip) {
			cprm.to_skip--;
			dump_emit(&cprm, "", 1);
		}
		file_end_write(cprm.file);
		free_vma_snapshot(&cprm);
	}

	coredump_sock_shutdown(cprm.file);

	/* Let the parent know that a coredump was generated. */
	if (cn.mask & COREDUMP_USERSPACE)
		core_dumped = true;
		cn.core_dumped = true;

	/*
	 * When core_pipe_limit is set we wait for the coredump server
@@ -1150,19 +1200,8 @@ void do_coredump(const kernel_siginfo_t *siginfo)
	}

close_fail:
	if (cprm.file)
		filp_close(cprm.file, NULL);
fail_dropcount:
	if (cn.core_type == COREDUMP_PIPE)
		atomic_dec(&core_dump_count);
fail_unlock:
	kfree(argv);
	kfree(cn.corename);
	coredump_finish(core_dumped);
	coredump_cleanup(&cn, &cprm);
	revert_creds(old_cred);
fail_creds:
	put_cred(cred);
fail:
	return;
}

@@ -1388,6 +1427,8 @@ void validate_coredump_safety(void)

static inline bool check_coredump_socket(void)
{
	const char *p;

	if (core_pattern[0] != '@')
		return true;

@@ -1399,8 +1440,25 @@ static inline bool check_coredump_socket(void)
	if (current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns)
		return false;

	/* Must be an absolute path or the socket request. */
	if (*(core_pattern + 1) != '/' && *(core_pattern + 1) != '@')
	/* Must be an absolute path... */
	if (core_pattern[1] != '/') {
		/* ... or the socket request protocol... */
		if (core_pattern[1] != '@')
			return false;
		/* ... and if so must be an absolute path. */
		if (core_pattern[2] != '/')
			return false;
		p = &core_pattern[2];
	} else {
		p = &core_pattern[1];
	}

	/* The path obviously cannot exceed UNIX_PATH_MAX. */
	if (strlen(p) >= UNIX_PATH_MAX)
		return false;

	/* Must not contain ".." in the path. */
	if (name_contains_dotdot(core_pattern))
		return false;

	return true;
+2 −2
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr);
extern int dump_align(struct coredump_params *cprm, int align);
int dump_user_range(struct coredump_params *cprm, unsigned long start,
		    unsigned long len);
extern void do_coredump(const kernel_siginfo_t *siginfo);
extern void vfs_coredump(const kernel_siginfo_t *siginfo);

/*
 * Logging for the coredump code, ratelimited.
@@ -63,7 +63,7 @@ extern void do_coredump(const kernel_siginfo_t *siginfo);
#define coredump_report_failure(fmt, ...) __COREDUMP_PRINTK(KERN_WARNING, fmt, ##__VA_ARGS__)

#else
static inline void do_coredump(const kernel_siginfo_t *siginfo) {}
static inline void vfs_coredump(const kernel_siginfo_t *siginfo) {}

#define coredump_report(...)
#define coredump_report_failure(...)
Loading