From 3aa0cf23b4854d3982e5dc01d9fb4e72703304f8 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 15 Feb 2025 23:02:52 +0100 Subject: [PATCH 1/5] macro-fundamental: add _nonnull_if_nonzero_ Introduced in gcc 15: https://github.com/gcc-mirror/gcc/commit/19fe55c4801de50deee03b333e94d007aae222e3 --- src/fundamental/macro-fundamental.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 5eb31cd742..78a87d150b 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -112,6 +112,12 @@ # define _fallthrough_ __attribute__((__fallthrough__)) #endif +#if __GNUC__ >= 15 +# define _nonnull_if_nonzero_(p, n) __attribute__((nonnull_if_nonzero(p, n))) +#else +# define _nonnull_if_nonzero_(p, n) +#endif + #define XSTRINGIFY(x) #x #define STRINGIFY(x) XSTRINGIFY(x) From c7a097d7e1d108e77103ea97d9f4c8c62a5ebb48 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 15 Feb 2025 23:35:54 +0100 Subject: [PATCH 2/5] basic/escape: octescape() doesn't really take 'bad' param --- src/basic/escape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic/escape.c b/src/basic/escape.c index e50ae68cc6..0ab99b5a0b 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -449,7 +449,7 @@ char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFl char* octescape(const char *s, size_t len) { char *buf, *t; - /* Escapes all chars in bad, in addition to \ and " chars, in \nnn style escaping. */ + /* Escapes \ and " chars, in \nnn style escaping. */ assert(s || len == 0); From 7de7c7b6dce69b25b8c504224473c7ede5007354 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 15 Feb 2025 23:25:14 +0100 Subject: [PATCH 3/5] basic/escape: accept SIZE_MAX and perform overflow check in cescape_length() and decescape() too While at it, make cescape() static inline. --- src/basic/escape.c | 21 +++++++++++++-------- src/basic/escape.h | 6 ++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/basic/escape.c b/src/basic/escape.c index 0ab99b5a0b..81d15a67cd 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -80,10 +80,15 @@ char* cescape_length(const char *s, size_t n) { const char *f; char *r, *t; + /* Does C style string escaping. May be reversed with cunescape(). */ + assert(s || n == 0); - /* Does C style string escaping. May be reversed with - * cunescape(). */ + if (n == SIZE_MAX) + n = strlen(s); + + if (n > (SIZE_MAX - 1) / 4) + return NULL; r = new(char, n*4 + 1); if (!r) @@ -97,12 +102,6 @@ char* cescape_length(const char *s, size_t n) { return r; } -char* cescape(const char *s) { - assert(s); - - return cescape_length(s, strlen(s)); -} - int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) { int r = 1; @@ -486,6 +485,12 @@ char* decescape(const char *s, const char *bad, size_t len) { assert(s || len == 0); + if (len == SIZE_MAX) + len = strlen(s); + + if (len > (SIZE_MAX - 1) / 4) + return NULL; + t = buf = new(char, len * 4 + 1); if (!buf) return NULL; diff --git a/src/basic/escape.h b/src/basic/escape.h index 65caf0dbcf..e922b6693b 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -41,9 +41,11 @@ typedef enum ShellEscapeFlags { SHELL_ESCAPE_EMPTY = 1 << 2, /* Format empty arguments as "". */ } ShellEscapeFlags; -char* cescape(const char *s); -char* cescape_length(const char *s, size_t n); int cescape_char(char c, char *buf); +char* cescape_length(const char *s, size_t n); +static inline char* cescape(const char *s) { + return cescape_length(s, SIZE_MAX); +} int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul); From 6bdbfb7eb6f929bf02602b11f103b1d767828272 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 15 Feb 2025 23:43:11 +0100 Subject: [PATCH 4/5] basic/escape: swap 'bad' and 'len' params for decescape() To follow our usual coding style. --- src/basic/escape.c | 2 +- src/basic/escape.h | 2 +- src/resolve/resolved-dns-rr.c | 2 +- src/test/test-escape.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/basic/escape.c b/src/basic/escape.c index 81d15a67cd..e1ded55981 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -478,7 +478,7 @@ char* octescape(const char *s, size_t len) { return buf; } -char* decescape(const char *s, const char *bad, size_t len) { +char* decescape(const char *s, size_t len, const char *bad) { char *buf, *t; /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */ diff --git a/src/basic/escape.h b/src/basic/escape.h index e922b6693b..46ccaf78ec 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -67,7 +67,7 @@ static inline char* xescape(const char *s, const char *bad) { return xescape_full(s, bad, SIZE_MAX, 0); } char* octescape(const char *s, size_t len); -char* decescape(const char *s, const char *bad, size_t len); +char* decescape(const char *s, size_t len, const char *bad); char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags); char* shell_escape(const char *s, const char *bad); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index bbd0aef1c8..7f0898af6b 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -931,7 +931,7 @@ static char *format_svc_param_value(DnsSvcParam *i) { return strv_join(values_strv, ","); } default: { - value = decescape((char *)&i->value, " ,", i->length); + value = decescape((char*) &i->value, i->length, " ,"); if (!value) return NULL; break; diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 7021ff54d2..e4dfe32c04 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -242,7 +242,7 @@ TEST(octescape) { static void test_decescape_one(const char *s, const char *bad, const char *expected) { _cleanup_free_ char *ret = NULL; - assert_se(ret = decescape(s, bad, strlen_ptr(s))); + assert_se(ret = decescape(s, s ? SIZE_MAX : 0, bad)); log_debug("decescape(\"%s\") → \"%s\" (expected: \"%s\")", strnull(s), ret, expected); ASSERT_STREQ(ret, expected); } From 6da2ea9fa95bd697f354c2402961f579d34e08f0 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 15 Feb 2025 23:45:54 +0100 Subject: [PATCH 5/5] basic: use _nonnull_if_nonzero_ where appropriate --- src/basic/efivars.h | 2 +- src/basic/escape.h | 4 ++-- src/basic/hexdecoct.h | 14 +++++++------- src/basic/io-util.h | 4 ++-- src/basic/iovec-util.h | 8 ++++---- src/basic/memfd-util.h | 4 +++- src/basic/memory-util.c | 2 +- src/basic/memory-util.h | 12 ++++++------ src/basic/random-util.h | 8 +++++--- src/basic/string-util.h | 4 ++-- src/basic/strv.h | 2 +- 11 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/basic/efivars.h b/src/basic/efivars.h index f9167a2ee6..5fbfb1fe97 100644 --- a/src/basic/efivars.h +++ b/src/basic/efivars.h @@ -46,7 +46,7 @@ int efi_get_variable(const char *variable, uint32_t *attribute, void **ret_value, size_t *ret_size); int efi_get_variable_string(const char *variable, char **ret); int efi_get_variable_path(const char *variable, char **ret); -int efi_set_variable(const char *variable, const void *value, size_t size); +int efi_set_variable(const char *variable, const void *value, size_t size) _nonnull_if_nonzero_(2, 3); int efi_set_variable_string(const char *variable, const char *p); bool is_efi_boot(void); diff --git a/src/basic/escape.h b/src/basic/escape.h index 46ccaf78ec..224a795aa4 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -42,7 +42,7 @@ typedef enum ShellEscapeFlags { } ShellEscapeFlags; int cescape_char(char c, char *buf); -char* cescape_length(const char *s, size_t n); +char* cescape_length(const char *s, size_t n) _nonnull_if_nonzero_(1, 2); static inline char* cescape(const char *s) { return cescape_length(s, SIZE_MAX); } @@ -67,7 +67,7 @@ static inline char* xescape(const char *s, const char *bad) { return xescape_full(s, bad, SIZE_MAX, 0); } char* octescape(const char *s, size_t len); -char* decescape(const char *s, size_t len, const char *bad); +char* decescape(const char *s, size_t len, const char *bad) _nonnull_if_nonzero_(1, 2); char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags); char* shell_escape(const char *s, const char *bad); diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index d160ca28c9..b456200e1b 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -17,8 +17,8 @@ int undecchar(char c) _const_; char hexchar(int x) _const_; int unhexchar(char c) _const_; -char* hexmem(const void *p, size_t l); -int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size); +char* hexmem(const void *p, size_t l) _nonnull_if_nonzero_(1, 2); +int unhexmem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2); static inline int unhexmem(const char *p, void **ret_data, size_t *ret_size) { return unhexmem_full(p, SIZE_MAX, false, ret_data, ret_size); } @@ -30,10 +30,10 @@ char base64char(int x) _const_; char urlsafe_base64char(int x) _const_; int unbase64char(char c) _const_; -char* base32hexmem(const void *p, size_t l, bool padding); -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); +char* base32hexmem(const void *p, size_t l, bool padding) _nonnull_if_nonzero_(1, 2); +int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len) _nonnull_if_nonzero_(1, 2); -ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret); +ssize_t base64mem_full(const void *p, size_t l, size_t line_break, char **ret) _nonnull_if_nonzero_(1, 2); static inline ssize_t base64mem(const void *p, size_t l, char **ret) { return base64mem_full(p, l, SIZE_MAX, ret); } @@ -45,9 +45,9 @@ ssize_t base64_append( size_t l, size_t margin, size_t width); -int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size); +int unbase64mem_full(const char *p, size_t l, bool secure, void **ret_data, size_t *ret_size) _nonnull_if_nonzero_(1, 2); static inline int unbase64mem(const char *p, void **ret_data, size_t *ret_size) { return unbase64mem_full(p, SIZE_MAX, false, ret_data, ret_size); } -void hexdump(FILE *f, const void *p, size_t s); +void hexdump(FILE *f, const void *p, size_t s) _nonnull_if_nonzero_(2, 3); diff --git a/src/basic/io-util.h b/src/basic/io-util.h index df24e85b06..11a8e28295 100644 --- a/src/basic/io-util.h +++ b/src/basic/io-util.h @@ -15,14 +15,14 @@ int flush_fd(int fd); ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout); +int loop_write_full(int fd, const void *buf, size_t nbytes, usec_t timeout) _nonnull_if_nonzero_(2, 3); static inline int loop_write(int fd, const void *buf, size_t nbytes) { return loop_write_full(fd, buf, nbytes, 0); } int pipe_eof(int fd); -int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout); +int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) _nonnull_if_nonzero_(1, 2); int fd_wait_for_event(int fd, int event, usec_t timeout); ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 868454040b..ace20098c8 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -12,9 +12,9 @@ extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */ extern const struct iovec iovec_empty; /* Points to an empty, but valid (i.e. non-NULL) pointer */ -size_t iovec_total_size(const struct iovec *iovec, size_t n); +size_t iovec_total_size(const struct iovec *iovec, size_t n) _nonnull_if_nonzero_(1, 2); -bool iovec_increment(struct iovec *iovec, size_t n, size_t k); +bool iovec_increment(struct iovec *iovec, size_t n, size_t k) _nonnull_if_nonzero_(1, 2); static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) { assert(iovec); @@ -42,7 +42,7 @@ static inline void iovec_done_erase(struct iovec *iovec) { char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); -void iovec_array_free(struct iovec *iovec, size_t n_iovec); +void iovec_array_free(struct iovec *iovec, size_t n_iovec) _nonnull_if_nonzero_(1, 2); static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) { @@ -55,7 +55,7 @@ static inline int iovec_memcmp(const struct iovec *a, const struct iovec *b) { b ? b->iov_len : 0); } -static inline struct iovec *iovec_memdup(const struct iovec *source, struct iovec *ret) { +static inline struct iovec* iovec_memdup(const struct iovec *source, struct iovec *ret) { assert(ret); if (!iovec_is_set(source)) diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h index 077ca6f67b..9052ff2971 100644 --- a/src/basic/memfd-util.h +++ b/src/basic/memfd-util.h @@ -6,6 +6,8 @@ #include #include +#include "macro.h" + int memfd_create_wrapper(const char *name, unsigned mode); int memfd_new_full(const char *name, unsigned extra_flags); @@ -13,7 +15,7 @@ static inline int memfd_new(const char *name) { return memfd_new_full(name, 0); } -int memfd_new_and_seal(const char *name, const void *data, size_t sz); +int memfd_new_and_seal(const char *name, const void *data, size_t sz) _nonnull_if_nonzero_(2, 3); static inline int memfd_new_and_seal_string(const char *name, const char *s) { return memfd_new_and_seal(name, s, SIZE_MAX); } diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c index ed6024fa4a..46ff4074a4 100644 --- a/src/basic/memory-util.c +++ b/src/basic/memory-util.c @@ -40,7 +40,7 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) { return memcmp(data, p + 16, length) == 0; } -void *memdup_reverse(const void *mem, size_t size) { +void* memdup_reverse(const void *mem, size_t size) { assert(mem); assert(size != 0); diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 1f604cc452..04de0ab1c1 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -59,19 +59,19 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2 #define zero(x) (memzero(&(x), sizeof(x))) -bool memeqbyte(uint8_t byte, const void *data, size_t length); +bool memeqbyte(uint8_t byte, const void *data, size_t length) _nonnull_if_nonzero_(2, 3); #define memeqzero(data, length) memeqbyte(0x00, data, length) #define eqzero(x) memeqzero(x, sizeof(x)) -static inline void *mempset(void *s, int c, size_t n) { +static inline void* mempset(void *s, int c, size_t n) { memset(s, c, n); - return (uint8_t*)s + n; + return (uint8_t*) s + n; } /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ -static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { +static inline void* memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { if (needlelen <= 0) return (void*) haystack; @@ -85,7 +85,7 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { +static inline void* mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { const uint8_t *p; p = memmem_safe(haystack, haystacklen, needle, needlelen); @@ -116,4 +116,4 @@ static inline void erase_char(char *p) { } /* Makes a copy of the buffer with reversed order of bytes */ -void *memdup_reverse(const void *mem, size_t size); +void* memdup_reverse(const void *mem, size_t size); diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 0b5ba77190..587ca1c283 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -6,8 +6,10 @@ #include #include -void random_bytes(void *p, size_t n); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */ -int crypto_random_bytes(void *p, size_t n); /* Returns secure random bytes after waiting for the RNG to initialize. */ +#include "macro.h" + +void random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns random bytes suitable for most uses, but may be insecure sometimes. */ +int crypto_random_bytes(void *p, size_t n) _nonnull_if_nonzero_(1, 2); /* Returns secure random bytes after waiting for the RNG to initialize. */ int crypto_random_bytes_allocate_iovec(size_t n, struct iovec *ret); static inline uint64_t random_u64(void) { @@ -29,6 +31,6 @@ static inline uint32_t random_u32(void) { size_t random_pool_size(void); -int random_write_entropy(int fd, const void *seed, size_t size, bool credit); +int random_write_entropy(int fd, const void *seed, size_t size, bool credit) _nonnull_if_nonzero_(2, 3); uint64_t random_u64_range(uint64_t max); diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 8a5df741cc..85f206c689 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -107,7 +107,7 @@ static inline const char* empty_or_dash_to_null(const char *p) { char* first_word(const char *s, const char *word) _pure_; char* strprepend(char **x, const char *s); -char* strextendn(char **x, const char *s, size_t l); +char* strextendn(char **x, const char *s, size_t l) _nonnull_if_nonzero_(2, 3); #define strjoin(a, ...) strextend_with_separator_internal(NULL, NULL, a, __VA_ARGS__, NULL) @@ -223,7 +223,7 @@ static inline int free_and_strdup_warn(char **p, const char *s) { return log_oom(); return r; } -int free_and_strndup(char **p, const char *s, size_t l); +int free_and_strndup(char **p, const char *s, size_t l) _nonnull_if_nonzero_(2, 3); int strdup_to_full(char **ret, const char *src); static inline int strdup_to(char **ret, const char *src) { diff --git a/src/basic/strv.h b/src/basic/strv.h index 5cdc801f35..529afc386d 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -33,7 +33,7 @@ char** strv_free_erase(char **l); DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) -void strv_free_many(char ***strvs, size_t n); +void strv_free_many(char ***strvs, size_t n) _nonnull_if_nonzero_(1, 2); char** strv_copy_n(char * const *l, size_t n); static inline char** strv_copy(char * const *l) {