From 0e62d0340c8c583ea61a5019a6589d38bf023e41 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 30 Nov 2021 02:01:53 +0000 Subject: [PATCH 01/12] coredump: check return of dwarf_getscopes --- src/coredump/stacktrace.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index a028166f858..24ea087d382 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -60,22 +60,23 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { 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; + if (n > 0) + for (s = scopes; s && 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); + 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) + break; + } } - } } if (!symbol) From fe165e70cd787806fe0809efc2db45ff0451d645 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 29 Nov 2021 10:23:49 +0000 Subject: [PATCH 02/12] coredump: flush accumulating buffer after parsing core file --- src/coredump/stacktrace.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index 24ea087d382..a74ef41ac9d 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -326,7 +326,7 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** .package_metadata = &package_metadata, .modules = &modules, }; - char *buf = NULL; + _cleanup_free_ char *buf = NULL; size_t sz = 0; int r; @@ -379,6 +379,9 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** goto finish; } + r = fflush_and_check(c.f); + if (r < 0) + goto finish; c.f = safe_fclose(c.f); *ret = TAKE_PTR(buf); @@ -396,8 +399,6 @@ finish: safe_fclose(c.f); - free(buf); - return r; } From 67f7675f477d369506eeb17ef2d5b365f83ac84a Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 29 Nov 2021 11:31:00 +0000 Subject: [PATCH 03/12] coredump: use _cleanup_ pattern Note that c.f needs to be closed _before_ taking or freeing the buf pointer, as it might be invalidated --- src/coredump/stacktrace.c | 98 +++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index a74ef41ac9d..23161d5ac13 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -21,7 +21,7 @@ #define THREADS_MAX 64 #define ELF_PACKAGE_METADATA_ID 0xcafe1a7e -struct stack_context { +typedef struct StackContext { FILE *f; Dwfl *dwfl; Elf *elf; @@ -29,10 +29,31 @@ struct stack_context { 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) { + dwfl_end(c->dwfl); + c->dwfl = NULL; + } + + if (c->elf) { + elf_end(c->elf); + c->elf = NULL; + } + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, elf_end, NULL); static int frame_callback(Dwfl_Frame *frame, void *userdata) { - struct stack_context *c = userdata; + StackContext *c = userdata; Dwarf_Addr pc, pc_adjusted, bias = 0; _cleanup_free_ Dwarf_Die *scopes = NULL; const char *fname = NULL, *symbol = NULL; @@ -93,7 +114,7 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { } static int thread_callback(Dwfl_Thread *thread, void *userdata) { - struct stack_context *c = userdata; + StackContext *c = userdata; pid_t tid; assert(thread); @@ -118,7 +139,7 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) { return DWARF_CB_OK; } -static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, struct stack_context *c) { +static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *elf, StackContext *c) { size_t n_program_headers; int r; @@ -215,7 +236,7 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e 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; + StackContext *c = arg; size_t n_program_headers; GElf_Addr id_vaddr, bias; const unsigned char *id; @@ -299,7 +320,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, if (!data) continue; - Elf *memelf = elf_memory(data->d_buf, data->d_size); + _cleanup_(elf_endp) Elf *memelf = elf_memory(data->d_buf, data->d_size); if (!memelf) continue; r = parse_package_metadata(name, id_json, memelf, c); @@ -322,11 +343,11 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** _cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL; _cleanup_(set_freep) Set *modules = NULL; - struct stack_context c = { + _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, }; - _cleanup_free_ char *buf = NULL; size_t sz = 0; int r; @@ -343,63 +364,38 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** elf_version(EV_CURRENT); c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); - if (!c.elf) { - r = -EINVAL; - goto finish; - } + if (!c.elf) + return -EINVAL; c.dwfl = dwfl_begin(&callbacks); - if (!c.dwfl) { - r = -EINVAL; - goto finish; - } + if (!c.dwfl) + return -EINVAL; - if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) { - r = -EINVAL; - goto finish; - } + if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) + return -EINVAL; - if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) { - r = -EINVAL; - goto finish; - } + if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) + return -EINVAL; - if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) { - r = -EINVAL; - goto finish; - } + if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) + return -EINVAL; - if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) { - r = -EINVAL; - goto finish; - } + if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) + return -EINVAL; - if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) { - r = -EINVAL; - goto finish; - } + if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) + return -EINVAL; r = fflush_and_check(c.f); if (r < 0) - goto finish; - c.f = safe_fclose(c.f); + return r; + 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); - - return r; + return 0; } void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) { From 61aea456c12c54f49c4a76259af130e576130ce9 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 21 Nov 2021 17:05:28 +0000 Subject: [PATCH 04/12] 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. --- src/coredump/coredump.c | 6 +- src/coredump/stacktrace.c | 144 ++++++++++++++++++++++++++--- src/coredump/stacktrace.h | 5 +- units/systemd-coredump@.service.in | 3 +- 4 files changed, 140 insertions(+), 18 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 9b035a5d8ad..c4b393631ab 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -825,7 +825,11 @@ static int submit_coredump( "than %"PRIu64" (the configured maximum)", coredump_size, arg_process_size_max); } else if (coredump_fd >= 0) - coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata); + (void) parse_elf_object(coredump_fd, + context->meta[META_EXE], + /* fork_disable_dump= */endswith(context->meta[META_EXE], "systemd-coredump"), /* avoid loops */ + &stacktrace, + &json_metadata); #endif log: diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index 23161d5ac13..eec1af2dd34 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -4,15 +4,21 @@ #include #include #include +#include +#include #include #include #include "alloc-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 "stacktrace.h" #include "string-util.h" #include "util.h" @@ -365,30 +371,30 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); if (!c.elf) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, elf_begin() failed: %s", elf_errmsg(elf_errno())); c.dwfl = dwfl_begin(&callbacks); if (!c.dwfl) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_begin() failed: %s", dwfl_errmsg(dwfl_errno())); if (dwfl_core_file_report(c.dwfl, c.elf, executable) < 0) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_report() failed: %s", dwfl_errmsg(dwfl_errno())); if (dwfl_report_end(c.dwfl, NULL, NULL) != 0) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_report_end() failed: %s", dwfl_errmsg(dwfl_errno())); if (dwfl_getmodules(c.dwfl, &module_callback, &c, 0) < 0) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getmodules() failed: %s", dwfl_errmsg(dwfl_errno())); if (dwfl_core_file_attach(c.dwfl, c.elf) < 0) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_core_file_attach() failed: %s", dwfl_errmsg(dwfl_errno())); if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) - return -EINVAL; + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getthreads() failed: %s", dwfl_errmsg(dwfl_errno())); r = fflush_and_check(c.f); if (r < 0) - return r; + 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); @@ -398,12 +404,122 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** return 0; } -void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata) { +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 = 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"); + 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; } diff --git a/src/coredump/stacktrace.h b/src/coredump/stacktrace.h index 5039b934dda..429fb5c912d 100644 --- a/src/coredump/stacktrace.h +++ b/src/coredump/stacktrace.h @@ -3,4 +3,7 @@ #include "json.h" -void coredump_parse_core(int fd, const char *executable, char **ret, JsonVariant **ret_package_metadata); +/* 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); diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in index 6bf2817a8ca..15bfb243b41 100644 --- a/units/systemd-coredump@.service.in +++ b/units/systemd-coredump@.service.in @@ -35,11 +35,10 @@ ProtectKernelTunables=yes ProtectKernelLogs=yes ProtectSystem=strict RestrictAddressFamilies=AF_UNIX -RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes RuntimeMaxSec=5min StateDirectory=systemd/coredump SystemCallArchitectures=native SystemCallErrorNumber=EPERM -SystemCallFilter=@system-service +SystemCallFilter=@system-service @mount From f0e2377a680d03fe32cb47a2428fed7730094001 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 17 Nov 2021 01:27:57 +0000 Subject: [PATCH 05/12] coredump: make the buffer optional when analyzing a core file Allow later usage when we only want to fetch the JSON packaging metadata --- src/coredump/stacktrace.c | 52 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index eec1af2dd34..dc8a71ac7c8 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -113,7 +113,8 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { 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); + 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; @@ -129,13 +130,15 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) { if (c->n_thread >= THREADS_MAX) return DWARF_CB_ABORT; - if (c->n_thread != 0) + if (c->n_thread != 0 && c->f) 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 (c->f) { + 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; @@ -203,10 +206,12 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e /* 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); + 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. */ @@ -261,12 +266,13 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, * 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 (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 { + if (c->f) + 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 @@ -280,7 +286,8 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, 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)); + if (c->f) + 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 @@ -358,14 +365,15 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** 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; + if (ret) { + c.f = open_memstream_unlocked(&buf, &sz); + if (!c.f) + return -ENOMEM; + } elf_version(EV_CURRENT); @@ -392,12 +400,14 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** if (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getthreads() failed: %s", dwfl_errmsg(dwfl_errno())); - r = fflush_and_check(c.f); - if (r < 0) - return log_warning_errno(r, "Could not parse core file, flushing file buffer failed: %m"); + 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); + c.f = safe_fclose(c.f); + *ret = TAKE_PTR(buf); + } if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(package_metadata); From f05b5cddfb2886ab95241d64f97ab039440afe96 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 24 Nov 2021 20:13:44 +0000 Subject: [PATCH 06/12] coredump: always log on failure in parse_core() --- src/coredump/stacktrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index dc8a71ac7c8..b430f2b3eae 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -367,12 +367,12 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** assert(fd >= 0); if (lseek(fd, 0, SEEK_SET) == (off_t) -1) - return -errno; + 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 -ENOMEM; + return log_oom(); } elf_version(EV_CURRENT); From ea680f0524c1644642b55cadfe91b22797ec3530 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 17 Nov 2021 01:44:33 +0000 Subject: [PATCH 07/12] coredump: move elf parsing utilities to src/shared/elf-util --- meson.build | 1 - src/coredump/coredump.c | 7 +------ src/coredump/meson.build | 6 ------ src/{coredump/stacktrace.c => shared/elf-util.c} | 2 +- src/{coredump/stacktrace.h => shared/elf-util.h} | 0 src/shared/meson.build | 7 +++++++ 6 files changed, 9 insertions(+), 14 deletions(-) rename src/{coredump/stacktrace.c => shared/elf-util.c} (99%) rename src/{coredump/stacktrace.h => shared/elf-util.h} (100%) diff --git a/meson.build b/meson.build index b0054b66672..987ceb37b5b 100644 --- a/meson.build +++ b/meson.build @@ -2822,7 +2822,6 @@ if conf.get('ENABLE_COREDUMP') == 1 link_with : [libshared], dependencies : [threads, libacl, - libdw, libxz, liblz4, libzstd], diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index c4b393631ab..e2d8b382687 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -7,11 +7,6 @@ #include #include -#if HAVE_ELFUTILS -#include -#include -#endif - #include "sd-daemon.h" #include "sd-journal.h" #include "sd-login.h" @@ -27,6 +22,7 @@ #include "copy.h" #include "coredump-vacuum.h" #include "dirent-util.h" +#include "elf-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -43,7 +39,6 @@ #include "signal-util.h" #include "socket-util.h" #include "special.h" -#include "stacktrace.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" diff --git a/src/coredump/meson.build b/src/coredump/meson.build index 5607a78cf53..b832192c9f9 100644 --- a/src/coredump/meson.build +++ b/src/coredump/meson.build @@ -6,12 +6,6 @@ systemd_coredump_sources = files(''' coredump-vacuum.h '''.split()) -if conf.get('HAVE_ELFUTILS') == 1 - systemd_coredump_sources += files( - 'stacktrace.c', - 'stacktrace.h') -endif - coredumpctl_sources = files('coredumpctl.c') if conf.get('ENABLE_COREDUMP') == 1 and install_sysconfdir_samples diff --git a/src/coredump/stacktrace.c b/src/shared/elf-util.c similarity index 99% rename from src/coredump/stacktrace.c rename to src/shared/elf-util.c index b430f2b3eae..12b42bf90fb 100644 --- a/src/coredump/stacktrace.c +++ b/src/shared/elf-util.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "elf-util.h" #include "errno-util.h" #include "fileio.h" #include "fd-util.h" @@ -19,7 +20,6 @@ #include "macro.h" #include "process-util.h" #include "rlimit-util.h" -#include "stacktrace.h" #include "string-util.h" #include "util.h" diff --git a/src/coredump/stacktrace.h b/src/shared/elf-util.h similarity index 100% rename from src/coredump/stacktrace.h rename to src/shared/elf-util.h diff --git a/src/shared/meson.build b/src/shared/meson.build index 130c03ec99a..2187cc62052 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -388,6 +388,12 @@ if conf.get('ENABLE_NSCD') == 1 shared_sources += files('nscd-flush.c') endif +if conf.get('HAVE_ELFUTILS') == 1 + shared_sources += files( + 'elf-util.c', + 'elf-util.h') +endif + generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh') ip_protocol_list_txt = custom_target( 'ip-protocol-list.txt', @@ -436,6 +442,7 @@ libshared_deps = [threads, libcap, libcrypt, libdl, + libdw, libgcrypt, libiptc, libkmod, From 5564c6d802e8fc8fd4d47bb0d781f7514e097f39 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 18 Nov 2021 00:51:41 +0000 Subject: [PATCH 08/12] elf-util: split out helper to parse build-id --- src/shared/elf-util.c | 74 +++++++++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 12b42bf90fb..9eb8495c351 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -245,13 +245,54 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e 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 = dwfl_module_build_id(mod, &id, &id_vaddr); + else + id_len = 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 id_vaddr, bias; - const unsigned char *id; - int id_len, r; + GElf_Addr bias; + int r; Elf *elf; assert(mod); @@ -265,30 +306,9 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, * 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. */ - if (c->f) - 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); - if (c->f) - fprintf(c->f, "Found module %s with build-id: %s\n", name, json_variant_string(build_id)); - } + 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. */ From 889f2529733c646ba7becfd05e31c90a91d67044 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Sun, 21 Nov 2021 17:31:20 +0000 Subject: [PATCH 09/12] elf-util: add stub for builds without libdw --- src/coredump/coredump.c | 2 -- src/shared/elf-util.h | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e2d8b382687..cb74b34affa 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -813,7 +813,6 @@ static int submit_coredump( if (r < 0) return log_error_errno(r, "Failed to drop privileges: %m"); -#if HAVE_ELFUTILS /* Try to get a stack trace if we can */ if (coredump_size > arg_process_size_max) { log_debug("Not generating stack trace: core size %"PRIu64" is greater " @@ -825,7 +824,6 @@ static int submit_coredump( /* fork_disable_dump= */endswith(context->meta[META_EXE], "systemd-coredump"), /* avoid loops */ &stacktrace, &json_metadata); -#endif log: core_message = strjoina("Process ", context->meta[META_ARGV_PID], diff --git a/src/shared/elf-util.h b/src/shared/elf-util.h index 429fb5c912d..cf3d9be1284 100644 --- a/src/shared/elf-util.h +++ b/src/shared/elf-util.h @@ -3,7 +3,13 @@ #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 From 5361f62d6d5b25c6545059f7d2513324be8d7a49 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 24 Nov 2021 20:23:02 +0000 Subject: [PATCH 10/12] 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 --- meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/meson.build b/meson.build index 987ceb37b5b..c80750c3b59 100644 --- a/meson.build +++ b/meson.build @@ -2850,7 +2850,6 @@ if conf.get('ENABLE_PSTORE') == 1 link_with : [libshared], dependencies : [threads, libacl, - libdw, libxz, liblz4, libzstd], From 56f2a72919cbc30e1a047066e16d288b146ddd7c Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Mon, 22 Nov 2021 11:11:21 +0000 Subject: [PATCH 11/12] 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. --- src/shared/elf-util.c | 230 +++++++++++++++++++++++++++++++---------- src/shared/meson.build | 9 +- test/test-functions | 2 +- 3 files changed, 179 insertions(+), 62 deletions(-) diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 9eb8495c351..e5fb8cb2e3f 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#if HAVE_ELFUTILS + #include #include #include @@ -10,6 +12,7 @@ #include #include "alloc-util.h" +#include "dlfcn-util.h" #include "elf-util.h" #include "errno-util.h" #include "fileio.h" @@ -27,6 +30,115 @@ #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; @@ -44,19 +156,19 @@ static StackContext* stack_context_destroy(StackContext *c) { c->f = safe_fclose(c->f); if (c->dwfl) { - dwfl_end(c->dwfl); + sym_dwfl_end(c->dwfl); c->dwfl = NULL; } if (c->elf) { - elf_end(c->elf); + sym_elf_end(c->elf); c->elf = NULL; } return NULL; } -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, elf_end, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, sym_elf_end, NULL); static int frame_callback(Dwfl_Frame *frame, void *userdata) { StackContext *c = userdata; @@ -73,32 +185,32 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { if (c->n_frame >= FRAMES_MAX) return DWARF_CB_ABORT; - if (!dwfl_frame_pc(frame, &pc, &is_activation)) + if (!sym_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); + module = sym_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); + cudie = sym_dwfl_module_addrdie(module, pc_adjusted, &bias); if (cudie) { - n = dwarf_getscopes(cudie, pc_adjusted - bias, &scopes); + n = sym_dwarf_getscopes(cudie, pc_adjusted - bias, &scopes); if (n > 0) for (s = scopes; s && s < scopes + n; s++) { - if (IN_SET(dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) { + if (IN_SET(sym_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); + a = sym_dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space); if (!a) - a = dwarf_attr_integrate(s, DW_AT_linkage_name, &space); + a = sym_dwarf_attr_integrate(s, DW_AT_linkage_name, &space); if (a) - symbol = dwarf_formstring(a); + symbol = sym_dwarf_formstring(a); if (!symbol) - symbol = dwarf_diename(s); + symbol = sym_dwarf_diename(s); if (symbol) break; @@ -107,9 +219,9 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { } if (!symbol) - symbol = dwfl_module_addrname(module, pc_adjusted); + symbol = sym_dwfl_module_addrname(module, pc_adjusted); - fname = dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL); + fname = sym_dwfl_module_info(module, NULL, &start, NULL, NULL, NULL, NULL, NULL); module_offset = pc - start; } @@ -136,11 +248,11 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) { c->n_frame = 0; if (c->f) { - tid = dwfl_thread_tid(thread); + tid = sym_dwfl_thread_tid(thread); fprintf(c->f, "Stack trace of thread " PID_FMT ":\n", tid); } - if (dwfl_thread_getframes(thread, frame_callback, c) < 0) + if (sym_dwfl_thread_getframes(thread, frame_callback, c) < 0) return DWARF_CB_ABORT; c->n_thread++; @@ -160,7 +272,7 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e if (set_contains(*c->modules, name)) return 0; - r = elf_getphdrnum(elf, &n_program_headers); + 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; @@ -173,22 +285,22 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e Elf_Data *data; /* Package metadata is in PT_NOTE headers. */ - program_header = gelf_getphdr(elf, i, &mem); + 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 = elf_getdata_rawchunk(elf, - program_header->p_offset, - program_header->p_filesz, - ELF_T_NHDR); + 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 = gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0) { + (note_offset = sym_gelf_getnote(data, note_offset, ¬e_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; @@ -257,9 +369,9 @@ static int parse_buildid(Dwfl_Module *mod, Elf *elf, const char *name, StackCont assert(c); if (mod) - id_len = dwfl_module_build_id(mod, &id, &id_vaddr); + id_len = sym_dwfl_module_build_id(mod, &id, &id_vaddr); else - id_len = dwelf_elf_gnu_build_id(elf, (const void **)&id); + 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 @@ -304,7 +416,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, /* 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 + * 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) @@ -312,7 +424,7 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, /* 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); + elf = sym_dwfl_module_getelf(mod, &bias); if (elf) { r = parse_package_metadata(name, id_json, elf, c); if (r < 0) @@ -329,10 +441,10 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, * 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); + r = sym_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 */ + sym_elf_errmsg(-1)); /* -1 retrieves the most recent error */ return DWARF_CB_OK; } @@ -341,19 +453,19 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, Elf_Data *data; /* The core file stores the ELF files in the PT_LOAD segment. */ - program_header = gelf_getphdr(elf, i, &mem); + 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 = elf_getdata_rawchunk(elf, - program_header->p_offset, - program_header->p_filesz, - ELF_T_NHDR); + data = sym_elf_getdata_rawchunk(elf, + program_header->p_offset, + program_header->p_filesz, + ELF_T_NHDR); if (!data) continue; - _cleanup_(elf_endp) Elf *memelf = elf_memory(data->d_buf, data->d_size); + _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); @@ -368,10 +480,10 @@ static int module_callback(Dwfl_Module *mod, void **userdata, const char *name, 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, + 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; @@ -395,30 +507,30 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** return log_oom(); } - elf_version(EV_CURRENT); + sym_elf_version(EV_CURRENT); - c.elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + 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", elf_errmsg(elf_errno())); + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, elf_begin() failed: %s", sym_elf_errmsg(sym_elf_errno())); - c.dwfl = dwfl_begin(&callbacks); + 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", dwfl_errmsg(dwfl_errno())); + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_begin() failed: %s", sym_dwfl_errmsg(sym_dwfl_errno())); - if (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", dwfl_errmsg(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 (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", dwfl_errmsg(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 (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", dwfl_errmsg(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 (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", dwfl_errmsg(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 (dwfl_getthreads(c.dwfl, thread_callback, &c) < 0) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Could not parse core file, dwfl_getthreads() failed: %s", dwfl_errmsg(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); @@ -440,6 +552,14 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha _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; @@ -553,3 +673,5 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha return 0; } + +#endif diff --git a/src/shared/meson.build b/src/shared/meson.build index 2187cc62052..562d984fc36 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -107,6 +107,8 @@ shared_sources = files(''' dropin.h efi-loader.c efi-loader.h + elf-util.c + elf-util.h enable-mempool.c env-file-label.c env-file-label.h @@ -388,12 +390,6 @@ if conf.get('ENABLE_NSCD') == 1 shared_sources += files('nscd-flush.c') endif -if conf.get('HAVE_ELFUTILS') == 1 - shared_sources += files( - 'elf-util.c', - 'elf-util.h') -endif - generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh') ip_protocol_list_txt = custom_target( 'ip-protocol-list.txt', @@ -442,7 +438,6 @@ libshared_deps = [threads, libcap, libcrypt, libdl, - libdw, libgcrypt, libiptc, libkmod, diff --git a/test/test-functions b/test/test-functions index d8b549a1dc8..5e3eadd37fc 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1174,7 +1174,7 @@ install_missing_libraries() { local lib path # A number of dependencies is now optional via dlopen, so the install # 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" if pkg-config --exists "$lib"; then path="$(pkg-config --variable=libdir "$lib")" From d2d8bd3a1756c7c6bd1ea331b9e043a7d5028a64 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Wed, 24 Nov 2021 20:26:19 +0000 Subject: [PATCH 12/12] TODO: update dlopen todo list --- TODO | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO b/TODO index 0047829c51c..e30523252a0 100644 --- a/TODO +++ b/TODO @@ -394,7 +394,6 @@ Features: * make us use dynamically fewer deps for containers in general purpose distros: o turn into dlopen() deps: - - elfutils (always) - p11-kit-trust (always) - kmod-libs (only when called from PID 1) - libblkid (only in RootImage= handling in PID 1, but not elsewhere)