1
0
mirror of https://github.com/systemd/systemd synced 2026-04-09 16:44:51 +02:00

Compare commits

...

18 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
4248b9f6a5
Merge pull request #21580 from bluca/core_fork
Switch coredump ELF parsing to fork+dlopen
2021-11-30 21:49:53 +01:00
Yu Watanabe
528da64a0c network: address: drop deprecated temporary address
Fixes #19838.
2021-11-30 18:17:23 +01:00
Luca Boccassi
d2d8bd3a17 TODO: update dlopen todo list 2021-11-30 16:49:59 +00:00
Luca Boccassi
56f2a72919 elf-util: switch libelf/libdw to dlopen()
In order to avoid inflating the dependency list for the core
library, use dlopen when inspecting elfs, since it's only
used in two non-core executables.
2021-11-30 16:49:59 +00:00
Luca Boccassi
5361f62d6d meson: remove libdw dependency from pstore
systemd-pstore does not use any symbol from libdw, and never did,
but the dependency was listed since the beginning
2021-11-30 16:49:59 +00:00
Luca Boccassi
889f252973 elf-util: add stub for builds without libdw 2021-11-30 16:49:59 +00:00
Luca Boccassi
5564c6d802 elf-util: split out helper to parse build-id 2021-11-30 16:49:59 +00:00
Luca Boccassi
ea680f0524 coredump: move elf parsing utilities to src/shared/elf-util 2021-11-30 16:49:59 +00:00
Luca Boccassi
f05b5cddfb coredump: always log on failure in parse_core() 2021-11-30 16:49:58 +00:00
Luca Boccassi
f0e2377a68 coredump: make the buffer optional when analyzing a core file
Allow later usage when we only want to fetch the JSON packaging metadata
2021-11-30 16:49:58 +00:00
Luca Boccassi
61aea456c1 coredump: analyze object with libdwelf in forked process
Parsing objects is risky as data could be malformed or malicious,
so avoid doing that from the main systemd-coredump process and
instead fork another process, and set it to avoid generating
core files itself.
2021-11-30 16:49:58 +00:00
Luca Boccassi
67f7675f47 coredump: use _cleanup_ pattern
Note that c.f needs to be closed _before_ taking or freeing
the buf pointer, as it might be invalidated
2021-11-30 16:49:58 +00:00
Luca Boccassi
fe165e70cd coredump: flush accumulating buffer after parsing core file 2021-11-30 16:49:58 +00:00
Luca Boccassi
0e62d0340c coredump: check return of dwarf_getscopes 2021-11-30 16:49:58 +00:00
Zbigniew Jędrzejewski-Szmek
8592a0d799
Merge pull request #21578 from bluca/json_spec
docs: clarify JSON terms in COREDUMP_PACKAGE_METADATA
2021-11-30 16:36:36 +01:00
Rasmus Villemoes
4ced52595f sysusers: avoid creating spurious "nobody" group
On distros using Debian's base-passwd, the name of the group with gid 65534 is
nogroup. Currently, systemd-sysusers creates a spurious "nobody" group

  systemd-sysusers[243]: Creating group nobody with gid 996

That's both confusing and redundant, as the nobody user still has primary group
65534 aka nogroup, and the nobody group simply goes completely unused.

So explicitly specify the primary group of the nobody user, and add a line
ensuring that that group exists.

This is not a problem for Debian (or Ubuntu) itself, as they add their own
version of basic.conf in their systemd build logic. But it appears on for
example Yocto/OpenEmbedded.
2021-11-30 16:16:25 +01:00
Luca Boccassi
45f822ceb3 docs: clarify ELF note usage in COREDUMP_PACKAGE_METADATA 2021-11-30 14:42:43 +00:00
Luca Boccassi
bedd92b956 docs: clarify JSON terms in COREDUMP_PACKAGE_METADATA 2021-11-30 12:36:16 +00:00
14 changed files with 731 additions and 447 deletions

1
TODO
View File

@ -394,7 +394,6 @@ Features:
* make us use dynamically fewer deps for containers in general purpose distros: * make us use dynamically fewer deps for containers in general purpose distros:
o turn into dlopen() deps: o turn into dlopen() deps:
- elfutils (always)
- p11-kit-trust (always) - p11-kit-trust (always)
- kmod-libs (only when called from PID 1) - kmod-libs (only when called from PID 1)
- libblkid (only in RootImage= handling in PID 1, but not elsewhere) - libblkid (only in RootImage= handling in PID 1, but not elsewhere)

View File

