diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index 271d8ab1e3..d5a8046676 100644
--- a/docs/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -114,6 +114,7 @@ All execution-related settings are available for transient units.
✓ SupplementaryGroups=
✓ Nice=
✓ OOMScoreAdjust=
+✓ CoredumpFilter=
✓ IOSchedulingClass=
✓ IOSchedulingPriority=
✓ CPUSchedulingPolicy=
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 79a2c744c6..073d331e6d 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -656,6 +656,32 @@ CapabilityBoundingSet=~CAP_B CAP_C
to 0022.
+
+ CoredumpFilter=
+
+ Controls which types of memory mappings will be saved if the process dumps core
+ (using the /proc/pid/coredump_filter file). Takes a
+ whitespace-separated combination of mapping type names or numbers (with the default base 16). Mapping
+ type names are private-anonymous, shared-anonymous,
+ private-file-backed, shared-file-backed,
+ elf-headers, private-huge,
+ shared-huge, private-dax, shared-dax,
+ and the special values all (all types) and default (the
+ kernel default of private-anonymous
+ shared-anonymous elf-headers
+ private-huge). See
+ core5 for the
+ meaning of the mapping types. When specified multiple times, all specified masks are ORed. When not
+ set, or if the empty value is assigned, the inherited value is not changed.
+
+
+ Add DAX pages to the dump filter
+
+ CoredumpFilter=default private-dax shared-dax
+
+
+
+
KeyringMode=
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index e0094b0f37..8de5cd5c56 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -395,7 +395,7 @@ int safe_atoi(const char *s, int *ret_i) {
return 0;
}
-int safe_atollu(const char *s, long long unsigned *ret_llu) {
+int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) {
char *x = NULL;
unsigned long long l;
@@ -404,7 +404,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
s += strspn(s, WHITESPACE);
errno = 0;
- l = strtoull(s, &x, 0);
+ l = strtoull(s, &x, base);
if (errno > 0)
return -errno;
if (!x || x == s || *x != 0)
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 36d76ba576..970bdefbf0 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -28,7 +28,6 @@ static inline int safe_atou(const char *s, unsigned *ret_u) {
}
int safe_atoi(const char *s, int *ret_i);
-int safe_atollu(const char *s, unsigned long long *ret_u);
int safe_atolli(const char *s, long long int *ret_i);
int safe_atou8(const char *s, uint8_t *ret);
@@ -59,6 +58,12 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) {
return safe_atoi(s, (int*) ret_i);
}
+int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu);
+
+static inline int safe_atollu(const char *s, long long unsigned *ret_llu) {
+ return safe_atollu_full(s, 0, ret_llu);
+}
+
static inline int safe_atou64(const char *s, uint64_t *ret_u) {
assert_cc(sizeof(uint64_t) == sizeof(unsigned long long));
return safe_atollu(s, (unsigned long long*) ret_u);
@@ -69,6 +74,11 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) {
return safe_atolli(s, (long long int*) ret_i);
}
+static inline int safe_atoux64(const char *s, uint64_t *ret) {
+ assert_cc(sizeof(int64_t) == sizeof(long long unsigned));
+ return safe_atollu_full(s, 16, (long long unsigned*) ret);
+}
+
#if LONG_MAX == INT_MAX
static inline int safe_atolu(const char *s, unsigned long *ret_u) {
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 5696a60ba8..29b55c62eb 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -102,6 +102,7 @@ static int property_get_oom_score_adjust(
ExecContext *c = userdata;
int32_t n;
+ int r;
assert(bus);
assert(reply);
@@ -113,13 +114,55 @@ static int property_get_oom_score_adjust(
_cleanup_free_ char *t = NULL;
n = 0;
- if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0)
- safe_atoi32(t, &n);
+ r = read_one_line_file("/proc/self/oom_score_adj", &t);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
+ else {
+ r = safe_atoi32(t, &n);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/oom_score_adj, ignoring: %m", t);
+ }
}
return sd_bus_message_append(reply, "i", n);
}
+static int property_get_coredump_filter(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ uint64_t n;
+ int r;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ if (c->coredump_filter_set)
+ n = c->coredump_filter;
+ else {
+ _cleanup_free_ char *t = NULL;
+
+ n = COREDUMP_FILTER_MASK_DEFAULT;
+ r = read_one_line_file("/proc/self/coredump_filter", &t);
+ if (r < 0)
+ log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
+ else {
+ r = safe_atoux64(t, &n);
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
+ }
+ }
+
+ return sd_bus_message_append(reply, "t", n);
+}
+
static int property_get_nice(
sd_bus *bus,
const char *path,
@@ -747,6 +790,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IOSchedulingClass", "i", property_get_ioprio_class, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IOSchedulingPriority", "i", property_get_ioprio_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2190,6 +2234,21 @@ int bus_exec_context_set_transient_property(
return 1;
+ } else if (streq(name, "CoredumpFilter")) {
+ uint64_t f;
+
+ r = sd_bus_message_read(message, "t", &f);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ c->coredump_filter = f;
+ c->coredump_filter_set = true;
+ unit_write_settingf(u, flags, name, "CoredumpFilter=0x%"PRIx64, f);
+ }
+
+ return 1;
+
} else if (streq(name, "EnvironmentFiles")) {
_cleanup_free_ char *joined = NULL;
diff --git a/src/core/execute.c b/src/core/execute.c
index 8e1e77b4b2..c59acad4ce 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -3323,6 +3323,14 @@ static int exec_child(
}
}
+ if (context->coredump_filter_set) {
+ r = set_coredump_filter(context->coredump_filter);
+ if (ERRNO_IS_PRIVILEGE(r))
+ log_unit_debug_errno(unit, r, "Failed to adjust coredump_filter, ignoring: %m");
+ else if (r < 0)
+ return log_unit_error_errno(unit, r, "Failed to adjust coredump_filter: %m");
+ }
+
if (context->nice_set) {
r = setpriority_closest(context->nice);
if (r < 0)
@@ -4614,6 +4622,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
"%sOOMScoreAdjust: %i\n",
prefix, c->oom_score_adjust);
+ if (c->coredump_filter_set)
+ fprintf(f,
+ "%sCoredumpFilter: 0x%"PRIx64"\n",
+ prefix, c->coredump_filter);
+
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i]) {
fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
diff --git a/src/core/execute.h b/src/core/execute.h
index 4baf5b1a40..f96a2915eb 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -14,6 +14,7 @@ typedef struct Manager Manager;
#include
#include "cgroup-util.h"
+#include "coredump-util.h"
#include "cpu-set-util.h"
#include "exec-util.h"
#include "fdset.h"
@@ -161,6 +162,7 @@ struct ExecContext {
bool working_directory_home:1;
bool oom_score_adjust_set:1;
+ bool coredump_filter_set:1;
bool nice_set:1;
bool ioprio_set:1;
bool cpu_sched_set:1;
@@ -179,6 +181,7 @@ struct ExecContext {
int ioprio;
int cpu_sched_policy;
int cpu_sched_priority;
+ uint64_t coredump_filter;
CPUSet cpu_set;
NUMAPolicy numa_policy;
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 69abdb65ba..165b9ca9c1 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -28,6 +28,7 @@ $1.Group, config_parse_user_group_compat, 0,
$1.SupplementaryGroups, config_parse_user_group_strv_compat, 0, offsetof($1, exec_context.supplementary_groups)
$1.Nice, config_parse_exec_nice, 0, offsetof($1, exec_context)
$1.OOMScoreAdjust, config_parse_exec_oom_score_adjust, 0, offsetof($1, exec_context)
+$1.CoredumpFilter, config_parse_exec_coredump_filter, 0, offsetof($1, exec_context)
$1.IOSchedulingClass, config_parse_exec_io_class, 0, offsetof($1, exec_context)
$1.IOSchedulingPriority, config_parse_exec_io_priority, 0, offsetof($1, exec_context)
$1.CPUSchedulingPolicy, config_parse_exec_cpu_sched_policy, 0, offsetof($1, exec_context)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 646364eb89..b4d6c522e4 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -592,6 +592,45 @@ int config_parse_exec_oom_score_adjust(
return 0;
}
+int config_parse_exec_coredump_filter(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecContext *c = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ c->coredump_filter = 0;
+ c->coredump_filter_set = false;
+ return 0;
+ }
+
+ uint64_t f;
+ r = coredump_filter_mask_from_string(rvalue, &f);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ c->coredump_filter |= f;
+ c->oom_score_adjust_set = true;
+ return 0;
+}
+
int config_parse_exec(
const char *unit,
const char *filename,
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index b6b46b2449..bc72ef7745 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -26,6 +26,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_socket_protocol);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bind);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_nice);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_oom_score_adjust);
+CONFIG_PARSER_PROTOTYPE(config_parse_exec_coredump_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_exec);
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout);
CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort);
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index a30876c1a1..463a0ddb71 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -8,6 +8,7 @@
#include "cgroup-setup.h"
#include "cgroup-util.h"
#include "condition.h"
+#include "coredump-util.h"
#include "cpu-set-util.h"
#include "escape.h"
#include "exec-util.h"
@@ -119,6 +120,7 @@ DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64);
DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode);
DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou);
DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
int r;
@@ -898,6 +900,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"OOMScoreAdjust"))
return bus_append_safe_atoi(m, field, eq);
+ if (streq(field, "CoredumpFilter"))
+ return bus_append_coredump_filter_mask_from_string(m, field, eq);
+
if (streq(field, "Nice"))
return bus_append_parse_nice(m, field, eq);
diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c
index 05762c68a3..d107a06ce3 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -374,6 +374,12 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b
(void) format_timespan(timespan, sizeof(timespan), u, 0);
bus_print_property_value(name, expected_value, value, timespan);
+ } else if (streq(name, "CoredumpFilter")) {
+ char buf[STRLEN("0xFFFFFFFF")];
+
+ xsprintf(buf, "0x%"PRIx64, u);
+ bus_print_property_value(name, expected_value, value, buf);
+
} else if (streq(name, "RestrictNamespaces")) {
_cleanup_free_ char *s = NULL;
const char *result;
diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c
new file mode 100644
index 0000000000..67897414bc
--- /dev/null
+++ b/src/shared/coredump-util.c
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "coredump-util.h"
+#include "extract-word.h"
+#include "fileio.h"
+#include "string-table.h"
+
+static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = {
+ [COREDUMP_FILTER_PRIVATE_ANONYMOUS] = "private-anonymous",
+ [COREDUMP_FILTER_SHARED_ANONYMOUS] = "shared-anonymous",
+ [COREDUMP_FILTER_PRIVATE_FILE_BACKED] = "private-file-backed",
+ [COREDUMP_FILTER_SHARED_FILE_BACKED] = "shared-file-backed",
+ [COREDUMP_FILTER_ELF_HEADERS] = "elf-headers",
+ [COREDUMP_FILTER_PRIVATE_HUGE] = "private-huge",
+ [COREDUMP_FILTER_SHARED_HUGE] = "shared-huge",
+ [COREDUMP_FILTER_PRIVATE_DAX] = "private-dax",
+ [COREDUMP_FILTER_SHARED_DAX] = "shared-dax",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(coredump_filter, CoredumpFilter);
+
+int coredump_filter_mask_from_string(const char *s, uint64_t *ret) {
+ uint64_t m = 0;
+
+ assert(s);
+ assert(ret);
+
+ for (;;) {
+ _cleanup_free_ char *n = NULL;
+ CoredumpFilter v;
+ int r;
+
+ r = extract_first_word(&s, &n, NULL, 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (streq(n, "default")) {
+ m |= COREDUMP_FILTER_MASK_DEFAULT;
+ continue;
+ }
+
+ if (streq(n, "all")) {
+ m = UINT64_MAX;
+ continue;
+ }
+
+ v = coredump_filter_from_string(n);
+ if (v >= 0) {
+ m |= 1u << v;
+ continue;
+ }
+
+ uint64_t x;
+ r = safe_atoux64(n, &x);
+ if (r < 0)
+ return r;
+
+ m |= x;
+ }
+
+ *ret = m;
+ return 0;
+}
+
+int set_coredump_filter(uint64_t value) {
+ char t[STRLEN("0xFFFFFFFF")];
+
+ sprintf(t, "0x%"PRIx64, value);
+
+ return write_string_file("/proc/self/coredump_filter", t,
+ WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
+}
diff --git a/src/shared/coredump-util.h b/src/shared/coredump-util.h
new file mode 100644
index 0000000000..ff2e511bf1
--- /dev/null
+++ b/src/shared/coredump-util.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+
+typedef enum CoredumpFilter {
+ COREDUMP_FILTER_PRIVATE_ANONYMOUS = 0,
+ COREDUMP_FILTER_SHARED_ANONYMOUS,
+ COREDUMP_FILTER_PRIVATE_FILE_BACKED,
+ COREDUMP_FILTER_SHARED_FILE_BACKED,
+ COREDUMP_FILTER_ELF_HEADERS,
+ COREDUMP_FILTER_PRIVATE_HUGE,
+ COREDUMP_FILTER_SHARED_HUGE,
+ COREDUMP_FILTER_PRIVATE_DAX,
+ COREDUMP_FILTER_SHARED_DAX,
+ _COREDUMP_FILTER_MAX,
+ _COREDUMP_FILTER_INVALID = -1,
+} CoredumpFilter;
+
+#define COREDUMP_FILTER_MASK_DEFAULT (1u << COREDUMP_FILTER_PRIVATE_ANONYMOUS | \
+ 1u << COREDUMP_FILTER_SHARED_ANONYMOUS | \
+ 1u << COREDUMP_FILTER_ELF_HEADERS | \
+ 1u << COREDUMP_FILTER_PRIVATE_HUGE)
+
+const char* coredump_filter_to_string(CoredumpFilter i) _const_;
+CoredumpFilter coredump_filter_from_string(const char *s) _pure_;
+int coredump_filter_mask_from_string(const char *s, uint64_t *ret);
+
+int set_coredump_filter(uint64_t value);
diff --git a/src/shared/meson.build b/src/shared/meson.build
index 45a723f363..d1832a1f53 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -51,6 +51,8 @@ shared_sources = files('''
condition.h
conf-parser.c
conf-parser.h
+ coredump-util.c
+ coredump-util.h
cpu-set-util.c
cpu-set-util.h
crypt-util.c
diff --git a/src/test/meson.build b/src/test/meson.build
index 6297875c0d..a674d6cfe9 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -588,6 +588,10 @@ tests += [
[],
[]],
+ [['src/test/test-coredump-util.c'],
+ [],
+ []],
+
[['src/test/test-daemon.c'],
[],
[]],
diff --git a/src/test/test-coredump-util.c b/src/test/test-coredump-util.c
new file mode 100644
index 0000000000..14a78007e3
--- /dev/null
+++ b/src/test/test-coredump-util.c
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "coredump-util.h"
+#include "macro.h"
+#include "tests.h"
+
+static void test_coredump_filter_to_from_string(void) {
+ log_info("/* %s */", __func__);
+
+ for (CoredumpFilter i = 0; i < _COREDUMP_FILTER_MAX; i++) {
+ const char *n;
+
+ assert_se(n = coredump_filter_to_string(i));
+ log_info("0x%x\t%s", 1<