Skip to content

Commit 7fc7aa5

Browse files
keszybzbluca
authored andcommitted
coredump: use %d in kernel core pattern
The kernel provides %d which is documented as "dump mode—same as value returned by prctl(2) PR_GET_DUMPABLE". We already query /proc/pid/auxv for this information, but unfortunately this check is subject to a race, because the crashed process may be replaced by an attacker before we read this data, for example replacing a SUID process that was killed by a signal with another process that is not SUID, tricking us into making the coredump of the original process readable by the attacker. With this patch, we effectively add one more check to the list of conditions that need be satisfied if we are to make the coredump accessible to the user. Reportedy-by: Qualys Security Advisory <[email protected]> (cherry-picked from commit 0c49e0049b7665bb7769a13ef346fef92e1ad4d6) (cherry-picked from commit c58a8a6ec9817275bb4babaa2c08e0e35090d4e3) (cherry picked from commit 19d439189ab85dd7222bdd59fd442bbcc8ea99a7) (cherry picked from commit 254ab8d)
1 parent 5855552 commit 7fc7aa5

File tree

4 files changed

+36
-4
lines changed

4 files changed

+36
-4
lines changed

man/systemd-coredump.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,18 @@ COREDUMP_FILENAME=/var/lib/systemd/coredump/core.Web….552351.….zst
274274
</listitem>
275275
</varlistentry>
276276

277+
<varlistentry>
278+
<term><varname>COREDUMP_DUMPABLE=</varname></term>
279+
280+
<listitem><para>The <constant>PR_GET_DUMPABLE</constant> field as reported by the kernel, see
281+
<citerefentry
282+
project='man-pages'><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
283+
</para>
284+
285+
<xi:include href="version-info.xml" xpointer="v258"/>
286+
</listitem>
287+
</varlistentry>
288+
277289
<varlistentry>
278290
<term><varname>COREDUMP_OPEN_FDS=</varname></term>
279291

src/coredump/coredump.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ typedef enum {
9696
_META_ARGV_REQUIRED,
9797
/* The fields below were added to kernel/core_pattern at later points, so they might be missing. */
9898
META_ARGV_HOSTNAME = _META_ARGV_REQUIRED, /* %h: hostname */
99+
META_ARGV_DUMPABLE, /* %d: as set by the kernel */
99100
/* If new fields are added, they should be added here, to maintain compatibility
100101
* with callers which don't know about the new fields. */
101102
_META_ARGV_MAX,
@@ -124,6 +125,7 @@ static const char * const meta_field_names[_META_MAX] = {
124125
[META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=",
125126
[META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=",
126127
[META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=",
128+
[META_ARGV_DUMPABLE] = "COREDUMP_DUMPABLE=",
127129
[META_COMM] = "COREDUMP_COMM=",
128130
[META_EXE] = "COREDUMP_EXE=",
129131
[META_UNIT] = "COREDUMP_UNIT=",
@@ -134,6 +136,7 @@ typedef struct Context {
134136
const char *meta[_META_MAX];
135137
size_t meta_size[_META_MAX];
136138
pid_t pid;
139+
unsigned dumpable;
137140
bool is_pid1;
138141
bool is_journald;
139142
} Context;
@@ -395,14 +398,16 @@ static int grant_user_access(int core_fd, const Context *context) {
395398
if (r < 0)
396399
return r;
397400

398-
/* We allow access if we got all the data and at_secure is not set and
399-
* the uid/gid matches euid/egid. */
401+
/* We allow access if dumpable on the command line was exactly 1, we got all the data,
402+
* at_secure is not set, and the uid/gid match euid/egid. */
400403
bool ret =
404+
context->dumpable == 1 &&
401405
at_secure == 0 &&
402406
uid != UID_INVALID && euid != UID_INVALID && uid == euid &&
403407
gid != GID_INVALID && egid != GID_INVALID && gid == egid;
404-
log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)",
408+
log_debug("Will %s access (dumpable=%u uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)",
405409
ret ? "permit" : "restrict",
410+
context->dumpable,
406411
uid, euid, gid, egid, yes_no(at_secure));
407412
return ret;
408413
}
@@ -1048,6 +1053,16 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
10481053
if (r < 0)
10491054
return log_error_errno(r, "Failed to parse PID \"%s\": %m", context->meta[META_ARGV_PID]);
10501055

1056+
/* The value is set to contents of /proc/sys/fs/suid_dumpable, which we set to 2,
1057+
* if the process is marked as not dumpable, see PR_SET_DUMPABLE(2const). */
1058+
if (context->meta[META_ARGV_DUMPABLE]) {
1059+
r = safe_atou(context->meta[META_ARGV_DUMPABLE], &context->dumpable);
1060+
if (r < 0)
1061+
return log_error_errno(r, "Failed to parse dumpable field \"%s\": %m", context->meta[META_ARGV_DUMPABLE]);
1062+
if (context->dumpable > 2)
1063+
log_notice("Got unexpected %%d/dumpable value %u.", context->dumpable);
1064+
}
1065+
10511066
unit = context->meta[META_UNIT];
10521067
context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE);
10531068
context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE);

sysctl.d/50-coredump.conf.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# the core dump.
1414
#
1515
# See systemd-coredump(8) and core(5).
16-
kernel.core_pattern=|{{ROOTLIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h
16+
kernel.core_pattern=|{{ROOTLIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h %d
1717

1818
# Allow 16 coredumps to be dispatched in parallel by the kernel.
1919
# We collect metadata from /proc/%P/, and thus need to make sure the crashed

test/units/testsuite-74.coredump.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,17 @@ journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE
163163
/usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509900 12345
164164
journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
165165
/usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509901 12345 mymachine
166+
journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
167+
/usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509902 12345 youmachine 1
166168
# Wait a bit for the coredumps to get processed
167169
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -lt 2 ]]; do sleep 1; done"
168170
coredumpctl info $$
169171
coredumpctl info COREDUMP_TIMESTAMP=1679509900000000
170172
coredumpctl info COREDUMP_TIMESTAMP=1679509901000000
171173
coredumpctl info COREDUMP_HOSTNAME="mymachine"
174+
coredumpctl info COREDUMP_TIMESTAMP=1679509902000000
175+
coredumpctl info COREDUMP_HOSTNAME="youmachine"
176+
coredumpctl info COREDUMP_DUMPABLE="1"
172177

173178
# This used to cause a stack overflow
174179
systemd-run -t --property CoredumpFilter=all ls /tmp

0 commit comments

Comments
 (0)