Skip to content

Commit 7bbb05d

Browse files
committed
coredump: split file coredumping into coredump_file()
* Move that whole mess into a separate helper instead of having all that hanging around in vfs_coredump() directly. * Stop using that need_suid_safe variable and add an inline helper that clearly communicates what's going on everywhere consistently. The mm flag snapshot is stable and can't change so nothing's gained with that boolean. * Only setup cprm->file once everything else succeeded, using RAII for the coredump file before. That allows to don't care to what goto label we jump in vfs_coredump(). Link: https://lore.kernel.org/20250612-work-coredump-massage-v1-10-315c0c34ba94@kernel.org Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 70e3ee3 commit 7bbb05d

1 file changed

Lines changed: 106 additions & 101 deletions

File tree

fs/coredump.c

Lines changed: 106 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,108 @@ static inline void coredump_sock_wait(struct file *file) { }
865865
static inline void coredump_sock_shutdown(struct file *file) { }
866866
#endif
867867

868+
/* cprm->mm_flags contains a stable snapshot of dumpability flags. */
869+
static inline bool coredump_force_suid_safe(const struct coredump_params *cprm)
870+
{
871+
/* Require nonrelative corefile path and be extra careful. */
872+
return __get_dumpable(cprm->mm_flags) == SUID_DUMP_ROOT;
873+
}
874+
875+
static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
876+
const struct linux_binfmt *binfmt)
877+
{
878+
struct mnt_idmap *idmap;
879+
struct inode *inode;
880+
struct file *file __free(fput) = NULL;
881+
int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW | O_LARGEFILE | O_EXCL;
882+
883+
if (cprm->limit < binfmt->min_coredump)
884+
return false;
885+
886+
if (coredump_force_suid_safe(cprm) && cn->corename[0] != '/') {
887+
coredump_report_failure("this process can only dump core to a fully qualified path, skipping core dump");
888+
return false;
889+
}
890+
891+
/*
892+
* Unlink the file if it exists unless this is a SUID
893+
* binary - in that case, we're running around with root
894+
* privs and don't want to unlink another user's coredump.
895+
*/
896+
if (!coredump_force_suid_safe(cprm)) {
897+
/*
898+
* If it doesn't exist, that's fine. If there's some
899+
* other problem, we'll catch it at the filp_open().
900+
*/
901+
do_unlinkat(AT_FDCWD, getname_kernel(cn->corename));
902+
}
903+
904+
/*
905+
* There is a race between unlinking and creating the
906+
* file, but if that causes an EEXIST here, that's
907+
* fine - another process raced with us while creating
908+
* the corefile, and the other process won. To userspace,
909+
* what matters is that at least one of the two processes
910+
* writes its coredump successfully, not which one.
911+
*/
912+
if (coredump_force_suid_safe(cprm)) {
913+
/*
914+
* Using user namespaces, normal user tasks can change
915+
* their current->fs->root to point to arbitrary
916+
* directories. Since the intention of the "only dump
917+
* with a fully qualified path" rule is to control where
918+
* coredumps may be placed using root privileges,
919+
* current->fs->root must not be used. Instead, use the
920+
* root directory of init_task.
921+
*/
922+
struct path root;
923+
924+
task_lock(&init_task);
925+
get_fs_root(init_task.fs, &root);
926+
task_unlock(&init_task);
927+
file = file_open_root(&root, cn->corename, open_flags, 0600);
928+
path_put(&root);
929+
} else {
930+
file = filp_open(cn->corename, open_flags, 0600);
931+
}
932+
if (IS_ERR(file))
933+
return false;
934+
935+
inode = file_inode(file);
936+
if (inode->i_nlink > 1)
937+
return false;
938+
if (d_unhashed(file->f_path.dentry))
939+
return false;
940+
/*
941+
* AK: actually i see no reason to not allow this for named
942+
* pipes etc, but keep the previous behaviour for now.
943+
*/
944+
if (!S_ISREG(inode->i_mode))
945+
return false;
946+
/*
947+
* Don't dump core if the filesystem changed owner or mode
948+
* of the file during file creation. This is an issue when
949+
* a process dumps core while its cwd is e.g. on a vfat
950+
* filesystem.
951+
*/
952+
idmap = file_mnt_idmap(file);
953+
if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) {
954+
coredump_report_failure("Core dump to %s aborted: cannot preserve file owner", cn->corename);
955+
return false;
956+
}
957+
if ((inode->i_mode & 0677) != 0600) {
958+
coredump_report_failure("Core dump to %s aborted: cannot preserve file permissions", cn->corename);
959+
return false;
960+
}
961+
if (!(file->f_mode & FMODE_CAN_WRITE))
962+
return false;
963+
if (do_truncate(idmap, file->f_path.dentry, 0, 0, file))
964+
return false;
965+
966+
cprm->file = no_free_ptr(file);
967+
return true;
968+
}
969+
868970
void vfs_coredump(const kernel_siginfo_t *siginfo)
869971
{
870972
struct core_state core_state;
@@ -876,8 +978,6 @@ void vfs_coredump(const kernel_siginfo_t *siginfo)
876978
int retval = 0;
877979
size_t *argv = NULL;
878980
int argc = 0;
879-
/* require nonrelative corefile path and be extra careful */
880-
bool need_suid_safe = false;
881981
bool core_dumped = false;
882982
static atomic_t core_dump_count = ATOMIC_INIT(0);
883983
struct coredump_params cprm = {
@@ -910,11 +1010,8 @@ void vfs_coredump(const kernel_siginfo_t *siginfo)
9101010
* so we dump it as root in mode 2, and only into a controlled
9111011
* environment (pipe handler or fully qualified path).
9121012
*/
913-
if (__get_dumpable(cprm.mm_flags) == SUID_DUMP_ROOT) {
914-
/* Setuid core dump mode */
915-
cred->fsuid = GLOBAL_ROOT_UID; /* Dump root private */
916-
need_suid_safe = true;
917-
}
1013+
if (coredump_force_suid_safe(&cprm))
1014+
cred->fsuid = GLOBAL_ROOT_UID;
9181015

9191016
retval = coredump_wait(siginfo->si_signo, &core_state);
9201017
if (retval < 0)
@@ -928,102 +1025,10 @@ void vfs_coredump(const kernel_siginfo_t *siginfo)
9281025
}
9291026

9301027
switch (cn.core_type) {
931-
case COREDUMP_FILE: {
932-
struct mnt_idmap *idmap;
933-
struct inode *inode;
934-
int open_flags = O_CREAT | O_WRONLY | O_NOFOLLOW |
935-
O_LARGEFILE | O_EXCL;
936-
937-
if (cprm.limit < binfmt->min_coredump)
938-
goto fail_unlock;
939-
940-
if (need_suid_safe && cn.corename[0] != '/') {
941-
coredump_report_failure(
942-
"this process can only dump core to a fully qualified path, skipping core dump");
943-
goto fail_unlock;
944-
}
945-
946-
/*
947-
* Unlink the file if it exists unless this is a SUID
948-
* binary - in that case, we're running around with root
949-
* privs and don't want to unlink another user's coredump.
950-
*/
951-
if (!need_suid_safe) {
952-
/*
953-
* If it doesn't exist, that's fine. If there's some
954-
* other problem, we'll catch it at the filp_open().
955-
*/
956-
do_unlinkat(AT_FDCWD, getname_kernel(cn.corename));
957-
}
958-
959-
/*
960-
* There is a race between unlinking and creating the
961-
* file, but if that causes an EEXIST here, that's
962-
* fine - another process raced with us while creating
963-
* the corefile, and the other process won. To userspace,
964-
* what matters is that at least one of the two processes
965-
* writes its coredump successfully, not which one.
966-
*/
967-
if (need_suid_safe) {
968-
/*
969-
* Using user namespaces, normal user tasks can change
970-
* their current->fs->root to point to arbitrary
971-
* directories. Since the intention of the "only dump
972-
* with a fully qualified path" rule is to control where
973-
* coredumps may be placed using root privileges,
974-
* current->fs->root must not be used. Instead, use the
975-
* root directory of init_task.
976-
*/
977-
struct path root;
978-
979-
task_lock(&init_task);
980-
get_fs_root(init_task.fs, &root);
981-
task_unlock(&init_task);
982-
cprm.file = file_open_root(&root, cn.corename,
983-
open_flags, 0600);
984-
path_put(&root);
985-
} else {
986-
cprm.file = filp_open(cn.corename, open_flags, 0600);
987-
}
988-
if (IS_ERR(cprm.file))
989-
goto fail_unlock;
990-
991-
inode = file_inode(cprm.file);
992-
if (inode->i_nlink > 1)
993-
goto close_fail;
994-
if (d_unhashed(cprm.file->f_path.dentry))
995-
goto close_fail;
996-
/*
997-
* AK: actually i see no reason to not allow this for named
998-
* pipes etc, but keep the previous behaviour for now.
999-
*/
1000-
if (!S_ISREG(inode->i_mode))
1001-
goto close_fail;
1002-
/*
1003-
* Don't dump core if the filesystem changed owner or mode
1004-
* of the file during file creation. This is an issue when
1005-
* a process dumps core while its cwd is e.g. on a vfat
1006-
* filesystem.
1007-
*/
1008-
idmap = file_mnt_idmap(cprm.file);
1009-
if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode),
1010-
current_fsuid())) {
1011-
coredump_report_failure("Core dump to %s aborted: "
1012-
"cannot preserve file owner", cn.corename);
1013-
goto close_fail;
1014-
}
1015-
if ((inode->i_mode & 0677) != 0600) {
1016-
coredump_report_failure("Core dump to %s aborted: "
1017-
"cannot preserve file permissions", cn.corename);
1018-
goto close_fail;
1019-
}
1020-
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
1021-
goto close_fail;
1022-
if (do_truncate(idmap, cprm.file->f_path.dentry,
1023-
0, 0, cprm.file))
1028+
case COREDUMP_FILE:
1029+
if (!coredump_file(&cn, &cprm, binfmt))
10241030
goto close_fail;
10251031
break;
1026-
}
10271032
case COREDUMP_PIPE: {
10281033
int argi;
10291034
int dump_count;

0 commit comments

Comments
 (0)