From f392dfb5a1286184189233a84f6d6871bd4f7ade Mon Sep 17 00:00:00 2001 From: Frantisek Sumsal Date: Wed, 24 May 2023 13:29:52 +0200 Subject: [PATCH] tree-wide: check memstream buffer after closing the handle When closing the FILE handle attached to a memstream, it may attempt to do a realloc() that may fail during OOM situations, in which case we are left with the buffer pointer pointing to NULL and buffer size > 0. For example: ``` #include #include #include void *realloc(void *ptr, size_t size) { return NULL; } int main(int argc, char *argv[]) { FILE *f; char *buf; size_t sz = 0; f = open_memstream(&buf, &sz); if (!f) return -ENOMEM; fputs("Hello", f); fflush(f); printf("buf: 0x%lx, sz: %lu, errno: %d\n", (unsigned long) buf, sz, errno); fclose(f); printf("buf: 0x%lx, sz: %lu, errno: %d\n", (unsigned long) buf, sz, errno); return 0; } ``` ``` $ gcc -o main main.c $ ./main buf: 0x74d4a0, sz: 5, errno: 0 buf: 0x0, sz: 5, errno: 0 ``` This might do unexpected things if the underlying code expects a valid pointer to the memstream buffer after closing the handle. Found by Nallocfuzz. --- src/basic/string-util.c | 17 ++--- src/busctl/busctl.c | 3 + src/core/manager-dump.c | 3 + src/coredump/coredump.c | 3 + src/journal/journalctl.c | 4 ++ src/libsystemd/sd-bus/bus-introspect.c | 4 ++ src/libsystemd/sd-bus/bus-match.c | 7 ++- src/network/generator/network-generator.c | 75 ++++++++++++----------- src/oom/oomd-manager.c | 3 + src/oom/oomd-util.c | 5 ++ src/resolve/resolved-dns-dnssec.c | 6 +- src/resolve/resolved-manager.c | 5 ++ src/shared/bus-util.c | 3 + src/shared/calendarspec.c | 18 +++--- src/shared/elf-util.c | 10 +++ src/shared/format-table.c | 3 + src/shared/json.c | 29 ++++----- 17 files changed, 130 insertions(+), 68 deletions(-) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index c74ee67dfec..4d02ad51933 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "escape.h" #include "extract-word.h" +#include "fd-util.h" #include "fileio.h" #include "gunicode.h" #include "locale-util.h" @@ -603,9 +604,9 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { STATE_CSI, STATE_CSO, } state = STATE_OTHER; - char *obuf = NULL; + _cleanup_free_ char *obuf = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0; - FILE *f; assert(ibuf); assert(*ibuf); @@ -713,11 +714,13 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { } } - if (fflush_and_check(f) < 0) { - fclose(f); - return mfree(obuf); - } - fclose(f); + if (fflush_and_check(f) < 0) + return NULL; + + f = safe_fclose(f); + + if (!obuf) + return NULL; free_and_replace(*ibuf, obuf); diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 965ded9675e..4d69aee5eb7 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -1072,6 +1072,9 @@ static int introspect(int argc, char **argv, void *userdata) { mf = safe_fclose(mf); + if (!buf) + return bus_log_parse_error(ENOMEM); + z = set_get(members, &((Member) { .type = "property", .interface = m->interface, diff --git a/src/core/manager-dump.c b/src/core/manager-dump.c index 5a92356d487..35143ebddf4 100644 --- a/src/core/manager-dump.c +++ b/src/core/manager-dump.c @@ -95,6 +95,9 @@ int manager_get_dump_string(Manager *m, char **patterns, char **ret) { f = safe_fclose(f); + if (!dump) + return -ENOMEM; + *ret = TAKE_PTR(dump); return 0; diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 5fdcfa74372..a6b0d96488a 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -707,6 +707,9 @@ static int compose_open_fds(pid_t pid, char **open_fds) { if (errno > 0) return -errno; + if (!buffer) + return -ENOMEM; + *open_fds = TAKE_PTR(buffer); return 0; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 5f3a121ac9f..e379203d5d7 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1811,6 +1811,10 @@ static int format_journal_url( return r; f = safe_fclose(f); + + if (!url) + return -ENOMEM; + *ret_url = TAKE_PTR(url); return 0; } diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index 49236a8e129..3d4c47c6dd7 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -268,6 +268,10 @@ int introspect_finish(struct introspect *i, char **ret) { return r; i->f = safe_fclose(i->f); + + if (!i->introspection) + return -ENOMEM; + *ret = TAKE_PTR(i->introspection); return 0; diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 703b9ac038b..cc043abfe34 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -824,6 +824,7 @@ int bus_match_parse( char *bus_match_to_string(struct bus_match_component *components, size_t n_components) { _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t size = 0; int r; @@ -832,7 +833,7 @@ char *bus_match_to_string(struct bus_match_component *components, size_t n_compo assert(components); - FILE *f = open_memstream_unlocked(&buffer, &size); + f = open_memstream_unlocked(&buffer, &size); if (!f) return NULL; @@ -855,9 +856,11 @@ char *bus_match_to_string(struct bus_match_component *components, size_t n_compo } r = fflush_and_check(f); - safe_fclose(f); if (r < 0) return NULL; + + f = safe_fclose(f); + return TAKE_PTR(buffer); } diff --git a/src/network/generator/network-generator.c b/src/network/generator/network-generator.c index 1090934bfc1..569dcdf5114 100644 --- a/src/network/generator/network-generator.c +++ b/src/network/generator/network-generator.c @@ -1199,30 +1199,31 @@ void link_dump(Link *link, FILE *f) { int network_format(Network *network, char **ret) { _cleanup_free_ char *s = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t sz = 0; int r; assert(network); assert(ret); - { - _cleanup_fclose_ FILE *f = NULL; - - f = open_memstream_unlocked(&s, &sz); - if (!f) - return -ENOMEM; + f = open_memstream_unlocked(&s, &sz); + if (!f) + return -ENOMEM; - network_dump(network, f); + network_dump(network, f); - /* Add terminating 0, so that the output buffer is a valid string. */ - fputc('\0', f); + /* Add terminating 0, so that the output buffer is a valid string. */ + fputc('\0', f); - r = fflush_and_check(f); - } + r = fflush_and_check(f); if (r < 0) return r; - assert(s); + f = safe_fclose(f); + + if (!s) + return -ENOMEM; + *ret = TAKE_PTR(s); assert(sz > 0); return (int) sz - 1; @@ -1230,30 +1231,31 @@ int network_format(Network *network, char **ret) { int netdev_format(NetDev *netdev, char **ret) { _cleanup_free_ char *s = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t sz = 0; int r; assert(netdev); assert(ret); - { - _cleanup_fclose_ FILE *f = NULL; - - f = open_memstream_unlocked(&s, &sz); - if (!f) - return -ENOMEM; + f = open_memstream_unlocked(&s, &sz); + if (!f) + return -ENOMEM; - netdev_dump(netdev, f); + netdev_dump(netdev, f); - /* Add terminating 0, so that the output buffer is a valid string. */ - fputc('\0', f); + /* Add terminating 0, so that the output buffer is a valid string. */ + fputc('\0', f); - r = fflush_and_check(f); - } + r = fflush_and_check(f); if (r < 0) return r; - assert(s); + f = safe_fclose(f); + + if (!s) + return -ENOMEM; + *ret = TAKE_PTR(s); assert(sz > 0); return (int) sz - 1; @@ -1261,30 +1263,31 @@ int netdev_format(NetDev *netdev, char **ret) { int link_format(Link *link, char **ret) { _cleanup_free_ char *s = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t sz = 0; int r; assert(link); assert(ret); - { - _cleanup_fclose_ FILE *f = NULL; - - f = open_memstream_unlocked(&s, &sz); - if (!f) - return -ENOMEM; + f = open_memstream_unlocked(&s, &sz); + if (!f) + return -ENOMEM; - link_dump(link, f); + link_dump(link, f); - /* Add terminating 0, so that the output buffer is a valid string. */ - fputc('\0', f); + /* Add terminating 0, so that the output buffer is a valid string. */ + fputc('\0', f); - r = fflush_and_check(f); - } + r = fflush_and_check(f); if (r < 0) return r; - assert(s); + f = safe_fclose(f); + + if (!s) + return -ENOMEM; + *ret = TAKE_PTR(s); assert(sz > 0); return (int) sz - 1; diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c index 08a29ec77bd..3f050cdbb29 100644 --- a/src/oom/oomd-manager.c +++ b/src/oom/oomd-manager.c @@ -847,6 +847,9 @@ int manager_get_dump_string(Manager *m, char **ret) { f = safe_fclose(f); + if (!dump) + return -ENOMEM; + *ret = TAKE_PTR(dump); return 0; } diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c index 49c10b5e16c..6309d2c4739 100644 --- a/src/oom/oomd-util.c +++ b/src/oom/oomd-util.c @@ -299,6 +299,11 @@ static int dump_kill_candidates(OomdCGroupContext **sorted, int n, int dump_unti if (r < 0) return r; + f = safe_fclose(f); + + if (!dump) + return -ENOMEM; + return log_dump(LOG_INFO, dump); } diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index fc076856b60..e7c18c29a0b 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -867,10 +867,14 @@ static int dnssec_rrset_serialize_sig( } r = fflush_and_check(f); - f = safe_fclose(f); /* sig_data may be reallocated when f is closed. */ if (r < 0) return r; + f = safe_fclose(f); /* sig_data may be reallocated when f is closed. */ + + if (!sig_data) + return -ENOMEM; + *ret_sig_data = TAKE_PTR(sig_data); *ret_sig_size = sig_size; return 0; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 184d8e3f3d3..67786d39fdb 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -519,6 +519,11 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si if (fflush_and_check(f) < 0) return log_oom(); + f = safe_fclose(f); + + if (!buffer) + return -ENOMEM; + log_dump(LOG_INFO, buffer); return 0; } diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index ca0d77c1367..b99883a088e 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -628,6 +628,9 @@ static int method_dump_memory_state_by_fd(sd_bus_message *message, void *userdat dump_file = safe_fclose(dump_file); + if (!dump) + return -ENOMEM; + fd = acquire_data_fd(dump, dump_size, 0); if (fd < 0) return fd; diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 86a6d3f6080..5690e3144aa 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "calendarspec.h" #include "errno-util.h" +#include "fd-util.h" #include "fileio.h" #include "macro.h" #include "parse-util.h" @@ -337,9 +338,9 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c, bool us } int calendar_spec_to_string(const CalendarSpec *c, char **p) { - char *buf = NULL; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t sz = 0; - FILE *f; int r; assert(c); @@ -384,14 +385,15 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { } r = fflush_and_check(f); - fclose(f); - - if (r < 0) { - free(buf); + if (r < 0) return r; - } - *p = buf; + f = safe_fclose(f); + + if (!buf) + return -ENOMEM; + + *p = TAKE_PTR(buf); return 0; } diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 98c47d91250..5885215a1ca 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -621,8 +621,13 @@ static int parse_core(int fd, const char *executable, char **ret, JsonVariant ** return log_warning_errno(r, "Could not parse core file, flushing file buffer failed: %m"); c.f = safe_fclose(c.f); + + if (!buf) + return log_oom(); + *ret = TAKE_PTR(buf); } + if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(package_metadata); @@ -735,8 +740,13 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r return log_warning_errno(r, "Could not parse ELF file, flushing file buffer failed: %m"); c.f = safe_fclose(c.f); + + if (!buf) + return log_oom(); + *ret = TAKE_PTR(buf); } + if (ret_package_metadata) *ret_package_metadata = TAKE_PTR(elf_metadata); diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 204e8b68b6d..83b749d677a 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -2537,6 +2537,9 @@ int table_format(Table *t, char **ret) { f = safe_fclose(f); + if (!buf) + return -ENOMEM; + *ret = TAKE_PTR(buf); return 0; diff --git a/src/shared/json.c b/src/shared/json.c index 73050b55c85..5bf00f009fc 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -1769,6 +1769,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) { _cleanup_free_ char *s = NULL; + _cleanup_fclose_ FILE *f = NULL; size_t sz = 0; int r; @@ -1781,26 +1782,26 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) { if (flags & JSON_FORMAT_OFF) return -ENOEXEC; - { - _cleanup_fclose_ FILE *f = NULL; - - f = open_memstream_unlocked(&s, &sz); - if (!f) - return -ENOMEM; + f = open_memstream_unlocked(&s, &sz); + if (!f) + return -ENOMEM; - r = json_variant_dump(v, flags, f, NULL); - if (r < 0) - return r; + r = json_variant_dump(v, flags, f, NULL); + if (r < 0) + return r; - /* Add terminating 0, so that the output buffer is a valid string. */ - fputc('\0', f); + /* Add terminating 0, so that the output buffer is a valid string. */ + fputc('\0', f); - r = fflush_and_check(f); - } + r = fflush_and_check(f); if (r < 0) return r; - assert(s); + f = safe_fclose(f); + + if (!s) + return -ENOMEM; + *ret = TAKE_PTR(s); assert(sz > 0); return (int) sz - 1; -- 2.47.3