fs/coredump: Enable dynamic configuration of max file note size
authorAllen Pais <apais@linux.microsoft.com>
Mon, 6 May 2024 19:37:00 +0000 (19:37 +0000)
committerKees Cook <keescook@chromium.org>
Wed, 8 May 2024 16:53:00 +0000 (09:53 -0700)
Introduce the capability to dynamically configure the maximum file
note size for ELF core dumps via sysctl.

Why is this being done?
We have observed that during a crash when there are more than 65k mmaps
in memory, the existing fixed limit on the size of the ELF notes section
becomes a bottleneck. The notes section quickly reaches its capacity,
leading to incomplete memory segment information in the resulting coredump.
This truncation compromises the utility of the coredumps, as crucial
information about the memory state at the time of the crash might be
omitted.

This enhancement removes the previous static limit of 4MB, allowing
system administrators to adjust the size based on system-specific
requirements or constraints.

Eg:
$ sysctl -a | grep core_file_note_size_limit
kernel.core_file_note_size_limit = 4194304

$ sysctl -n kernel.core_file_note_size_limit
4194304

$echo 519304 > /proc/sys/kernel/core_file_note_size_limit

$sysctl -n kernel.core_file_note_size_limit
519304

Attempting to write beyond the ceiling value of 16MB
$echo 17194304 > /proc/sys/kernel/core_file_note_size_limit
bash: echo: write error: Invalid argument

Signed-off-by: Vijay Nag <nagvijay@microsoft.com>
Signed-off-by: Allen Pais <apais@linux.microsoft.com>
Link: https://lore.kernel.org/r/20240506193700.7884-1-apais@linux.microsoft.com
Signed-off-by: Kees Cook <keescook@chromium.org>
fs/binfmt_elf.c
fs/coredump.c
include/linux/coredump.h

index 7862962f7a8597563c0f5c3aba7b7ddd778e03d5..b5a25ee49eea4ca9ffe148e2380c0e0ee14cbdf2 100644 (file)
@@ -1567,7 +1567,6 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
        fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata);
 }
 
-#define MAX_FILE_NOTE_SIZE (4*1024*1024)
 /*
  * Format of NT_FILE note:
  *
@@ -1595,8 +1594,12 @@ static int fill_files_note(struct memelfnote *note, struct coredump_params *cprm
 
        names_ofs = (2 + 3 * count) * sizeof(data[0]);
  alloc:
-       if (size >= MAX_FILE_NOTE_SIZE) /* paranoia check */
+       /* paranoia check */
+       if (size >= core_file_note_size_limit) {
+               pr_warn_once("coredump Note size too large: %u (does kernel.core_file_note_size_limit sysctl need adjustment?\n",
+                             size);
                return -EINVAL;
+       }
        size = round_up(size, PAGE_SIZE);
        /*
         * "size" can be 0 here legitimately.
index be6403b4b14b6a26e611398f0903244d1af96343..317065e3eb9bb0969ea73fbef46fd8d345ccfe2c 100644 (file)
 static bool dump_vma_snapshot(struct coredump_params *cprm);
 static void free_vma_snapshot(struct coredump_params *cprm);
 
+#define CORE_FILE_NOTE_SIZE_DEFAULT (4*1024*1024)
+/* Define a reasonable max cap */
+#define CORE_FILE_NOTE_SIZE_MAX (16*1024*1024)
+
 static int core_uses_pid;
 static unsigned int core_pipe_limit;
 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;
 
 struct core_name {
        char *corename;
@@ -998,6 +1003,9 @@ static int proc_dostring_coredump(struct ctl_table *table, int write,
        return error;
 }
 
+static const unsigned int core_file_note_size_min = CORE_FILE_NOTE_SIZE_DEFAULT;
+static const unsigned int core_file_note_size_max = CORE_FILE_NOTE_SIZE_MAX;
+
 static struct ctl_table coredump_sysctls[] = {
        {
                .procname       = "core_uses_pid",
@@ -1020,6 +1028,15 @@ static struct ctl_table coredump_sysctls[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "core_file_note_size_limit",
+               .data           = &core_file_note_size_limit,
+               .maxlen         = sizeof(unsigned int),
+               .mode           = 0644,
+               .proc_handler   = proc_douintvec_minmax,
+               .extra1         = (unsigned int *)&core_file_note_size_min,
+               .extra2         = (unsigned int *)&core_file_note_size_max,
+       },
 };
 
 static int __init init_fs_coredump_sysctls(void)
index d3eba4360150875d3a8f4890b25e985486f9eaf8..0904ba010341aa753085005d9855ca26caa5f92d 100644 (file)
@@ -30,6 +30,8 @@ struct coredump_params {
        struct core_vma_metadata *vma_meta;
 };
 
+extern unsigned int core_file_note_size_limit;
+
 /*
  * These are the only things you should do on a core-file: use only these
  * functions to write out all the necessary info.