From b50d7345a2b9c1a71f4aa8db191cb02761d4fa2c Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sat, 28 Mar 2026 21:45:01 +0100 Subject: [PATCH 1/6] libsystemd: make check-pointer-deref clean Add the needed assert changes to make the code clean for the new check-pointer-deref script. --- meson.build | 1 - src/libsystemd/sd-bus/bus-creds.c | 1 + src/libsystemd/sd-bus/bus-error.c | 2 ++ src/libsystemd/sd-bus/bus-internal.c | 2 ++ src/libsystemd/sd-bus/bus-introspect.c | 2 ++ src/libsystemd/sd-bus/bus-objects.c | 4 ++++ src/libsystemd/sd-bus/bus-socket.c | 1 + src/libsystemd/sd-bus/sd-bus.c | 1 + src/libsystemd/sd-bus/test-bus-objects.c | 4 ++++ src/libsystemd/sd-bus/test-bus-vtable.c | 1 + src/libsystemd/sd-device/device-monitor.c | 4 ++++ src/libsystemd/sd-event/sd-event.c | 1 + src/libsystemd/sd-id128/id128-util.c | 2 ++ src/libsystemd/sd-journal/journal-file.c | 2 ++ src/libsystemd/sd-journal/journal-verify.c | 2 ++ src/libsystemd/sd-journal/test-journal-interleaving.c | 4 ++++ src/libsystemd/sd-netlink/netlink-message-rtnl.c | 2 ++ 17 files changed, 35 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index c9e96b2591..0e78ccaac8 100644 --- a/meson.build +++ b/meson.build @@ -2978,7 +2978,6 @@ if spatch.found() # Remove directories from this list as they are cleaned up. coccinelle_exclude = [ 'src/basic/', - 'src/libsystemd/', 'src/shared/', # libc/ has no assert() or systemd-headers so leave it 'src/libc/', diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 0812dc5324..0b2a402251 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -358,6 +358,7 @@ _public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *ret) { _public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) { assert_return(c, -EINVAL); + assert_return(ret, -EINVAL); if (!(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT)) return -ENODATA; diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index bc0164c1b4..be64e4a6c8 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -165,6 +165,8 @@ static int errno_to_bus_error_name_new(int error, char **ret) { const char *name; char *n; + assert(ret); + /* D-Bus names must not start with a digit. Thus, an name like System.Error.500 would not be legal. * Let's just return 0 if an unknown errno is encountered, which will cause the caller to fall back * to BUS_ERROR_FAILED. diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index 4bcebeedb2..3d28d88555 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -263,6 +263,8 @@ bool path_simple_pattern(const char *pattern, const char *value) { } int bus_message_type_from_string(const char *s, uint8_t *u) { + assert(u); + if (streq(s, "signal")) *u = SD_BUS_MESSAGE_SIGNAL; else if (streq(s, "method_call")) diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index cdc869ce04..d66c3a704d 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -175,6 +175,8 @@ static int introspect_write_arguments(BusIntrospect *i, const char *signature, c assert(i); assert(i->m.f); + assert(signature); + assert(names); for (;;) { size_t l; diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 42e2332001..83ba3a5239 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1704,6 +1704,10 @@ typedef enum { static bool names_are_valid(const char *signature, const char **names, names_flags *flags) { int r; + assert(signature); + assert(names); + assert(flags); + if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0') *flags |= NAMES_PRESENT; diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index fdbf557f13..3c5f596440 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -35,6 +35,7 @@ #define SNDBUF_SIZE (8*1024*1024) static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) { + assert(idx); while (size > 0) { struct iovec *i = iov + *idx; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 2a3ea8eb8f..27f788d995 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -2021,6 +2021,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { assert(bus); assert(m); + assert(idx); r = bus_socket_write_message(bus, m, idx); if (r <= 0) diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 62050b103d..4ad60f0d58 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -177,6 +177,7 @@ static const sd_bus_vtable vtable2[] = { }; static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *reterr_error) { + assert(nodes); if (object_path_startswith("/value", path)) ASSERT_NOT_NULL(*nodes = strv_new("/value/c", "/value/b", "/value/a")); @@ -185,6 +186,7 @@ static int enumerator_callback(sd_bus *bus, const char *path, void *userdata, ch } static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *reterr_error) { + assert(nodes); if (object_path_startswith("/value/a", path)) ASSERT_NOT_NULL(*nodes = strv_new("/value/a/z", "/value/a/x", "/value/a/y")); @@ -195,6 +197,8 @@ static int enumerator2_callback(sd_bus *bus, const char *path, void *userdata, c static int enumerator3_callback(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *reterr_error) { _cleanup_strv_free_ char **v = NULL; + assert(nodes); + if (!object_path_startswith("/value/b", path)) return 1; diff --git a/src/libsystemd/sd-bus/test-bus-vtable.c b/src/libsystemd/sd-bus/test-bus-vtable.c index ae2ead550f..84e75cd1e3 100644 --- a/src/libsystemd/sd-bus/test-bus-vtable.c +++ b/src/libsystemd/sd-bus/test-bus-vtable.c @@ -22,6 +22,7 @@ static struct context c = {}; static int happy_finder_object = 0; static int happy_finder(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *reterr_error) { + assert(found); assert(userdata); assert(userdata == &c); diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index 67d68bb07f..962329a865 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -783,6 +783,8 @@ int device_monitor_send( static void bpf_stmt(struct sock_filter *ins, unsigned *i, unsigned short code, unsigned data) { + assert(i); + ins[(*i)++] = (struct sock_filter) { .code = code, .k = data, @@ -792,6 +794,8 @@ static void bpf_stmt(struct sock_filter *ins, unsigned *i, static void bpf_jmp(struct sock_filter *ins, unsigned *i, unsigned short code, unsigned data, unsigned short jt, unsigned short jf) { + assert(i); + ins[(*i)++] = (struct sock_filter) { .code = code, .jt = jt, diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 6867385e92..ad82f308ba 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -5077,6 +5077,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) { _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { assert_return(e, -EINVAL); assert_return(e = event_resolve(e), -ENOPKG); + assert_return(ret, -EINVAL); assert_return(!event_origin_changed(e), -ECHILD); *ret = e->iteration; diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 9d406a45d1..700c9268ed 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -199,6 +199,8 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) { } void id128_hash_func(const sd_id128_t *p, struct siphash *state) { + assert(p); + siphash24_compress_typesafe(*p, state); } diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index b4efcc050e..363df258a1 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -2107,6 +2107,8 @@ static int link_entry_into_array( assert(f->header); assert(first); assert(idx); + POINTER_MAY_BE_NULL(tail); + POINTER_MAY_BE_NULL(tidx); assert(p > 0); a = tail ? le32toh(*tail) : le64toh(*first); diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c index 0afb664896..1b3bf3fca3 100644 --- a/src/libsystemd/sd-journal/journal-verify.c +++ b/src/libsystemd/sd-journal/journal-verify.c @@ -23,6 +23,8 @@ static void draw_progress(uint64_t p, usec_t *last_usec) { unsigned n, i, j, k; usec_t z, x; + assert(last_usec); + if (!on_tty()) return; diff --git a/src/libsystemd/sd-journal/test-journal-interleaving.c b/src/libsystemd/sd-journal/test-journal-interleaving.c index fbbaa82850..b0d7e80b11 100644 --- a/src/libsystemd/sd-journal/test-journal-interleaving.c +++ b/src/libsystemd/sd-journal/test-journal-interleaving.c @@ -591,6 +591,8 @@ TEST(sequence_numbers) { } static int expected_result(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) { + assert(ret); + switch (direction) { case DIRECTION_DOWN: for (size_t i = 0; i < n; i++) { @@ -625,6 +627,8 @@ static int expected_result(uint64_t needle, const uint64_t *candidates, const ui } static int expected_result_next(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) { + assert(ret); + switch (direction) { case DIRECTION_DOWN: for (size_t i = 0; i < n; i++) diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index f1871ceb82..af1d6fd3f5 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -385,6 +385,8 @@ int sd_rtnl_message_new_addr_update( int family) { int r; + assert_return(ret, -EINVAL); + r = sd_rtnl_message_new_addr(rtnl, ret, RTM_NEWADDR, ifindex, family); if (r < 0) return r; From 029335ea74e4404ae057414d8c41fd49843d6943 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sat, 28 Mar 2026 22:18:32 +0100 Subject: [PATCH 2/6] shared: make check-pointer-deref clean Add the needed assert changes to make the code clean for the new check-pointer-deref script. --- meson.build | 1 - src/shared/bus-unit-procs.c | 3 +++ src/shared/calendarspec.c | 18 ++++++++++++++++-- src/shared/conf-parser.c | 2 ++ src/shared/creds-util.c | 4 ++++ src/shared/dns-packet.c | 3 +++ src/shared/format-table.c | 2 ++ src/shared/import-util.c | 4 ++++ src/shared/install-printf.c | 2 ++ src/shared/install.c | 5 +++++ src/shared/journal-importer.c | 2 ++ src/shared/local-addresses.c | 4 ++++ src/shared/loop-util.c | 2 ++ src/shared/netif-util.c | 2 ++ src/shared/parse-argument.c | 2 ++ src/shared/pkcs11-util.c | 5 +++++ src/shared/pretty-print.c | 1 + src/shared/rm-rf.c | 2 ++ src/shared/socket-netlink.c | 2 ++ src/shared/specifier.c | 4 ++++ src/shared/tests.c | 2 ++ src/shared/tpm2-util.c | 2 ++ src/shared/vlan-util.c | 3 +++ 23 files changed, 74 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 0e78ccaac8..2eeb7fe859 100644 --- a/meson.build +++ b/meson.build @@ -2978,7 +2978,6 @@ if spatch.found() # Remove directories from this list as they are cleaned up. coccinelle_exclude = [ 'src/basic/', - 'src/shared/', # libc/ has no assert() or systemd-headers so leave it 'src/libc/', # test/ has some deliberate wonky pointers, just leave excluded diff --git a/src/shared/bus-unit-procs.c b/src/shared/bus-unit-procs.c index bd510efebd..97a800b4a0 100644 --- a/src/shared/bus-unit-procs.c +++ b/src/shared/bus-unit-procs.c @@ -134,6 +134,9 @@ static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { } static int cgroup_info_compare_func(struct CGroupInfo * const *a, struct CGroupInfo * const *b) { + assert(a); + assert(b); + return strcmp((*a)->cgroup_path, (*b)->cgroup_path); } diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index df371ac4f8..0f2d0c718c 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -39,6 +39,7 @@ static CalendarComponent* chain_free(CalendarComponent *c) { DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarComponent*, chain_free); CalendarSpec* calendar_spec_free(CalendarSpec *c) { + POINTER_MAY_BE_NULL(c); if (!c) return NULL; @@ -57,6 +58,9 @@ CalendarSpec* calendar_spec_free(CalendarSpec *c) { static int component_compare(CalendarComponent * const *a, CalendarComponent * const *b) { int r; + assert(a); + assert(b); + r = CMP((*a)->start, (*b)->start); if (r != 0) return r; @@ -487,6 +491,10 @@ static int parse_one_number(const char *p, const char **e, unsigned long *ret) { char *ee = NULL; unsigned long value; + assert(p); + assert(e); + assert(ret); + errno = 0; value = strtoul(p, &ee, 10); if (errno > 0) @@ -504,6 +512,9 @@ static int parse_component_decimal(const char **p, bool usec, int *res) { const char *e = NULL; int r; + assert(p); + assert(res); + if (!ascii_isdigit(**p)) return -EINVAL; @@ -569,6 +580,8 @@ static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { struct tm tm; int r; + assert(c); + if ((usec_t) time > USEC_INFINITY / USEC_PER_SEC) return -ERANGE; @@ -616,9 +629,8 @@ static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { static int prepend_component(const char **p, bool usec, unsigned nesting, CalendarComponent **c) { int r, start, stop = -1, repeat = 0; CalendarComponent *cc; - const char *e = *p; + const char *e = *ASSERT_PTR(p); - assert(p); assert(c); if (nesting > CALENDARSPEC_COMPONENTS_MAX) @@ -1218,6 +1230,8 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { struct tm t; int k; + assert(tm); + if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS) return true; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 1d1d8a10cc..690bb70ed5 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -918,6 +918,8 @@ static int _hashmap_by_section_find_unused_line( unsigned n = 0; void *entry; + assert(ret); + HASHMAP_BASE_FOREACH_KEY(entry, cs, entries_by_section) { if (filename && !streq(cs->filename, filename)) continue; diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 6fedc500dc..fc58b4af30 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -129,6 +129,8 @@ int open_credentials_dir(void) { int get_system_credentials_dir(const char **ret) { int r; + assert(ret); + /* Note that for system credentials the environment variable we honour is just for debugging purpose * (unlike for the per-service credential path env var where it's key part of the protocol). */ r = get_credentials_dir_internal("SYSTEMD_SYSTEM_CREDENTIALS_DIRECTORY", ret); @@ -142,6 +144,8 @@ int get_system_credentials_dir(const char **ret) { int get_encrypted_system_credentials_dir(const char **ret) { int r; + assert(ret); + r = get_credentials_dir_internal("SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY", ret); if (r >= 0 || r != -ENXIO) return r; diff --git a/src/shared/dns-packet.c b/src/shared/dns-packet.c index d333b255ee..70844c22f7 100644 --- a/src/shared/dns-packet.c +++ b/src/shared/dns-packet.c @@ -1468,6 +1468,7 @@ int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { int r; assert(p); + assert(ret); r = dns_packet_read(p, sizeof(uint8_t), &d, start); if (r < 0) @@ -1498,6 +1499,7 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { int r; assert(p); + assert(ret); r = dns_packet_read(p, sizeof(uint32_t), &d, start); if (r < 0) @@ -2404,6 +2406,7 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { * a reply). */ assert(rr); + assert(rfc6975); assert(rr->key->type == DNS_TYPE_OPT); /* Check that the version is 0 */ diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 04552b5b56..eb1866e8d4 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -1543,6 +1543,8 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t static int table_data_compare(const size_t *a, const size_t *b, Table *t) { int r; + assert(a); + assert(b); assert(t); assert(t->sort_map); diff --git a/src/shared/import-util.c b/src/shared/import-util.c index 35079a6537..8bc90003b2 100644 --- a/src/shared/import-util.c +++ b/src/shared/import-util.c @@ -142,6 +142,8 @@ int tar_strip_suffixes(const char *name, char **ret) { const char *e; char *s; + assert(ret); + e = endswith(name, ".tar"); if (!e) e = endswith(name, ".tar.xz"); @@ -183,6 +185,8 @@ int raw_strip_suffixes(const char *name, char **ret) { _cleanup_free_ char *q = NULL; + assert(ret); + q = strdup(name); if (!q) return -ENOMEM; diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 81a5e4a73e..ccb73f061b 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -49,6 +49,8 @@ static int specifier_instance(char specifier, const void *data, const char *root char *instance; int r; + assert(ret); + r = unit_name_to_instance(i->name, &instance); if (r < 0) return r; diff --git a/src/shared/install.c b/src/shared/install.c index b8e364011e..2398224176 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -646,6 +646,7 @@ static int mark_symlink_for_removal( char *n; int r; + assert(remove_symlinks_to); assert(p); r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops_free); @@ -1037,6 +1038,7 @@ static int find_symlinks_in_scope( assert(lp); assert(info); + assert(state); /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name" * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are @@ -1842,6 +1844,8 @@ static int install_info_discover_and_check( int r; + POINTER_MAY_BE_NULL(ret); + r = install_info_discover(ctx, lp, name_or_path, flags, ret, changes, n_changes); if (r < 0) return r; @@ -3510,6 +3514,7 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char assert(name); assert(presets); + POINTER_MAY_BE_NULL(instance_name_list); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; diff --git a/src/shared/journal-importer.c b/src/shared/journal-importer.c index 3aa472a2d1..87286a33b2 100644 --- a/src/shared/journal-importer.c +++ b/src/shared/journal-importer.c @@ -50,6 +50,8 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) { char *c = NULL; assert(imp); + assert(line); + assert(size); assert(imp->state == IMPORTER_STATE_LINE); assert(imp->offset <= imp->filled); assert(imp->filled <= MALLOC_SIZEOF_SAFE(imp->buf)); diff --git a/src/shared/local-addresses.c b/src/shared/local-addresses.c index c1c07c9c5f..7e0843e8d3 100644 --- a/src/shared/local-addresses.c +++ b/src/shared/local-addresses.c @@ -53,6 +53,9 @@ bool has_local_address(const struct local_address *addresses, size_t n_addresses static void suppress_duplicates(struct local_address *list, size_t *n_list) { size_t old_size, new_size; + POINTER_MAY_BE_NULL(list); + assert(n_list); + /* Removes duplicate entries, assumes the list of addresses is already sorted. Updates in-place. */ if (*n_list < 2) /* list with less than two entries can't have duplicates */ @@ -88,6 +91,7 @@ static int add_local_address_full( assert(ifindex > 0); assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); + POINTER_MAY_BE_NULL(prefsrc); if (!GREEDY_REALLOC(*list, *n_list + 1)) return -ENOMEM; diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 8c92429648..210f60e32f 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -33,6 +33,8 @@ #include "time-util.h" static void cleanup_clear_loop_close(int *fd) { + assert(fd); + if (*fd < 0) return; diff --git a/src/shared/netif-util.c b/src/shared/netif-util.c index 74daf1facc..0281fd0313 100644 --- a/src/shared/netif-util.c +++ b/src/shared/netif-util.c @@ -40,6 +40,8 @@ int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) { const char *t; char *p; + assert(ret); + if (device && sd_device_get_devtype(device, &t) >= 0 && !isempty(t)) diff --git a/src/shared/parse-argument.c b/src/shared/parse-argument.c index 39e5328e70..0abae6547c 100644 --- a/src/shared/parse-argument.c +++ b/src/shared/parse-argument.c @@ -79,6 +79,8 @@ int parse_path_argument(const char *path, bool suppress_root, char **arg) { char *p; int r; + assert(arg); + /* * This function is intended to be used in command line parsers, to handle paths that are passed * in. It makes the path absolute, and reduces it to NULL if omitted or root (the latter optionally). diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 1fa0d77d6d..fb709cef11 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -874,6 +874,8 @@ int pkcs11_token_find_related_object( CK_OBJECT_HANDLE objects[2]; CK_RV rv; + assert(ret_object); + rv = m->C_GetAttributeValue(session, prototype, attributes, ELEMENTSOF(attributes)); if (!IN_SET(rv, CKR_OK, CKR_ATTRIBUTE_TYPE_INVALID)) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve length of attributes: %s", sym_p11_kit_strerror(rv)); @@ -1072,6 +1074,9 @@ static int pkcs11_token_decrypt_data_ecc( int r; #endif + assert(ret_decrypted_data); + assert(ret_decrypted_data_size); + rv = m->C_GetSessionInfo(session, &session_info); if (rv != CKR_OK) return log_error_errno(SYNTHETIC_ERRNO(EIO), diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index 7e04acbcfc..6899618051 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -100,6 +100,7 @@ int terminal_urlify(const char *url, const char *text, char **ret) { char *n; assert(url); + assert(ret); /* Takes a URL and a pretty string and formats it as clickable link for the terminal. See * https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda for details. */ diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c index f375d25428..8c179bbaca 100644 --- a/src/shared/rm-rf.c +++ b/src/shared/rm-rf.c @@ -268,6 +268,8 @@ typedef struct TodoEntry { } TodoEntry; static void free_todo_entries(TodoEntry **todos) { + assert(todos); + for (TodoEntry *x = *todos; x && x->dir; x++) { closedir(x->dir); free(x->dirname); diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 885606b6e0..ca9c259b26 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -98,6 +98,8 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s) { SocketAddress b; int r; + assert(a); + /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ r = socket_address_parse(&b, s); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 62b036a74e..5951603522 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -139,6 +139,8 @@ int specifier_id128(char specifier, const void *data, const char *root, const vo const sd_id128_t *id = ASSERT_PTR(data); char *n; + assert(ret); + n = new(char, SD_ID128_STRING_MAX); if (!n) return -ENOMEM; @@ -151,6 +153,8 @@ int specifier_uuid(char specifier, const void *data, const char *root, const voi const sd_id128_t *id = ASSERT_PTR(data); char *n; + assert(ret); + n = new(char, SD_ID128_UUID_STRING_MAX); if (!n) return -ENOMEM; diff --git a/src/shared/tests.c b/src/shared/tests.c index 55464c3553..8111d481e4 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -71,6 +71,8 @@ int get_testdata_dir(const char *suffix, char **ret) { const char *dir; char *p; + assert(ret); + load_testdata_env(); /* if the env var is set, use that */ diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index dc062117b4..5dd1fba78f 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -5038,6 +5038,8 @@ static int tpm2_calculate_seal_public( int r; assert(parent); + POINTER_MAY_BE_NULL(attributes); + POINTER_MAY_BE_NULL(policy); assert(seed); assert(secret); assert(ret); diff --git a/src/shared/vlan-util.c b/src/shared/vlan-util.c index 2343acb346..cf931dfae7 100644 --- a/src/shared/vlan-util.c +++ b/src/shared/vlan-util.c @@ -26,6 +26,9 @@ int parse_vid_range(const char *p, uint16_t *vid, uint16_t *vid_end) { unsigned lower, upper; int r; + assert(vid); + assert(vid_end); + r = parse_range(p, &lower, &upper); if (r < 0) return r; From 1692d51eb231d95b767b890b47c7277b6597cdf4 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sat, 28 Mar 2026 22:39:03 +0100 Subject: [PATCH 3/6] shared,sysext,analyze: remove unneeded if() for vars already guarded There are some unnecessary() if() checks for variables that are already guarded by assert()s. Drop those. Thanks to keszybz for clarifying the direction of this commit. --- src/analyze/analyze-security.c | 3 +-- src/shared/condition.c | 8 +++----- src/shared/openssl-util.c | 6 ++---- src/sysext/sysext.c | 3 +-- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index 5e9db877b7..c1b2948e6c 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -577,8 +577,7 @@ static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilter if (set_contains(s, syscall) == allow_list) { log_debug("Offending syscall filter item: %s", syscall); - if (ret_offending_syscall) - *ret_offending_syscall = syscall; + *ret_offending_syscall = syscall; return true; /* bad! */ } } diff --git a/src/shared/condition.c b/src/shared/condition.c index dd720c55bd..af5093a6a3 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -82,11 +82,9 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger .negate = negate, }; - if (parameter) { - c->parameter = strdup(parameter); - if (!c->parameter) - return mfree(c); - } + c->parameter = strdup(parameter); + if (!c->parameter) + return mfree(c); return c; } diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 9e0b998746..b512fcef94 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -1339,8 +1339,7 @@ static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s", ERR_error_string(ERR_get_error(), NULL)); - if (ret) - *ret = TAKE_PTR(pk); + *ret = TAKE_PTR(pk); return 0; } @@ -1404,8 +1403,7 @@ static int load_x509_certificate_from_file(const char *path, X509 **ret) { return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", ERR_error_string(ERR_get_error(), NULL)); - if (ret) - *ret = TAKE_PTR(cert); + *ret = TAKE_PTR(cert); return 0; } diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 20f0ceb3f0..52e6c3be66 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -2437,8 +2437,7 @@ static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **re return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name); } - if (ret_images) - *ret_images = TAKE_PTR(images); + *ret_images = TAKE_PTR(images); return 0; } From 18e70eec2fb2f543c829ceb627410fd52fb0c858 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sun, 29 Mar 2026 11:52:01 +0200 Subject: [PATCH 4/6] basic: make check-pointer-deref clean Add the needed assert changes to make the code clean for the new check-pointer-deref script. Note that for `_cmp()` functions its a bit of a question if we want `assert()` or `POINTER_MAY_BE_NULL()` here. The former is more correct but it adds a (small) runtime hit. If we use POINTER_MAY_BE_NULL() it is compiled out but strictly speaking it is wrong (so at least we would need a comment). --- meson.build | 3 --- src/basic/alloc-util.c | 2 ++ src/basic/devnum-util.c | 2 ++ src/basic/escape.c | 1 + src/basic/ether-addr-util.c | 5 +++++ src/basic/extract-word.c | 3 +++ src/basic/fd-util.c | 1 + src/basic/glob-util.c | 6 ++++++ src/basic/gunicode.c | 2 ++ src/basic/hash-funcs.c | 10 ++++++++++ src/basic/hashmap.c | 10 ++++++++++ src/basic/in-addr-util.c | 2 ++ src/basic/io-util.c | 2 ++ src/basic/log.c | 3 +++ src/basic/mountpoint-util.c | 5 +++++ src/basic/ordered-set.c | 4 ++++ src/basic/parse-util.c | 21 +++++++++++++++++++++ src/basic/path-util.c | 7 +++++++ src/basic/prioq.c | 2 ++ src/basic/process-util.c | 5 +++++ src/basic/recurse-dir.c | 3 +++ src/basic/rlimit-util.c | 3 +++ src/basic/socket-util.c | 5 +++++ src/basic/sort-util.c | 6 ++++++ src/basic/string-table.c | 2 ++ src/basic/string-util.c | 10 ++++++++++ src/basic/strv.c | 14 ++++++++++++++ src/basic/uid-classification.c | 2 ++ src/basic/unit-def.c | 2 ++ src/basic/utf8.c | 6 ++++++ 30 files changed, 146 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 2eeb7fe859..f38696e328 100644 --- a/meson.build +++ b/meson.build @@ -2974,10 +2974,7 @@ endif spatch = find_program('spatch', required : false) if spatch.found() - # Directories excluded from coccinelle checks until their warnings are fixed. - # Remove directories from this list as they are cleaned up. coccinelle_exclude = [ - 'src/basic/', # libc/ has no assert() or systemd-headers so leave it 'src/libc/', # test/ has some deliberate wonky pointers, just leave excluded diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c index 1eb98c1199..3a813575c3 100644 --- a/src/basic/alloc-util.c +++ b/src/basic/alloc-util.c @@ -35,6 +35,8 @@ void* memdup_suffix0(const void *p, size_t l) { } size_t malloc_sizeof_safe(void **xp) { + POINTER_MAY_BE_NULL(xp); + if (_unlikely_(!xp || !*xp)) return 0; diff --git a/src/basic/devnum-util.c b/src/basic/devnum-util.c index 99c20662df..92ba078dc5 100644 --- a/src/basic/devnum-util.c +++ b/src/basic/devnum-util.c @@ -18,6 +18,8 @@ int parse_devnum(const char *s, dev_t *ret) { size_t n; int r; + assert(ret); + n = strspn(s, DIGITS); if (n == 0) return -EINVAL; diff --git a/src/basic/escape.c b/src/basic/escape.c index e1771bf432..881f813793 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -106,6 +106,7 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, assert(p); assert(ret); + assert(eight_bit); /* Unescapes C style. Returns the unescaped character in ret. * Sets *eight_bit to true if the escaped sequence either fits in diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index 375c044415..d5b733457d 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -91,10 +91,15 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR } int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { + assert(a); + assert(b); + return memcmp(a, b, ETH_ALEN); } static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) { + assert(p); + siphash24_compress_typesafe(*p, state); } diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 99de8740d1..5ebf67437c 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -204,6 +204,9 @@ int extract_first_word_and_warn( const char *save; int r; + assert(p); + assert(ret); + save = *p; r = extract_first_word(p, ret, separators, flags); if (r >= 0) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 6fb4e78c2f..65459f9aa3 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -868,6 +868,7 @@ int fd_reopen_condition( assert(fd >= 0); assert(!FLAGS_SET(flags, O_CREAT)); + assert(ret_new_fd); /* Invokes fd_reopen(fd, flags), but only if the existing F_GETFL flags don't match the specified * flags (masked by the specified mask). This is useful for converting O_PATH fds into real fds if diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c index a2b2edd210..eadfbb0221 100644 --- a/src/basic/glob-util.c +++ b/src/basic/glob-util.c @@ -11,6 +11,9 @@ #ifndef __GLIBC__ static bool safe_glob_verify(const char *p, const char *prefix) { + POINTER_MAY_BE_NULL(p); + POINTER_MAY_BE_NULL(prefix); + if (isempty(p)) return false; /* should not happen, but for safey. */ @@ -162,6 +165,9 @@ int glob_extend(char ***strv, const char *path, int flags) { } int glob_non_glob_prefix(const char *path, char **ret) { + assert(path); + assert(ret); + /* Return the path of the path that has no glob characters. */ size_t n = strcspn(path, GLOB_CHARS); diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c index 5b5e06c025..7c33a7c872 100644 --- a/src/basic/gunicode.c +++ b/src/basic/gunicode.c @@ -27,6 +27,8 @@ char * utf8_prev_char (const char *p) { + assert(p); + for (;;) { p--; diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index 5c0af6fe5a..d4adc98e94 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -103,10 +103,15 @@ DEFINE_HASH_OPS_FULL( void, free); void uint64_hash_func(const uint64_t *p, struct siphash *state) { + assert(p); + siphash24_compress_typesafe(*p, state); } int uint64_compare_func(const uint64_t *a, const uint64_t *b) { + assert(a); + assert(b); + return CMP(*a, *b); } @@ -119,6 +124,8 @@ DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( #if SIZEOF_DEV_T != 8 void devt_hash_func(const dev_t *p, struct siphash *state) { + assert(p); + siphash24_compress_typesafe(*p, state); } #endif @@ -126,6 +133,9 @@ void devt_hash_func(const dev_t *p, struct siphash *state) { int devt_compare_func(const dev_t *a, const dev_t *b) { int r; + assert(a); + assert(b); + r = CMP(major(*a), major(*b)); if (r != 0) return r; diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 30c0517a57..1d3ee1d9e7 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -848,6 +848,8 @@ int set_ensure_allocated(Set **s, const struct hash_ops *hash_ops) { int hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) { int r; + assert(h); + r = hashmap_ensure_allocated(h, hash_ops); if (r < 0) return r; @@ -858,6 +860,8 @@ int hashmap_ensure_put(Hashmap **h, const struct hash_ops *hash_ops, const void int ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) { int r; + assert(h); + r = ordered_hashmap_ensure_allocated(h, hash_ops); if (r < 0) return r; @@ -868,6 +872,8 @@ int ordered_hashmap_ensure_put(OrderedHashmap **h, const struct hash_ops *hash_o int ordered_hashmap_ensure_replace(OrderedHashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) { int r; + assert(h); + r = ordered_hashmap_ensure_allocated(h, hash_ops); if (r < 0) return r; @@ -878,6 +884,8 @@ int ordered_hashmap_ensure_replace(OrderedHashmap **h, const struct hash_ops *ha int hashmap_ensure_replace(Hashmap **h, const struct hash_ops *hash_ops, const void *key, void *value) { int r; + assert(h); + r = hashmap_ensure_allocated(h, hash_ops); if (r < 0) return r; @@ -1283,6 +1291,8 @@ int set_put(Set *s, const void *key) { int set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key) { int r; + assert(s); + r = set_ensure_allocated(s, hash_ops); if (r < 0) return r; diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 957ce2a466..fedf631f7d 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -841,6 +841,8 @@ int in_addr_parse_prefixlen(int family, const char *p, unsigned char *ret) { uint8_t u; int r; + assert(ret); + if (!IN_SET(family, AF_INET, AF_INET6)) return -EAFNOSUPPORT; diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 0b54464cc4..103aa2a7cd 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -248,6 +248,8 @@ int fd_wait_for_event(int fd, int event, usec_t timeout) { static size_t nul_length(const uint8_t *p, size_t sz) { size_t n = 0; + assert(p); + while (sz > 0) { if (*p != 0) break; diff --git a/src/basic/log.c b/src/basic/log.c index b0b4ba5c50..473d0bd70f 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -950,6 +950,9 @@ int log_format_iovec( const char *format, va_list ap) { + assert(iovec); + assert(n); + while (format && *n + 1 < iovec_len) { va_list aq; char *m; diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 79b51cb099..c09ac7bf84 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -320,6 +320,8 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) { uint64_t mnt_id; int r; + assert(ret); + r = path_get_mnt_id_at_internal(dir_fd, path, /* unique = */ false, &mnt_id); if (r < 0) return r; @@ -597,6 +599,9 @@ const char* mount_propagation_flag_to_string(unsigned long flags) { int mount_propagation_flag_from_string(const char *name, unsigned long *ret) { + POINTER_MAY_BE_NULL(name); + assert(ret); + if (isempty(name)) *ret = 0; else if (streq(name, "shared")) diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c index 09fdc3dfce..f5ed009826 100644 --- a/src/basic/ordered-set.c +++ b/src/basic/ordered-set.c @@ -9,6 +9,8 @@ #include "strv.h" int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) { + assert(s); + if (*s) return 0; @@ -22,6 +24,8 @@ int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) { int ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p) { int r; + assert(s); + r = ordered_set_ensure_allocated(s, ops); if (r < 0) return r; diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 3ccdb0001d..f882124a0e 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -127,6 +127,8 @@ int parse_mtu(int family, const char *s, uint32_t *ret) { uint64_t u, m; int r; + assert(ret); + r = parse_size(s, 1024, &u); if (r < 0) return r; @@ -376,6 +378,9 @@ int parse_user_shell(const char *s, char **ret_sh, bool *ret_copy) { char *sh; int r; + assert(ret_sh); + assert(ret_copy); + if (path_is_absolute(s) && path_is_normalized(s)) { sh = strdup(s); if (!sh) @@ -476,6 +481,8 @@ int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) unsigned v; int r; + assert(ret); + r = safe_atou(s, &v); if (r < 0) return r; @@ -577,6 +584,8 @@ int safe_atou8_full(const char *s, unsigned base, uint8_t *ret) { unsigned u; int r; + assert(ret); + r = safe_atou_full(s, base, &u); if (r < 0) return r; @@ -591,6 +600,8 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { unsigned u; int r; + assert(ret); + r = safe_atou_full(s, base, &u); if (r < 0) return r; @@ -654,6 +665,9 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { unsigned val = 0; const char *s; + assert(p); + assert(res); + s = *p; /* accept any number of digits, strtoull is limited to 19 */ @@ -688,6 +702,8 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { int parse_nice(const char *s, int *ret) { int n, r; + assert(ret); + r = safe_atoi(s, &n); if (r < 0) return r; @@ -703,6 +719,8 @@ int parse_ip_port(const char *s, uint16_t *ret) { uint16_t l; int r; + assert(ret); + r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l); if (r < 0) return r; @@ -719,6 +737,9 @@ int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow unsigned l, h; int r; + assert(low); + assert(high); + r = parse_range(s, &l, &h); if (r < 0) return r; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 8d60365767..5b499fb6cd 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -587,6 +587,8 @@ char* path_extend_internal(char **x, ...) { va_list ap; bool slash; + POINTER_MAY_BE_NULL(x); + /* Joins all listed strings until the sentinel and places a "/" between them unless the strings * end/begin already with one so that it is unnecessary. Note that slashes which are already * duplicate won't be removed. The string returned is hence always equal to or longer than the sum of @@ -830,6 +832,8 @@ int fsck_exists_for_fstype(const char *fstype) { } static const char* skip_slash_or_dot(const char *p) { + POINTER_MAY_BE_NULL(p); + for (; !isempty(p); p++) { if (*p == '/') continue; @@ -933,6 +937,9 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * const char *q, *last_end, *last_begin; size_t len; + POINTER_MAY_BE_NULL(next); + POINTER_MAY_BE_NULL(ret); + /* Similar to path_find_first_component(), but search components from the end. * * Examples diff --git a/src/basic/prioq.c b/src/basic/prioq.c index 157c081eb4..94ee528556 100644 --- a/src/basic/prioq.c +++ b/src/basic/prioq.c @@ -168,6 +168,8 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) { int _prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx) { int r; + assert(q); + r = prioq_ensure_allocated(q, compare_func); if (r < 0) return r; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index e3d6b3905d..697498ea5d 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1151,6 +1151,8 @@ int safe_personality(unsigned long p) { int opinionated_personality(unsigned long *ret) { int current; + assert(ret); + /* Returns the current personality, or PERSONALITY_INVALID if we can't determine it. This function is a bit * opinionated though, and ignores all the finer-grained bits and exotic personalities, only distinguishing the * two most relevant personalities: PER_LINUX and PER_LINUX32. */ @@ -1191,6 +1193,9 @@ void valgrind_summary_hack(void) { int pid_compare_func(const pid_t *a, const pid_t *b) { /* Suitable for usage in qsort() */ + assert(a); + assert(b); + return CMP(*a, *b); } diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index bc3c32afe6..1bd8231966 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -16,6 +16,9 @@ #define DEFAULT_RECURSION_MAX 100 static int sort_func(struct dirent * const *a, struct dirent * const *b) { + assert(a); + assert(b); + return strcmp((*a)->d_name, (*b)->d_name); } diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index ba1af81bb4..6dbc5008b1 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -170,6 +170,9 @@ static int rlimit_parse_nice(const char *val, rlim_t *ret) { uint64_t rl; int r; + assert(val); + assert(ret); + /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index eab09786b6..698aa69a4b 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -360,6 +360,7 @@ int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ assert(sa); + assert(ret_port); switch (sa->sa.sa_family) { @@ -824,6 +825,8 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { bool address_label_valid(const char *p) { + POINTER_MAY_BE_NULL(p); + if (isempty(p)) return false; @@ -1556,6 +1559,8 @@ int socket_set_option(int fd, int af, int opt_ipv4, int opt_ipv6, int val) { int socket_get_mtu(int fd, int af, size_t *ret) { int mtu, r; + assert(ret); + if (af == AF_UNSPEC) { af = socket_get_family(fd); if (af < 0) diff --git a/src/basic/sort-util.c b/src/basic/sort-util.c index d848745677..2859f14376 100644 --- a/src/basic/sort-util.c +++ b/src/basic/sort-util.c @@ -65,9 +65,15 @@ void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_ } int cmp_int(const int *a, const int *b) { + assert(a); + assert(b); + return CMP(*a, *b); } int cmp_uint16(const uint16_t *a, const uint16_t *b) { + assert(a); + assert(b); + return CMP(*a, *b); } diff --git a/src/basic/string-table.c b/src/basic/string-table.c index 461f94f9ee..66cf4bae11 100644 --- a/src/basic/string-table.c +++ b/src/basic/string-table.c @@ -41,6 +41,8 @@ ssize_t string_table_lookup_from_string_with_boolean(const char * const *table, int string_table_lookup_to_string_fallback(const char * const *table, size_t len, ssize_t i, size_t max, char **ret) { char *s; + assert(ret); + if (i < 0 || i > (ssize_t) max) return -ERANGE; diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 055e571afb..303603baa3 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -174,6 +174,9 @@ char* ascii_strlower_n(char *s, size_t n) { int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { + assert(a); + assert(b); + for (; n > 0; a++, b++, n--) { int x, y; @@ -276,6 +279,10 @@ static bool string_has_ansi_sequence(const char *s, size_t len) { } static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) { + + assert(s); + assert(ret_where); + /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */ for (size_t i = length - 2; i > 0; i--) { /* -2 because at least two bytes are needed */ @@ -1133,6 +1140,7 @@ int string_truncate_lines(const char *s, size_t n_lines, char **ret) { size_t n = 0; assert(s); + assert(ret); /* Truncate after the specified number of lines. Returns > 0 if a truncation was applied or == 0 if * there were fewer lines in the string anyway. Trailing newlines on input are ignored, and not @@ -1187,6 +1195,8 @@ int string_extract_line(const char *s, size_t i, char **ret) { const char *p = s; size_t c = 0; + assert(ret); + /* Extract the i'nth line from the specified string. Returns > 0 if there are more lines after that, * and == 0 if we are looking at the last line or already beyond the last line. As special * optimization, if the first line is requested and the string only consists of one line we return diff --git a/src/basic/strv.c b/src/basic/strv.c index 6cbc9633ae..5e4d46bde4 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -587,6 +587,9 @@ int strv_push_with_size(char ***l, size_t *n, char *value) { * If n is not NULL, the size after the push will be returned. * If value is empty, no action is taken and *n is not set. */ + assert(l); + POINTER_MAY_BE_NULL(n); + if (!value) return 0; @@ -615,6 +618,8 @@ int strv_push_pair(char ***l, char *a, char *b) { char **c; size_t n; + assert(l); + if (!a && !b) return 0; @@ -828,6 +833,9 @@ char** strv_remove(char **l, const char *s) { } bool strv_overlap(char * const *a, char * const *b) { + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); + STRV_FOREACH(i, a) if (strv_contains(b, *i)) return true; @@ -836,6 +844,9 @@ bool strv_overlap(char * const *a, char * const *b) { } static int str_compare(char * const *a, char * const *b) { + assert(a); + assert(b); + return strcmp(*a, *b); } @@ -862,6 +873,9 @@ char** strv_sort_uniq(char **l) { int strv_compare(char * const *a, char * const *b) { int r; + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); + if (strv_isempty(a)) { if (strv_isempty(b)) return 0; diff --git a/src/basic/uid-classification.c b/src/basic/uid-classification.c index 7a45fc5750..364bb75997 100644 --- a/src/basic/uid-classification.c +++ b/src/basic/uid-classification.c @@ -25,6 +25,8 @@ static int parse_alloc_uid(const char *path, const char *name, const char *t, ui uid_t uid; int r; + assert(ret_uid); + r = parse_uid(t, &uid); if (r < 0) return log_debug_errno(r, "%s: failed to parse %s %s, ignoring: %m", path, name, t); diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index ea4eebbf5d..a89a81c703 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -24,6 +24,8 @@ int unit_name_from_dbus_path(const char *path, char **name) { const char *e; char *n; + assert(name); + e = startswith(path, "/org/freedesktop/systemd1/unit/"); if (!e) return -EINVAL; diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 8a0cfc012e..c527908b26 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -63,6 +63,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { size_t len; assert(str); + assert(ret_unichar); len = utf8_encoded_expected_len(str[0]); @@ -500,6 +501,8 @@ size_t char16_strlen(const char16_t *s) { } size_t char16_strsize(const char16_t *s) { + POINTER_MAY_BE_NULL(s); + return s ? (char16_strlen(s) + 1) * sizeof(*s) : 0; } @@ -566,6 +569,8 @@ int utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { size_t utf8_n_codepoints(const char *str) { size_t n = 0; + assert(str); + /* Returns the number of UTF-8 codepoints in this string, or SIZE_MAX if the string is not valid UTF-8. */ while (*str != 0) { @@ -583,6 +588,7 @@ size_t utf8_n_codepoints(const char *str) { } size_t utf8_console_width(const char *str) { + POINTER_MAY_BE_NULL(str); if (isempty(str)) return 0; From bb94af45c1e8fd87127ac875a09a8efa80df4fb6 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Sun, 29 Mar 2026 12:46:28 +0200 Subject: [PATCH 5/6] many: use POINTER_MAY_BE_NULL in inner sort _cmp funcs Some functions are in the hot path of a sort. While it is more correct to also use assert() here for the passed pointers it is also a runtime cost that should never be needed when e.g. qsort() is used as it will always pass non-NULL pointers. So for these critical functions use POINTER_MAY_BE_NULL() which is compiled out and add a comment to them. Not all _compare() functions are changed, it was a bit of a judgement call because some (like ether_addr_compare) are not called with that much data. But happy to get advise here and tweak accordingly. --- src/basic/sort-util.c | 12 ++++++++---- src/basic/strv.c | 6 ++++-- src/core/job.c | 8 ++++---- src/shared/format-table.c | 6 ++++-- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/basic/sort-util.c b/src/basic/sort-util.c index 2859f14376..a76a6c85a8 100644 --- a/src/basic/sort-util.c +++ b/src/basic/sort-util.c @@ -65,15 +65,19 @@ void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_ } int cmp_int(const int *a, const int *b) { - assert(a); - assert(b); + /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we + just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */ + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); return CMP(*a, *b); } int cmp_uint16(const uint16_t *a, const uint16_t *b) { - assert(a); - assert(b); + /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we + just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */ + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); return CMP(*a, *b); } diff --git a/src/basic/strv.c b/src/basic/strv.c index 5e4d46bde4..4044a129c6 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -844,8 +844,10 @@ bool strv_overlap(char * const *a, char * const *b) { } static int str_compare(char * const *a, char * const *b) { - assert(a); - assert(b); + /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we + just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */ + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); return strcmp(*a, *b); } diff --git a/src/core/job.c b/src/core/job.c index 638e6e759e..7dd8a4617d 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -1525,10 +1525,10 @@ void job_add_to_gc_queue(Job *j) { } static int job_compare_id(Job * const *a, Job * const *b) { - assert(a); - assert(b); - assert(*a); - assert(*b); + /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we + just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */ + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); return CMP((*a)->id, (*b)->id); } diff --git a/src/shared/format-table.c b/src/shared/format-table.c index eb1866e8d4..55e19ebd70 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -1543,8 +1543,10 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t static int table_data_compare(const size_t *a, const size_t *b, Table *t) { int r; - assert(a); - assert(b); + /* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we + just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */ + POINTER_MAY_BE_NULL(a); + POINTER_MAY_BE_NULL(b); assert(t); assert(t->sort_map); From dbc530390970cb3f07dbc9d97710d11276e88394 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 31 Mar 2026 17:40:03 +0200 Subject: [PATCH 6/6] shared: make dns_packet_read_uint{8,32} accept NULL ret The shared/ dns_packet_read_uint*() functions used to be a bit inconsistent when it comes to the handling of NULL in *ret. The helpers dns_packet_read_{uint16,name} accepted NULL but the dns_packet_read_uint{8,32} did not. This commit makes them all symmetric in accepting NULL. --- src/shared/dns-packet.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/shared/dns-packet.c b/src/shared/dns-packet.c index 70844c22f7..f8078c1211 100644 --- a/src/shared/dns-packet.c +++ b/src/shared/dns-packet.c @@ -1468,13 +1468,14 @@ int dns_packet_read_uint8(DnsPacket *p, uint8_t *ret, size_t *start) { int r; assert(p); - assert(ret); r = dns_packet_read(p, sizeof(uint8_t), &d, start); if (r < 0) return r; - *ret = ((uint8_t*) d)[0]; + if (ret) + *ret = ((uint8_t*) d)[0]; + return 0; } @@ -1499,13 +1500,13 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { int r; assert(p); - assert(ret); r = dns_packet_read(p, sizeof(uint32_t), &d, start); if (r < 0) return r; - *ret = unaligned_read_be32(d); + if (ret) + *ret = unaligned_read_be32(d); return 0; }