From: Michael Vogt Date: Sun, 29 Mar 2026 09:52:01 +0000 (+0200) Subject: basic: make check-pointer-deref clean X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=18e70eec2fb2f543c829ceb627410fd52fb0c858;p=thirdparty%2Fsystemd.git 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). --- diff --git a/meson.build b/meson.build index 2eeb7fe8592..f38696e3286 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 1eb98c1199d..3a813575c3a 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 99c20662df6..92ba078dc55 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 e1771bf4322..881f813793a 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 375c0444157..d5b733457d2 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 99de8740d13..5ebf67437c2 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 6fb4e78c2f6..65459f9aa38 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 a2b2edd2108..eadfbb02215 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 5b5e06c0253..7c33a7c8725 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 5c0af6fe5a3..d4adc98e945 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 30c0517a57c..1d3ee1d9e79 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 957ce2a4665..fedf631f7d8 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 0b54464cc40..103aa2a7cde 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 b0b4ba5c505..473d0bd70f5 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 79b51cb099f..c09ac7bf84f 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 09fdc3dfcee..f5ed0098262 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 3ccdb0001d1..f882124a0e6 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 8d60365767f..5b499fb6cd3 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 157c081eb45..94ee528556a 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 e3d6b3905d4..697498ea5d4 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 bc3c32afe69..1bd82319663 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 ba1af81bb42..6dbc5008b18 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 eab09786b67..698aa69a4b0 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 d848745677d..2859f14376e 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 461f94f9ee3..66cf4bae114 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 055e571afb6..303603baa33 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 6cbc9633ae1..5e4d46bde4d 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 7a45fc57504..364bb759973 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 ea4eebbf5d3..a89a81c703a 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 8a0cfc012ec..c527908b264 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;