@ -34,11 +34,11 @@ multiple implementers might use it when building packages, or core file analyzer
so on. so on.
The metadata will be embedded in a single, new, 4-bytes-aligned, allocated, 0-padded, The metadata will be embedded in a single, new, 4-bytes-aligned, allocated, 0-padded,
read-only ELF header section, in a key-value JSON format. Implementers working on parsing read-only ELF header section, in a name-value JSON object format. Implementers working on parsing
core files should not assume a specific list of keys, but parse anything that is included core files should not assume a specific list of names, but parse anything that is included
in the section. Implementers working on build tools should strive to use the same key in the section, and should look for the note using the `note type`. Implementers working on
names, for consistency. The most common will be listed here. When corresponding to the build tools should strive to use the same names, for consistency. The most common will be
content of os-release, the values should match, again for consistency. listed here. When corresponding to the content of os-release, the values should match, again for consistency.
If available, the metadata should also include the debuginfod server URL that can provide If available, the metadata should also include the debuginfod server URL that can provide
the original executable, debuginfo and sources, to further facilitate debugging. the original executable, debuginfo and sources, to further facilitate debugging.
@ -47,9 +47,9 @@ the original executable, debuginfo and sources, to further facilitate debugging.
``` ```
SECTION: `.note.package` SECTION: `.note.package`
node-id: `0xcafe1a7e` note type: `0xcafe1a7e`
Owner: `FDO` (FreeDesktop.org) Owner: `FDO` (FreeDesktop.org)
Value: a JSON string with the structure described below Value: a single JSON object encoded as a zero-terminated UTF-8 string
``` ```
* JSON payload * JSON payload
@ -67,6 +67,11 @@ Value: a JSON string with the structure described below
} }
``` ```
The format is a single JSON object, encoded as a zero-terminated `UTF-8` string.
Each name in the object shall be unique as per recommendations of
[RFC8259](https://datatracker.ietf.org/doc/html/rfc8259#section-4). Strings shall
not contain any control character, nor use `\uXXX` escaping.
When it comes to JSON numbers, this specification assumes that JSON parsers When it comes to JSON numbers, this specification assumes that JSON parsers
processing this information are capable of reproducing the full signed 53bit processing this information are capable of reproducing the full signed 53bit
integer range (i.e. -2⁵³+1…+2⁵³-1) as well as the full 64bit IEEE floating integer range (i.e. -2⁵³+1…+2⁵³-1) as well as the full 64bit IEEE floating

View File

@ -2822,7 +2822,6 @@ if conf.get('ENABLE_COREDUMP') == 1
link_with : [libshared], link_with : [libshared],
dependencies : [threads, dependencies : [threads,
libacl, libacl,
libdw,
libxz, libxz,
liblz4, liblz4,
libzstd], libzstd],
@ -2851,7 +2850,6 @@ if conf.get('ENABLE_PSTORE') == 1
link_with : [libshared], link_with : [libshared],
dependencies : [threads, dependencies : [threads,
libacl, libacl,
libdw,
libxz, libxz,
liblz4, liblz4,
libzstd], libzstd],

View File

@ -7,11 +7,6 @@
#include <sys/xattr.h> #include <sys/xattr.h>
#include <unistd.h> #include <unistd.h>
#if HAVE_ELFUTILS
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#endif
#include "sd-daemon.h" #include "sd-daemon.h"
#include "sd-journal.h" #include "sd-journal.h"
#include "sd-login.h" #include "sd-login.h"
@ -27,6 +22,7 @@
#include "copy.h" #include "copy.h"
#include "coredump-vacuum.h" #include "coredump-vacuum.h"
#include "dirent-util.h" #include "dirent-util.h"
#include "elf-util.h"
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h" #include "fileio.h"
@ -43,7 +39,6 @@
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h" #include "socket-util.h"
#include "special.h" #include "special.h"
#include "stacktrace.h"
#include "stat-util.h" #include "stat-util.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
@ -818,15 +813,17 @@ static int submit_coredump(
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to drop privileges: %m"); return log_error_errno(r, "Failed to drop privileges: %m");
#if HAVE_ELFUTILS
/* Try to get a stack trace if we can */ /* Try to get a stack trace if we can */
if (coredump_size > arg_process_size_max) { if (coredump_size > arg_process_size_max) {
log_debug("Not generating stack trace: core size %"PRIu64" is greater " log_debug("Not generating stack trace: core size %"PRIu64" is greater "
"than %"PRIu64" (the configured maximum)", "than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max); coredump_size, arg_process_size_max);
} else if (coredump_fd >= 0) } else if (coredump_fd >= 0)
coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata); (void) parse_elf_object(coredump_fd,
#endif context->meta[META_EXE],
/* fork_disable_dump= */endswith(context->meta[META_EXE], "systemd-coredump"), /* avoid loops */
&stacktrace,
&json_metadata);
log: log:
core_message = strjoina("Process ", context->meta[META_ARGV_PID], core_message = strjoina("Process ", context->meta[META_ARGV_PID],

View File

@ -6,12 +6,6 @@ systemd_coredump_sources = files('''
coredump-vacuum.h coredump-vacuum.h
'''.split()) '''.split())
if conf.get('HAVE_ELFUTILS') == 1
systemd_coredump_sources += files(
'stacktrace.c',
'stacktrace.h')
endif
coredumpctl_sources = files('coredumpctl.c') coredumpctl_sources = files('coredumpctl.c')
if conf.get('ENABLE_COREDUMP') == 1 and install_sysconfdir_samples if conf.get('ENABLE_COREDUMP') == 1 and install_sysconfdir_samples

View File

@ -1,411 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <dwarf.h>
#include <elfutils/libdwelf.h>
#include <elfutils/libdwfl.h>
#include <libelf.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "stacktrace.h"
#include "string-util.h"
#include "util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
struct stack_context {
FILE *f;
Dwfl *dwfl;
Elf *elf;
unsigned n_thread;
unsigned n_frame;
JsonVariant **package_metadata;
Set **modules;
};
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
struct stack_context *c = userdata;
Dwarf_Addr pc, pc_adjusted, bias = 0;
_cleanup_free_ Dwarf_Die *scopes = NULL;
const char *fname = NULL, *symbol = NULL;
Dwfl_Module *module;
bool is_activation;
uint64_t module_offset = 0;
assert(frame);
assert(c);
if (c->n_frame >= FRAMES_MAX)
return DWARF_CB_ABORT;
if (!dwfl_frame_pc(frame, &pc, &is_activation))
return DWARF_CB_ABORT;
pc_adjusted = pc - (is_activation ? 0 : 1);
module = dwfl_addrmodule(c->dwfl, pc_adjusted);
if (module) {
Dwarf_Die *s, *cudie;
int n;
Dwarf_Addr start;
cudie = dwfl_module_addrdie(module, pc_adjusted, &bias);
if (cudie) {
n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
for (s = scopes; s < scopes + n; s++) {
if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
Dwarf_Attribute *a, space;
a = dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
if (!a)
a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
if (a)
symbol = dwarf_formstring(a);
if (!symbol)
symbol = dwarf_diename(s);
if (symbol)
break;
}
}
}
if (!symbol)
symbol = dwfl_module_addrname(module, pc_adjusted);
fname = dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
module_offset = pc - start;
}
fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s + 0x%" PRIx64 ")\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname), module_offset);
c->n_frame++;
return DWARF_CB_OK;
}
static int thread_callback(Dwfl_Thread *thread, void *userdata) {
struct stack_context *c = userdata;
pid_t tid;
assert(thread);
assert(c);
if (c->n_thread >= THREADS_MAX)
return DWARF_CB_ABORT;
if (c->n_thread != 0)
fputc('\n', c->f);
c->n_frame = 0;
tid = dwfl_thread_tid(thread);
fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
if (dwfl_thread_getframes(thread, frame_callback, c) < 0)
return DWARF_CB_ABORT;
c->n_thread++;
return DWARF_CB_OK;
}
static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, struct stack_context *c) {
size_t n_program_headers;
int r;
assert(name);
assert(elf);
assert(c);
/* When iterating over PT_LOAD we will visit modules more than once */
if (set_contains(*c->modules, name))
return 0;
r = elf_getphdrnum(elf, &n_program_headers);
if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
return 0;
/* Iterate over all program headers in that ELF object. These will have been copied by
* the kernel verbatim when the core file is generated. */
for (size_t i = 0; i < n_program_headers; ++i) {
size_t note_offset = 0, name_offset, desc_offset;
GElf_Phdr mem, *program_header;
GElf_Nhdr note_header;
Elf_Data *data;
/* Package metadata is in PT_NOTE headers. */
program_header = gelf_getphdr(elf, i, &mem);
if (!program_header || program_header->p_type != PT_NOTE)
continue;
/* Fortunately there is an iterator we can use to walk over the
* elements of a PT_NOTE program header. We are interested in the
* note with type. */
data = elf_getdata_rawchunk(elf,
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
if (!data)
continue;
while (note_offset < data->d_size &&
(note_offset = gelf_getnote(data, note_offset, &note_header, &name_offset, &desc_offset)) > 0) {
const char *note_name = (const char *)data->d_buf + name_offset;
const char *payload = (const char *)data->d_buf + desc_offset;
if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
continue;
/* Package metadata might have different owners, but the
* magic ID is always the same. */
if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
r = json_parse(payload, 0, &v, NULL, NULL);
if (r < 0)
return log_error_errno(r, "json_parse on %s failed: %m", payload);
/* First pretty-print to the buffer, so that the metadata goes as
* plaintext in the journal. */
fprintf(c->f, "Metadata for module %s owned by %s found: ",
name, note_name);
json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL);
fputc('\n', c->f);
/* Secondly, if we have a build-id, merge it in the same JSON object
* so that it appears all nicely together in the logs/metadata. */
if (id_json) {
r = json_variant_merge(&v, id_json);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
}
/* Then we build a new object using the module name as the key, and merge it
* with the previous parses, so that in the end it all fits together in a single
* JSON blob. */
r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v))));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
r = json_variant_merge(c->package_metadata, w);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
/* Finally stash the name, so we avoid double visits. */
r = set_put_strdup(c->modules, name);
if (r < 0)
return log_error_errno(r, "set_put_strdup failed: %m");
return 1;
}
}
}
/* Didn't find package metadata for this module - that's ok, just go to the next. */
return 0;
}
static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
_cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
struct stack_context *c = arg;
size_t n_program_headers;
GElf_Addr id_vaddr, bias;
const unsigned char *id;
int id_len, r;
Elf *elf;
assert(mod);
assert(c);
if (!name)
name = "(unnamed)"; /* For logging purposes */
/* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
* core file, and extracting the build-id first and then the package metadata.
* We proceed in a best-effort fashion - not all ELF objects might contain both or either.
* The build-id is easy, as libdwfl parses it during the dwfl_core_file_report() call and
* stores it separately in an internal library struct. */
id_len = dwfl_module_build_id(mod, &id, &id_vaddr);
if (id_len <= 0)
/* If we don't find a build-id, note it in the journal message, and try
* anyway to find the package metadata. It's unlikely to have the latter
* without the former, but there's no hard rule. */
fprintf(c->f, "Found module %s without build-id.\n", name);
else {
JsonVariant *build_id;
/* We will later parse package metadata json and pass it to our caller. Prepare the
* build-id in json format too, so that it can be appended and parsed cleanly. It
* will then be added as metadata to the journal message with the stack trace. */
r = json_build(&id_json, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("buildId", JSON_BUILD_HEX(id, id_len))));
if (r < 0) {
log_error_errno(r, "json_build on build-id failed: %m");
return DWARF_CB_ABORT;
}
build_id = json_variant_by_key(id_json, "buildId");
assert_se(build_id);
fprintf(c->f, "Found module %s with build-id: %s\n", name, json_variant_string(build_id));
}
/* The .note.package metadata is more difficult. From the module, we need to get a reference
* to the ELF object first. We might be lucky and just get it from elfutils. */
elf = dwfl_module_getelf(mod, &bias);
if (elf) {
r = parse_package_metadata(name, id_json, elf, c);
if (r < 0)
return DWARF_CB_ABORT;
if (r > 0)
return DWARF_CB_OK;
} else
elf = c->elf;
/* We did not get the ELF object, or it's just a reference to the core. That is likely
* because we didn't get direct access to the executable, and the version of elfutils does
* not yet support parsing it out of the core file directly.
* So fallback to manual extraction - get the PT_LOAD section from the core,
* and if it's the right one we can interpret it as an Elf object, and parse
* its notes manually. */
r = elf_getphdrnum(elf, &n_program_headers);
if (r < 0) {
log_warning("Could not parse number of program headers from core file: %s",
elf_errmsg(-1)); /* -1 retrieves the most recent error */
return DWARF_CB_OK;
}
for (size_t i = 0; i < n_program_headers; ++i) {
GElf_Phdr mem, *program_header;
Elf_Data *data;
/* The core file stores the ELF files in the PT_LOAD segment. */
program_header = gelf_getphdr(elf, i, &mem);
if (!program_header || program_header->p_type != PT_LOAD)
continue;
/* Now get a usable Elf reference, and parse the notes from it. */
data = elf_getdata_rawchunk(elf,
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
if (!data)
continue;
Elf *memelf = elf_memory(data->d_buf, data->d_size);
if (!memelf)
continue;
r = parse_package_metadata(name, id_json, memelf, c);
if (r < 0)
return DWARF_CB_ABORT;
if (r > 0)
break;
}
return DWARF_CB_OK;
}
static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
.section_address = dwfl_offline_section_address,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_(set_freep) Set *modules = NULL;
struct stack_context c = {
.package_metadata = &package_metadata,
.modules = &modules,
};
char *buf = NULL;
size_t sz = 0;
int r;
assert(fd >= 0);
assert(ret);
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
return -errno;
c.f = open_memstream_unlocked(&buf, &sz);
if (!c.f)
return -ENOMEM;
elf_version(EV_CURRENT);
c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!c.elf) {
r = -EINVAL;
goto finish;
}
c.dwfl = dwfl_begin(&callbacks);
if (!c.dwfl) {
r = -EINVAL;
goto finish;
}
if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) {
r = -EINVAL;
goto finish;
}
if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) {
r = -EINVAL;
goto finish;
}
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(package_metadata);
r = 0;
finish:
if (c.dwfl)
dwfl_end(c.dwfl);
if (c.elf)
elf_end(c.elf);
safe_fclose(c.f);
free(buf);
return r;
}
void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
int r;
r = parse_core(fd, executable, ret, ret_package_metadata);
if (r == -EINVAL)
log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
else if (r < 0)
log_warning_errno(r, "Failed to generate stack trace: %m");
}

View File

@ -1,6 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "json.h"
void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata);

View File

@ -1394,6 +1394,20 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
address->scope = tmp->scope; address->scope = tmp->scope;
address_set_lifetime(address, &cinfo); address_set_lifetime(address, &cinfo);
address_enter_configured(address); address_enter_configured(address);
if (address->family == AF_INET6 &&
address->source == NETWORK_CONFIG_SOURCE_FOREIGN &&
FLAGS_SET(address->flags, IFA_F_SECONDARY | IFA_F_DEPRECATED)) {
/* The temporary address became deprecated, i.e. preferred lifetime == 0. */
log_address_debug(address, "Received deprecated", link);
r = address_remove(address);
if (r < 0)
link_enter_failed(link);
return 0;
}
log_address_debug(address, "Received updated", link); log_address_debug(address, "Received updated", link);
} else { } else {
address_set_lifetime(tmp, &cinfo); address_set_lifetime(tmp, &cinfo);

677
src/shared/elf-util.c Normal file
View File

@ -0,0 +1,677 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#if HAVE_ELFUTILS
#include <dwarf.h>
#include <elfutils/libdwelf.h>
#include <elfutils/libdwfl.h>
#include <libelf.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <unistd.h>
#include "alloc-util.h"
#include "dlfcn-util.h"
#include "elf-util.h"
#include "errno-util.h"
#include "fileio.h"
#include "fd-util.h"
#include "format-util.h"
#include "hexdecoct.h"
#include "io-util.h"
#include "macro.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "string-util.h"
#include "util.h"
#define FRAMES_MAX 64
#define THREADS_MAX 64
#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
static void *dw_dl = NULL;
static void *elf_dl = NULL;
/* libdw symbols */
Dwarf_Attribute *(*sym_dwarf_attr_integrate)(Dwarf_Die *, unsigned int, Dwarf_Attribute *);
const char *(*sym_dwarf_diename)(Dwarf_Die *);
const char *(*sym_dwarf_formstring)(Dwarf_Attribute *);
int (*sym_dwarf_getscopes)(Dwarf_Die *, Dwarf_Addr, Dwarf_Die **);
int (*sym_dwarf_getscopes_die)(Dwarf_Die *, Dwarf_Die **);
Elf *(*sym_dwelf_elf_begin)(int);
ssize_t (*sym_dwelf_elf_gnu_build_id)(Elf *, const void **);
int (*sym_dwarf_tag)(Dwarf_Die *);
Dwfl_Module *(*sym_dwfl_addrmodule)(Dwfl *, Dwarf_Addr);
Dwfl *(*sym_dwfl_begin)(const Dwfl_Callbacks *);
int (*sym_dwfl_build_id_find_elf)(Dwfl_Module *, void **, const char *, Dwarf_Addr, char **, Elf **);
int (*sym_dwfl_core_file_attach)(Dwfl *, Elf *);
int (*sym_dwfl_core_file_report)(Dwfl *, Elf *, const char *);
void (*sym_dwfl_end)(Dwfl *);
const char *(*sym_dwfl_errmsg)(int);
int (*sym_dwfl_errno)(void);
bool (*sym_dwfl_frame_pc)(Dwfl_Frame *, Dwarf_Addr *, bool *);
ptrdiff_t (*sym_dwfl_getmodules)(Dwfl *, int (*)(Dwfl_Module *, void **, const char *, Dwarf_Addr, void *), void *, ptrdiff_t);
int (*sym_dwfl_getthreads)(Dwfl *, int (*)(Dwfl_Thread *, void *), void *);
Dwarf_Die *(*sym_dwfl_module_addrdie)(Dwfl_Module *, Dwarf_Addr, Dwarf_Addr *);
const char *(*sym_dwfl_module_addrname)(Dwfl_Module *, GElf_Addr);
int (*sym_dwfl_module_build_id)(Dwfl_Module *, const unsigned char **, GElf_Addr *);
Elf *(*sym_dwfl_module_getelf)(Dwfl_Module *, GElf_Addr *);
const char *(*sym_dwfl_module_info)(Dwfl_Module *, void ***, Dwarf_Addr *, Dwarf_Addr *, Dwarf_Addr *, Dwarf_Addr *, const char **, const char **);
int (*sym_dwfl_offline_section_address)(Dwfl_Module *, void **, const char *, Dwarf_Addr, const char *, GElf_Word, const GElf_Shdr *, Dwarf_Addr *);
int (*sym_dwfl_report_end)(Dwfl *, int (*)(Dwfl_Module *, void *, const char *, Dwarf_Addr, void *), void *);
int (*sym_dwfl_standard_find_debuginfo)(Dwfl_Module *, void **, const char *, Dwarf_Addr, const char *, const char *, GElf_Word, char **);
int (*sym_dwfl_thread_getframes)(Dwfl_Thread *, int (*)(Dwfl_Frame *, void *), void *);
pid_t (*sym_dwfl_thread_tid)(Dwfl_Thread *);
/* libelf symbols */
Elf *(*sym_elf_begin)(int, Elf_Cmd, Elf *);
int (*sym_elf_end)(Elf *);
Elf_Data *(*sym_elf_getdata_rawchunk)(Elf *, int64_t, size_t, Elf_Type);
GElf_Ehdr *(*sym_gelf_getehdr)(Elf *, GElf_Ehdr *);
int (*sym_elf_getphdrnum)(Elf *, size_t *);
const char *(*sym_elf_errmsg)(int);
int (*sym_elf_errno)(void);
Elf *(*sym_elf_memory)(char *, size_t);
unsigned int (*sym_elf_version)(unsigned int);
GElf_Phdr *(*sym_gelf_getphdr)(Elf *, int, GElf_Phdr *);
size_t (*sym_gelf_getnote)(Elf_Data *, size_t, GElf_Nhdr *, size_t *, size_t *);
static int dlopen_dw(void) {
int r;
r = dlopen_many_sym_or_warn(
&dw_dl, "libdw.so.1", LOG_DEBUG,
DLSYM_ARG(dwarf_getscopes),
DLSYM_ARG(dwarf_getscopes_die),
DLSYM_ARG(dwarf_tag),
DLSYM_ARG(dwarf_attr_integrate),
DLSYM_ARG(dwarf_formstring),
DLSYM_ARG(dwarf_diename),
DLSYM_ARG(dwelf_elf_gnu_build_id),
DLSYM_ARG(dwelf_elf_begin),
DLSYM_ARG(dwfl_addrmodule),
DLSYM_ARG(dwfl_frame_pc),
DLSYM_ARG(dwfl_module_addrdie),
DLSYM_ARG(dwfl_module_addrname),
DLSYM_ARG(dwfl_module_info),
DLSYM_ARG(dwfl_module_build_id),
DLSYM_ARG(dwfl_module_getelf),
DLSYM_ARG(dwfl_begin),
DLSYM_ARG(dwfl_core_file_report),
DLSYM_ARG(dwfl_report_end),
DLSYM_ARG(dwfl_getmodules),
DLSYM_ARG(dwfl_core_file_attach),
DLSYM_ARG(dwfl_end),
DLSYM_ARG(dwfl_errmsg),
DLSYM_ARG(dwfl_errno),
DLSYM_ARG(dwfl_build_id_find_elf),
DLSYM_ARG(dwfl_standard_find_debuginfo),
DLSYM_ARG(dwfl_thread_tid),
DLSYM_ARG(dwfl_thread_getframes),
DLSYM_ARG(dwfl_getthreads),
DLSYM_ARG(dwfl_offline_section_address));
if (r <= 0)
return r;
return 1;
}
static int dlopen_elf(void) {
int r;
r = dlopen_many_sym_or_warn(
&elf_dl, "libelf.so.1", LOG_DEBUG,
DLSYM_ARG(elf_begin),
DLSYM_ARG(elf_end),
DLSYM_ARG(elf_getphdrnum),
DLSYM_ARG(elf_getdata_rawchunk),
DLSYM_ARG(elf_errmsg),
DLSYM_ARG(elf_errno),
DLSYM_ARG(elf_memory),
DLSYM_ARG(elf_version),
DLSYM_ARG(gelf_getehdr),
DLSYM_ARG(gelf_getphdr),
DLSYM_ARG(gelf_getnote));
if (r <= 0)
return r;
return 1;
}
typedef struct StackContext {
FILE *f;
Dwfl *dwfl;
Elf *elf;
unsigned n_thread;
unsigned n_frame;
JsonVariant **package_metadata;
Set **modules;
} StackContext;
static StackContext* stack_context_destroy(StackContext *c) {
if (!c)
return NULL;
c->f = safe_fclose(c->f);
if (c->dwfl) {
sym_dwfl_end(c->dwfl);
c->dwfl = NULL;
}
if (c->elf) {
sym_elf_end(c->elf);
c->elf = NULL;
}
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, sym_elf_end, NULL);
static int frame_callback(Dwfl_Frame *frame, void *userdata) {
StackContext *c = userdata;
Dwarf_Addr pc, pc_adjusted, bias = 0;
_cleanup_free_ Dwarf_Die *scopes = NULL;
const char *fname = NULL, *symbol = NULL;
Dwfl_Module *module;
bool is_activation;
uint64_t module_offset = 0;
assert(frame);
assert(c);
if (c->n_frame >= FRAMES_MAX)
return DWARF_CB_ABORT;
if (!sym_dwfl_frame_pc(frame, &pc, &is_activation))
return DWARF_CB_ABORT;
pc_adjusted = pc - (is_activation ? 0 : 1);
module = sym_dwfl_addrmodule(c->dwfl, pc_adjusted);
if (module) {
Dwarf_Die *s, *cudie;
int n;
Dwarf_Addr start;
cudie = sym_dwfl_module_addrdie(module, pc_adjusted, &bias);
if (cudie) {
n = sym_dwarf_getscopes(cudie, pc_adjusted - bias, &scopes);
if (n > 0)
for (s = scopes; s && s < scopes + n; s++) {
if (IN_SET(sym_dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) {
Dwarf_Attribute *a, space;
a = sym_dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space);
if (!a)
a = sym_dwarf_attr_integrate(s, DW_AT_linkage_name, &space);
if (a)
symbol = sym_dwarf_formstring(a);
if (!symbol)
symbol = sym_dwarf_diename(s);
if (symbol)
break;
}
}
}
if (!symbol)
symbol = sym_dwfl_module_addrname(module, pc_adjusted);
fname = sym_dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL);
module_offset = pc - start;
}
if (c->f)
fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s + 0x%" PRIx64 ")\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname), module_offset);
c->n_frame++;
return DWARF_CB_OK;
}
static int thread_callback(Dwfl_Thread *thread, void *userdata) {
StackContext *c = userdata;
pid_t tid;
assert(thread);
assert(c);
if (c->n_thread >= THREADS_MAX)
return DWARF_CB_ABORT;
if (c->n_thread != 0 && c->f)
fputc('\n', c->f);
c->n_frame = 0;
if (c->f) {
tid = sym_dwfl_thread_tid(thread);
fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid);
}
if (sym_dwfl_thread_getframes(thread, frame_callback, c) < 0)
return DWARF_CB_ABORT;
c->n_thread++;
return DWARF_CB_OK;
}
static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, StackContext *c) {
size_t n_program_headers;
int r;
assert(name);
assert(elf);
assert(c);
/* When iterating over PT_LOAD we will visit modules more than once */
if (set_contains(*c->modules, name))
return 0;
r = sym_elf_getphdrnum(elf, &n_program_headers);
if (r < 0) /* Not the handle we are looking for - that's ok, skip it */
return 0;
/* Iterate over all program headers in that ELF object. These will have been copied by
* the kernel verbatim when the core file is generated. */
for (size_t i = 0; i < n_program_headers; ++i) {
size_t note_offset = 0, name_offset, desc_offset;
GElf_Phdr mem, *program_header;
GElf_Nhdr note_header;
Elf_Data *data;
/* Package metadata is in PT_NOTE headers. */
program_header = sym_gelf_getphdr(elf, i, &mem);
if (!program_header || program_header->p_type != PT_NOTE)
continue;
/* Fortunately there is an iterator we can use to walk over the
* elements of a PT_NOTE program header. We are interested in the
* note with type. */
data = sym_elf_getdata_rawchunk(elf,
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
if (!data)
continue;
while (note_offset < data->d_size &&
(note_offset = sym_gelf_getnote(data, note_offset, &note_header, &name_offset, &desc_offset)) > 0) {
const char *note_name = (const char *)data->d_buf + name_offset;
const char *payload = (const char *)data->d_buf + desc_offset;
if (note_header.n_namesz == 0 || note_header.n_descsz == 0)
continue;
/* Package metadata might have different owners, but the
* magic ID is always the same. */
if (note_header.n_type == ELF_PACKAGE_METADATA_ID) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL;
r = json_parse(payload, 0, &v, NULL, NULL);
if (r < 0)
return log_error_errno(r, "json_parse on %s failed: %m", payload);
/* First pretty-print to the buffer, so that the metadata goes as
* plaintext in the journal. */
if (c->f) {
fprintf(c->f, "Metadata for module %s owned by %s found: ",
name, note_name);
json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL);
fputc('\n', c->f);
}
/* Secondly, if we have a build-id, merge it in the same JSON object
* so that it appears all nicely together in the logs/metadata. */
if (id_json) {
r = json_variant_merge(&v, id_json);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
}
/* Then we build a new object using the module name as the key, and merge it
* with the previous parses, so that in the end it all fits together in a single
* JSON blob. */
r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v))));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
r = json_variant_merge(c->package_metadata, w);
if (r < 0)
return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m");
/* Finally stash the name, so we avoid double visits. */
r = set_put_strdup(c->modules, name);
if (r < 0)
return log_error_errno(r, "set_put_strdup failed: %m");
return 1;
}
}
}
/* Didn't find package metadata for this module - that's ok, just go to the next. */
return 0;
}
/* Get the build-id out of an ELF object or a dwarf core module. */
static int parse_buildid(Dwfl_Module *mod, Elf *elf, const char *name, StackContext *c, JsonVariant **ret_id_json) {
_cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
const unsigned char *id;
GElf_Addr id_vaddr;
ssize_t id_len;
int r;
assert(mod || elf);
assert(c);
if (mod)
id_len = sym_dwfl_module_build_id(mod, &id, &id_vaddr);
else
id_len = sym_dwelf_elf_gnu_build_id(elf, (const void **)&id);
if (id_len <= 0) {
/* If we don't find a build-id, note it in the journal message, and try
* anyway to find the package metadata. It's unlikely to have the latter
* without the former, but there's no hard rule. */
if (c->f)
fprintf(c->f, "Module %s without build-id.\n", name);
} else {
/* We will later parse package metadata json and pass it to our caller. Prepare the
* build-id in json format too, so that it can be appended and parsed cleanly. It
* will then be added as metadata to the journal message with the stack trace. */
r = json_build(&id_json, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("buildId", JSON_BUILD_HEX(id, id_len))));
if (r < 0)
return log_error_errno(r, "json_build on build-id failed: %m");
if (c->f) {
JsonVariant *build_id = json_variant_by_key(id_json, "buildId");
assert(build_id);
fprintf(c->f, "Module %s with build-id %s\n", name, json_variant_string(build_id));
}
}
if (ret_id_json)
*ret_id_json = TAKE_PTR(id_json);
return 0;
}
static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Dwarf_Addr start, void *arg) {
_cleanup_(json_variant_unrefp) JsonVariant *id_json = NULL;
StackContext *c = arg;
size_t n_program_headers;
GElf_Addr bias;
int r;
Elf *elf;
assert(mod);
assert(c);
if (!name)
name = "(unnamed)"; /* For logging purposes */
/* We are iterating on each "module", which is what dwfl calls ELF objects contained in the
* core file, and extracting the build-id first and then the package metadata.
* We proceed in a best-effort fashion - not all ELF objects might contain both or either.
* The build-id is easy, as libdwfl parses it during the sym_dwfl_core_file_report() call and
* stores it separately in an internal library struct. */
r = parse_buildid(mod, NULL, name, c, &id_json);
if (r < 0)
return DWARF_CB_ABORT;
/* The .note.package metadata is more difficult. From the module, we need to get a reference
* to the ELF object first. We might be lucky and just get it from elfutils. */
elf = sym_dwfl_module_getelf(mod, &bias);
if (elf) {
r = parse_package_metadata(name, id_json, elf, c);
if (r < 0)
return DWARF_CB_ABORT;
if (r > 0)
return DWARF_CB_OK;
} else
elf = c->elf;
/* We did not get the ELF object, or it's just a reference to the core. That is likely
* because we didn't get direct access to the executable, and the version of elfutils does
* not yet support parsing it out of the core file directly.
* So fallback to manual extraction - get the PT_LOAD section from the core,
* and if it's the right one we can interpret it as an Elf object, and parse
* its notes manually. */
r = sym_elf_getphdrnum(elf, &n_program_headers);
if (r < 0) {
log_warning("Could not parse number of program headers from core file: %s",
sym_elf_errmsg(-1)); /* -1 retrieves the most recent error */
return DWARF_CB_OK;
}
for (size_t i = 0; i < n_program_headers; ++i) {
GElf_Phdr mem, *program_header;
Elf_Data *data;
/* The core file stores the ELF files in the PT_LOAD segment. */
program_header = sym_gelf_getphdr(elf, i, &mem);
if (!program_header || program_header->p_type != PT_LOAD)
continue;
/* Now get a usable Elf reference, and parse the notes from it. */
data = sym_elf_getdata_rawchunk(elf,
program_header->p_offset,
program_header->p_filesz,
ELF_T_NHDR);
if (!data)
continue;
_cleanup_(sym_elf_endp) Elf *memelf = sym_elf_memory(data->d_buf, data->d_size);
if (!memelf)
continue;
r = parse_package_metadata(name, id_json, memelf, c);
if (r < 0)
return DWARF_CB_ABORT;
if (r > 0)
break;
}
return DWARF_CB_OK;
}
static int parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) {
const Dwfl_Callbacks callbacks = {
.find_elf = sym_dwfl_build_id_find_elf,
.section_address = sym_dwfl_offline_section_address,
.find_debuginfo = sym_dwfl_standard_find_debuginfo,
};
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_(set_freep) Set *modules = NULL;
_cleanup_free_ char *buf = NULL; /* buf should be freed last, c.f closed first (via stack_context_destroy) */
_cleanup_(stack_context_destroy) StackContext c = {
.package_metadata = &package_metadata,
.modules = &modules,
};
size_t sz = 0;
int r;
assert(fd >= 0);
if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
return log_warning_errno(errno, "Failed to seek to beginning of the core file: %m");
if (ret) {
c.f = open_memstream_unlocked(&buf, &sz);
if (!c.f)
return log_oom();
}
sym_elf_version(EV_CURRENT);
c.elf = sym_elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!c.elf)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, elf_begin() failed: %s", sym_elf_errmsg(sym_elf_errno()));
c.dwfl = sym_dwfl_begin(&callbacks);
if (!c.dwfl)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_begin() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (sym_dwfl_core_file_report(c.dwfl, c.elf, executable) < 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_report() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (sym_dwfl_report_end(c.dwfl, NULL, NULL) != 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_report_end() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (sym_dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getmodules() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (sym_dwfl_core_file_attach(c.dwfl, c.elf) < 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_attach() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (sym_dwfl_getthreads(c.dwfl, thread_callback, &c) < 0)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getthreads() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno()));
if (ret) {
r = fflush_and_check(c.f);
if (r < 0)
return log_warning_errno(r, "Could not parse core file, flushing file buffer failed: %m");
c.f = safe_fclose(c.f);
*ret = TAKE_PTR(buf);
}
if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(package_metadata);
return 0;
}
int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
_cleanup_close_pair_ int error_pipe[2] = { -1, -1 }, return_pipe[2] = { -1, -1 }, json_pipe[2] = { -1, -1 };
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_free_ char *buf = NULL;
int r;
r = dlopen_dw();
if (r < 0)
return r;
r = dlopen_elf();
if (r < 0)
return r;
r = RET_NERRNO(pipe2(error_pipe, O_CLOEXEC|O_NONBLOCK));
if (r < 0)
return r;
if (ret) {
r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC));
if (r < 0)
return r;
}
if (ret_package_metadata) {
r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC));
if (r < 0)
return r;
}
/* Parsing possibly malformed data is crash-happy, so fork. In case we crash,
* the core file will not be lost, and the messages will still be attached to
* the journal. Reading the elf object might be slow, but it still has an upper
* bound since the core files have an upper size limit. It's also not doing any
* system call or interacting with the system in any way, besides reading from
* the file descriptor and writing into these four pipes. */
r = safe_fork_full("(sd-parse-elf)",
(int[]){ fd, error_pipe[1], return_pipe[1], json_pipe[1] },
4,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG,
NULL);
if (r < 0) {
if (r == -EPROTO) { /* We should have the errno from the child, but don't clobber original error */
int e, k;
k = read(error_pipe[0], &e, sizeof(e));
if (k < 0 && errno != EAGAIN) /* Pipe is non-blocking, EAGAIN means there's nothing */
return -errno;
if (k == sizeof(e))
return e; /* propagate error sent to us from child */
if (k != 0)
return -EIO;
}
return r;
}
if (r == 0) {
/* We want to avoid loops, given this can be called from systemd-coredump */
if (fork_disable_dump)
prctl(PR_SET_DUMPABLE, 0);
r = parse_core(fd, executable, ret ? &buf : NULL, ret_package_metadata ? &package_metadata : NULL);
if (r < 0)
goto child_fail;
if (buf) {
r = loop_write(return_pipe[1], buf, strlen(buf), false);
if (r < 0)
goto child_fail;
return_pipe[1] = safe_close(return_pipe[1]);
}
if (package_metadata) {
_cleanup_fclose_ FILE *json_out = NULL;
json_out = take_fdopen(&json_pipe[1], "w");
if (!json_out) {
r = -errno;
goto child_fail;
}
json_variant_dump(package_metadata, JSON_FORMAT_FLUSH, json_out, NULL);
}
_exit(EXIT_SUCCESS);
child_fail:
(void) write(error_pipe[1], &r, sizeof(r));
_exit(EXIT_FAILURE);
}
error_pipe[1] = safe_close(error_pipe[1]);
return_pipe[1] = safe_close(return_pipe[1]);
json_pipe[1] = safe_close(json_pipe[1]);
if (ret) {
_cleanup_fclose_ FILE *in = NULL;
in = take_fdopen(&return_pipe[0], "r");
if (!in)
return -errno;
r = read_full_stream(in, &buf, NULL);
if (r < 0)
return r;
}
if (ret_package_metadata) {
_cleanup_fclose_ FILE *json_in = NULL;
json_in = take_fdopen(&json_pipe[0], "r");
if (!json_in)
return -errno;
r = json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
if (r < 0 && r != -EINVAL) /* EINVAL: json was empty, so we got nothing, but that's ok */
return r;
}
if (ret)
*ret = TAKE_PTR(buf);
if (ret_package_metadata)
*ret_package_metadata = TAKE_PTR(package_metadata);
return 0;
}
#endif

15
src/shared/elf-util.h Normal file
View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "json.h"
#if HAVE_ELFUTILS
/* Parse an ELF object in a forked process, so that errors while iterating over
* untrusted and potentially malicious data do not propagate to the main caller's process.
* If fork_disable_dump, the child process will not dump core if it crashes. */
int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata);
#else
static inline int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "elfutils disabled, parsing ELF objects not supported");
}
#endif

View File

@ -107,6 +107,8 @@ shared_sources = files('''
dropin.h dropin.h
efi-loader.c efi-loader.c
efi-loader.h efi-loader.h
elf-util.c
elf-util.h
enable-mempool.c enable-mempool.c
env-file-label.c env-file-label.c
env-file-label.h env-file-label.h

View File

@ -8,8 +8,9 @@
# The superuser # The superuser
u root 0 "Super User" /root u root 0 "Super User" /root
# The nobody user for NFS file systems # The nobody user/group for NFS file systems
u {{NOBODY_USER_NAME}} 65534 "Nobody" - g {{NOBODY_GROUP_NAME}} 65534 - -
u {{NOBODY_USER_NAME }} 65534:65534 "Nobody" -
# Administrator group: can *see* more than normal users # Administrator group: can *see* more than normal users
g adm {{ADM_GID }} - - g adm {{ADM_GID }} - -

View File

@ -1174,7 +1174,7 @@ install_missing_libraries() {
local lib path local lib path
# A number of dependencies is now optional via dlopen, so the install # A number of dependencies is now optional via dlopen, so the install
# script will not pick them up, since it looks at linkage. # script will not pick them up, since it looks at linkage.
for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf; do for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf libelf libdw; do
ddebug "Searching for $lib via pkg-config" ddebug "Searching for $lib via pkg-config"
if pkg-config --exists "$lib"; then if pkg-config --exists "$lib"; then
path="$(pkg-config --variable=libdir "$lib")" path="$(pkg-config --variable=libdir "$lib")"

View File

@ -35,11 +35,10 @@ ProtectKernelTunables=yes
ProtectKernelLogs=yes ProtectKernelLogs=yes
ProtectSystem=strict ProtectSystem=strict
RestrictAddressFamilies=AF_UNIX RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=yes
RestrictRealtime=yes RestrictRealtime=yes
RestrictSUIDSGID=yes RestrictSUIDSGID=yes
RuntimeMaxSec=5min RuntimeMaxSec=5min
StateDirectory=systemd/coredump StateDirectory=systemd/coredump
SystemCallArchitectures=native SystemCallArchitectures=native
SystemCallErrorNumber=EPERM SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service SystemCallFilter=@system-service @mount