diff options
41 files changed, 677 insertions, 577 deletions
diff --git a/src/systemd/src/basic/alloc-util.h b/src/systemd/src/basic/alloc-util.h index ebe42889ea..2a6deb12ca 100644 --- a/src/systemd/src/basic/alloc-util.h +++ b/src/systemd/src/basic/alloc-util.h @@ -46,6 +46,21 @@ static inline void *mfree(void *memory) { void* memdup(const void *p, size_t l) _alloc_(2); void* memdup_suffix0(const void *p, size_t l) _alloc_(2); +#define memdupa(p, l) \ + ({ \ + void *_q_; \ + _q_ = alloca(l); \ + memcpy(_q_, p, l); \ + }) + +#define memdupa_suffix0(p, l) \ + ({ \ + void *_q_; \ + _q_ = alloca(l + 1); \ + ((uint8_t*) _q_)[l] = 0; \ + memcpy(_q_, p, l); \ + }) + static inline void freep(void *p) { free(*(void**) p); } diff --git a/src/systemd/src/basic/env-util.c b/src/systemd/src/basic/env-util.c index 3b8130b03e..e494f65c98 100644 --- a/src/systemd/src/basic/env-util.c +++ b/src/systemd/src/basic/env-util.c @@ -21,10 +21,6 @@ DIGITS LETTERS \ "_" -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - static bool env_name_is_valid_n(const char *e, size_t n) { const char *p; @@ -42,7 +38,7 @@ static bool env_name_is_valid_n(const char *e, size_t n) { * either. Discounting the equal sign and trailing NUL this * hence leaves ARG_MAX-2 as longest possible variable * name. */ - if (n > ARG_MAX - 2) + if (n > (size_t) sysconf(_SC_ARG_MAX) - 2) return false; for (p = e; p < e + n; p++) @@ -76,7 +72,7 @@ bool env_value_is_valid(const char *e) { * either. Discounting the shortest possible variable name of * length 1, the equal sign and trailing NUL this hence leaves * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > ARG_MAX - 3) + if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3) return false; return true; @@ -99,7 +95,7 @@ bool env_assignment_is_valid(const char *e) { * be > ARG_MAX, hence the individual variable assignments * cannot be either, but let's leave room for one trailing NUL * byte. */ - if (strlen(e) > ARG_MAX - 1) + if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1) return false; return true; @@ -125,30 +121,28 @@ bool strv_env_is_valid(char **e) { } bool strv_env_name_is_valid(char **l) { - char **p, **q; + char **p; STRV_FOREACH(p, l) { if (!env_name_is_valid(*p)) return false; - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; + if (strv_contains(p + 1, *p)) + return false; } return true; } bool strv_env_name_or_assignment_is_valid(char **l) { - char **p, **q; + char **p; STRV_FOREACH(p, l) { if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) return false; - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; + if (strv_contains(p + 1, *p)) + return false; } return true; @@ -157,20 +151,23 @@ bool strv_env_name_or_assignment_is_valid(char **l) { static int env_append(char **r, char ***k, char **a) { assert(r); assert(k); + assert(*k >= r); if (!a) return 0; - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ + /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k' + * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv. + * + * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it. + * + * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */ for (; *a; a++) { - char **j; + char **j, *c; size_t n; n = strcspn(*a, "="); - if ((*a)[n] == '=') n++; @@ -178,24 +175,26 @@ static int env_append(char **r, char ***k, char **a) { if (strneq(*j, *a, n)) break; - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) + c = strdup(*a); + if (!c) return -ENOMEM; + + if (j >= *k) { /* Append to the end? */ + (*k)[0] = c; + (*k)[1] = NULL; + (*k)++; + } else + free_and_replace(*j, c); /* Override existing item */ } return 0; } char **strv_env_merge(size_t n_lists, ...) { - size_t n = 0; - char **l, **k, **r; + _cleanup_strv_free_ char **ret = NULL; + size_t n = 0, i; + char **l, **k; va_list ap; - size_t i; /* Merges an arbitrary number of environment sets */ @@ -206,29 +205,24 @@ char **strv_env_merge(size_t n_lists, ...) { } va_end(ap); - r = new(char*, n+1); - if (!r) + ret = new(char*, n+1); + if (!ret) return NULL; - k = r; + *ret = NULL; + k = ret; va_start(ap, n_lists); for (i = 0; i < n_lists; i++) { l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; + if (env_append(ret, &k, l) < 0) { + va_end(ap); + return NULL; + } } va_end(ap); - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; + return TAKE_PTR(ret); } static bool env_match(const char *t, const char *pattern) { @@ -382,22 +376,23 @@ char **strv_env_unset_many(char **l, ...) { } int strv_env_replace(char ***l, char *p) { - char **f; const char *t, *name; + char **f; + int r; assert(p); - /* Replace first occurrence of the env var or add a new one in the - * string list. Drop other occurrences. Edits in-place. Does not copy p. - * p must be a valid key=value assignment. + /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits + * in-place. Does not copy p. p must be a valid key=value assignment. */ t = strchr(p, '='); - assert(t); + if (!t) + return -EINVAL; name = strndupa(p, t - p); - for (f = *l; f && *f; f++) + STRV_FOREACH(f, *l) if (env_entry_has_name(*f, name)) { free_and_replace(*f, p); strv_env_unset(f + 1, *f); @@ -405,33 +400,40 @@ int strv_env_replace(char ***l, char *p) { } /* We didn't find a match, we need to append p or create a new strv */ - if (strv_push(l, p) < 0) - return -ENOMEM; + r = strv_push(l, p); + if (r < 0) + return r; + return 1; } char **strv_env_set(char **x, const char *p) { + _cleanup_strv_free_ char **ret = NULL; + size_t n, m; char **k; - _cleanup_strv_free_ char **r = NULL; - char* m[2] = { (char*) p, NULL }; /* Overrides the env var setting of p, returns a new copy */ - r = new(char*, strv_length(x)+2); - if (!r) + n = strv_length(x); + m = n + 2; + if (m < n) /* overflow? */ return NULL; - k = r; - if (env_append(r, &k, x) < 0) + ret = new(char*, m); + if (!ret) return NULL; - if (env_append(r, &k, m) < 0) + *ret = NULL; + k = ret; + + if (env_append(ret, &k, x) < 0) return NULL; - *k = NULL; + if (env_append(ret, &k, STRV_MAKE(p)) < 0) + return NULL; - return TAKE_PTR(r); + return TAKE_PTR(ret); } char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { @@ -750,36 +752,3 @@ int getenv_bool_secure(const char *p) { return parse_boolean(e); } - -int serialize_environment(FILE *f, char **environment) { - char **e; - - STRV_FOREACH(e, environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", ce); - } - - /* caller should call ferror() */ - - return 0; -} - -int deserialize_environment(char ***environment, const char *line) { - char *uce; - int r; - - assert(line); - assert(environment); - - assert(startswith(line, "env=")); - r = cunescape(line + 4, 0, &uce); - if (r < 0) - return r; - - return strv_env_replace(environment, uce); -} diff --git a/src/systemd/src/basic/env-util.h b/src/systemd/src/basic/env-util.h index 174433ea91..4d21ea6bef 100644 --- a/src/systemd/src/basic/env-util.h +++ b/src/systemd/src/basic/env-util.h @@ -45,6 +45,3 @@ char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); int getenv_bool_secure(const char *p); - -int serialize_environment(FILE *f, char **environment); -int deserialize_environment(char ***environment, const char *line); diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index b97bd191ab..5775a13e3e 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -353,22 +353,22 @@ bool fdname_is_valid(const char *s) { } int fd_get_path(int fd, char **ret) { - _cleanup_close_ int dir = -1; - char fdname[DECIMAL_STR_MAX(int)]; + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; int r; - dir = open("/proc/self/fd/", O_CLOEXEC | O_DIRECTORY | O_PATH); - if (dir < 0) - /* /proc is not available or not set up properly, we're most likely - * in some chroot environment. */ - return errno == ENOENT ? -EOPNOTSUPP : -errno; + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + r = readlink_malloc(procfs_path, ret); + if (r == -ENOENT) { + /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make + * things debuggable and distuingish the two. */ - xsprintf(fdname, "%i", fd); + if (access("/proc/self/fd/", F_OK) < 0) + /* /proc is not available or not set up properly, we're most likely in some chroot + * environment. */ + return errno == ENOENT ? -EOPNOTSUPP : -errno; - r = readlinkat_malloc(dir, fdname, ret); - if (r == -ENOENT) - /* If the file doesn't exist the fd is invalid */ - return -EBADF; + return -EBADF; /* The directory exists, hence it's the fd that doesn't. */ + } return r; } diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index d2fd4c47b3..f14afa5dce 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -1221,6 +1221,24 @@ int mkostemp_safe(char *pattern) { return fd; } +int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) { + int fd; + FILE *f; + + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + f = fdopen(fd, mode); + if (!f) { + safe_close(fd); + return -errno; + } + + *ret_f = f; + return 0; +} + int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h index 77e6206e95..102d33d75f 100644 --- a/src/systemd/src/basic/fileio.h +++ b/src/systemd/src/basic/fileio.h @@ -59,20 +59,12 @@ DIR *xopendirat(int dirfd, const char *name, int flags); int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f); -#define FOREACH_LINE(line, f, on_error) \ - for (;;) \ - if (!fgets(line, sizeof(line), f)) { \ - if (ferror(f)) { \ - on_error; \ - } \ - break; \ - } else - int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern); +int fmkostemp_safe(char *pattern, const char *mode, FILE**_f); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index 3d83fc9b10..55651baa80 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -132,7 +132,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char } int readlinkat_malloc(int fd, const char *p, char **ret) { - size_t l = 100; + size_t l = FILENAME_MAX+1; int r; assert(p); @@ -1173,7 +1173,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { return 0; if (fstat(truncate_fd, &st) < 0) { - log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name); + log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name); return 0; } @@ -1235,6 +1235,34 @@ int fsync_directory_of_file(int fd) { return 0; } +int fsync_path_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + int fd; + + if (isempty(path)) { + if (at_fd == AT_FDCWD) { + opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } else + fd = at_fd; + } else { + + opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + fd = opened_fd; + } + + if (fsync(fd) < 0) + return -errno; + + return 0; +} + int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd; diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h index bc753d5920..955b146a6a 100644 --- a/src/systemd/src/basic/fs-util.h +++ b/src/systemd/src/basic/fs-util.h @@ -105,5 +105,6 @@ void unlink_tempfilep(char (*p)[]); int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); +int fsync_path_at(int at_fd, const char *path); int open_parent(const char *path, int flags, mode_t mode); diff --git a/src/systemd/src/basic/hashmap.c b/src/systemd/src/basic/hashmap.c index 44d718c83d..eba56add1f 100644 --- a/src/systemd/src/basic/hashmap.c +++ b/src/systemd/src/basic/hashmap.c @@ -6,7 +6,6 @@ #include <string.h> #include "alloc-util.h" -#include "env-util.h" #include "fileio.h" #include "hashmap.h" #include "macro.h" @@ -767,24 +766,12 @@ static void reset_direct_storage(HashmapBase *h) { memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); } -static bool use_pool(void) { - static int b = -1; - - if (!is_main_thread()) - return false; - - if (b < 0) - b = getenv_bool("SYSTEMD_MEMPOOL") != 0; - - return b; -} - static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { HashmapBase *h; const struct hashmap_type_info *hi = &hashmap_type_info[type]; bool up; - up = use_pool(); + up = mempool_enabled(); h = up ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) @@ -1556,18 +1543,9 @@ static unsigned find_first_entry(HashmapBase *h) { return hashmap_iterate_entry(h, &i); } -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) { struct hashmap_base_entry *e; + void *key, *data; unsigned idx; idx = find_first_entry(h); @@ -1575,39 +1553,16 @@ void *internal_hashmap_first_key(HashmapBase *h) { return NULL; e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); + key = (void*) e->key; data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; + if (remove) + remove_entry(h, idx); - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); + if (ret_key) + *ret_key = key; - return key; + return data; } unsigned internal_hashmap_size(HashmapBase *h) { diff --git a/src/systemd/src/basic/hashmap.h b/src/systemd/src/basic/hashmap.h index b771ceccdc..bb2a5c76ec 100644 --- a/src/systemd/src/basic/hashmap.h +++ b/src/systemd/src/basic/hashmap.h @@ -290,36 +290,51 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { * the first entry is O(1). */ -void *internal_hashmap_steal_first(HashmapBase *h); +void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key); +static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret); +} +static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} +static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret); +} + + static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); } static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL); +} +static inline void *hashmap_first(Hashmap *h) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); +} +static inline void *ordered_hashmap_first(OrderedHashmap *h) { + return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL); } -void *internal_hashmap_steal_first_key(HashmapBase *h); +static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) { + void *key = NULL; + + (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key); + return key; +} static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), true); } static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), true); } - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), false); } static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); + return internal_hashmap_first_key(HASHMAP_BASE(h), false); } #define hashmap_clear_with_destructor(_s, _f) \ diff --git a/src/systemd/src/basic/list.h b/src/systemd/src/basic/list.h index 643e0bea88..040680c30a 100644 --- a/src/systemd/src/basic/list.h +++ b/src/systemd/src/basic/list.h @@ -38,9 +38,9 @@ /* Append an item to the list */ #define LIST_APPEND(name,head,item) \ do { \ - typeof(*(head)) *_tail; \ - LIST_FIND_TAIL(name,head,_tail); \ - LIST_INSERT_AFTER(name,head,_tail,item); \ + typeof(*(head)) **_hhead = &(head), *_tail; \ + LIST_FIND_TAIL(name, *_hhead, _tail); \ + LIST_INSERT_AFTER(name, *_hhead, _tail, item); \ } while (false) /* Remove an item from the list */ diff --git a/src/systemd/src/basic/macro.h b/src/systemd/src/basic/macro.h index 9972b6f992..ae88fa5b93 100644 --- a/src/systemd/src/basic/macro.h +++ b/src/systemd/src/basic/macro.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <assert.h> #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> @@ -56,10 +57,6 @@ #endif /* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - #define DISABLE_WARNING_FORMAT_NONLITERAL \ _Pragma("GCC diagnostic push"); \ _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") @@ -314,20 +311,13 @@ static inline int __coverity_check__(int condition) { } while (false) #if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ #define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING + static_assert(expr, #expr); #else #define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ struct CONCATENATE(_assert_struct_, __COUNTER__) { \ char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING + }; #endif #define assert_return(expr, r) \ @@ -346,7 +336,7 @@ static inline int __coverity_check__(int condition) { #define PTR_TO_INT(p) ((int) ((intptr_t) (p))) #define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) +#define PTR_TO_UINT(p) ((unsigned) ((uintptr_t) (p))) #define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) #define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) @@ -426,8 +416,11 @@ static inline int __coverity_check__(int condition) { #define IN_SET(x, ...) \ ({ \ bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros */ \ - static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ + /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ + * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ + * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ + * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ + assert_cc((sizeof((long double[]){__VA_ARGS__})/sizeof(long double)) <= 20); \ switch(x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ _found = true; \ diff --git a/src/systemd/src/basic/mempool.c b/src/systemd/src/basic/mempool.c index a5ec8a1020..159c963377 100644 --- a/src/systemd/src/basic/mempool.c +++ b/src/systemd/src/basic/mempool.c @@ -3,8 +3,10 @@ #include <stdint.h> #include <stdlib.h> +#include "env-util.h" #include "macro.h" #include "mempool.h" +#include "process-util.h" #include "util.h" struct pool { @@ -70,8 +72,21 @@ void mempool_free_tile(struct mempool *mp, void *p) { mp->freelist = p; } -#if VALGRIND +bool mempool_enabled(void) { + static int b = -1; + + if (!is_main_thread()) + return false; + if (!mempool_use_allowed) + b = false; + if (b < 0) + b = getenv_bool("SYSTEMD_MEMPOOL") != 0; + + return b; +} + +#if VALGRIND void mempool_drop(struct mempool *mp) { struct pool *p = mp->first_pool; while (p) { @@ -81,5 +96,4 @@ void mempool_drop(struct mempool *mp) { p = n; } } - #endif diff --git a/src/systemd/src/basic/mempool.h b/src/systemd/src/basic/mempool.h index 4098535c6f..0eecca0f92 100644 --- a/src/systemd/src/basic/mempool.h +++ b/src/systemd/src/basic/mempool.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include <stdbool.h> #include <stddef.h> struct pool; @@ -22,6 +23,9 @@ static struct mempool pool_name = { \ .at_least = alloc_at_least, \ } +extern const bool mempool_use_allowed; +bool mempool_enabled(void); + #if VALGRIND void mempool_drop(struct mempool *mp); #endif diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c index f3c6c16aae..2deb176240 100644 --- a/src/systemd/src/basic/path-util.c +++ b/src/systemd/src/basic/path-util.c @@ -333,12 +333,15 @@ char *path_simplify(char *path, bool kill_dots) { /* Removes redundant inner and trailing slashes. Also removes unnecessary dots * if kill_dots is true. Modifies the passed string in-place. * - * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) - * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) - * .//./foo//./bar/. becomes ./foo/bar (if kill_dots is false) - * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) + * ///foo//./bar/. becomes /foo/./bar/. (if kill_dots is false) + * ///foo//./bar/. becomes /foo/bar (if kill_dots is true) + * .//./foo//./bar/. becomes ././foo/./bar/. (if kill_dots is false) + * .//./foo//./bar/. becomes foo/bar (if kill_dots is true) */ + if (isempty(path)) + return path; + absolute = path_is_absolute(path); f = path; @@ -368,9 +371,14 @@ char *path_simplify(char *path, bool kill_dots) { *(t++) = *f; } - /* Special rule, if we are talking of the root directory, a trailing slash is good */ - if (absolute && t == path) - *(t++) = '/'; + /* Special rule, if we stripped everything, we either need a "/" (for the root directory) + * or "." for the current directory */ + if (t == path) { + if (absolute) + *(t++) = '/'; + else + *(t++) = '.'; + } *t = 0; return path; @@ -771,24 +779,32 @@ bool filename_is_valid(const char *p) { if (*e != 0) return false; - if (e - p > FILENAME_MAX) + if (e - p > FILENAME_MAX) /* FILENAME_MAX is counted *without* the trailing NUL byte */ return false; return true; } -bool path_is_normalized(const char *p) { +bool path_is_valid(const char *p) { if (isempty(p)) return false; - if (dot_or_dot_dot(p)) + if (strlen(p) >= PATH_MAX) /* PATH_MAX is counted *with* the trailing NUL byte */ return false; - if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) + return true; +} + +bool path_is_normalized(const char *p) { + + if (!path_is_valid(p)) return false; - if (strlen(p)+1 > PATH_MAX) + if (dot_or_dot_dot(p)) + return false; + + if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) return false; if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h index 49604eab80..17d31bbd89 100644 --- a/src/systemd/src/basic/path-util.h +++ b/src/systemd/src/basic/path-util.h @@ -134,6 +134,7 @@ char* dirname_malloc(const char *path); const char *last_path_component(const char *path); bool filename_is_valid(const char *p) _pure_; +bool path_is_valid(const char *p) _pure_; bool path_is_normalized(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); diff --git a/src/systemd/src/basic/prioq.c b/src/systemd/src/basic/prioq.c index ef28a086d1..4ef4eaf0cd 100644 --- a/src/systemd/src/basic/prioq.c +++ b/src/systemd/src/basic/prioq.c @@ -32,11 +32,14 @@ struct Prioq { Prioq *prioq_new(compare_func_t compare_func) { Prioq *q; - q = new0(Prioq, 1); + q = new(Prioq, 1); if (!q) return q; - q->compare_func = compare_func; + *q = (Prioq) { + .compare_func = compare_func, + }; + return q; } @@ -88,6 +91,7 @@ static void swap(Prioq *q, unsigned j, unsigned k) { static unsigned shuffle_up(Prioq *q, unsigned idx) { assert(q); + assert(idx < q->n_items); while (idx > 0) { unsigned k; @@ -211,9 +215,12 @@ _pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) assert(q); + if (q->n_items <= 0) + return NULL; + if (idx) { if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) + *idx >= q->n_items) return NULL; i = q->items + *idx; diff --git a/src/systemd/src/basic/prioq.h b/src/systemd/src/basic/prioq.h index e036175260..bba5c7caa4 100644 --- a/src/systemd/src/basic/prioq.h +++ b/src/systemd/src/basic/prioq.h @@ -12,6 +12,7 @@ typedef struct Prioq Prioq; Prioq *prioq_new(compare_func_t compare); Prioq *prioq_free(Prioq *q); +DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free); int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); int prioq_put(Prioq *q, void *data, unsigned *idx); diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 1098cf453f..b2aab853e2 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "architecture.h" +#include "def.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -336,15 +337,33 @@ int rename_process(const char name[]) { /* Now, let's tell the kernel about this new memory */ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { - log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m"); - (void) munmap(nn, nn_size); - goto use_saved_argv; - } + /* HACK: prctl() API is kind of dumb on this point. The existing end address may already be + * below the desired start address, in which case the kernel may have kicked this back due + * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in + * action). The proper solution would be to have a prctl() API that could set both start+end + * simultaneously, or at least let us query the existing address to anticipate this condition + * and respond accordingly. For now, we can only guess at the cause of this failure and try + * a workaround--which will briefly expand the arg space to something potentially huge before + * resizing it to what we want. */ + log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m"); + + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) { + log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m"); + (void) munmap(nn, nn_size); + goto use_saved_argv; + } - /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's - * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */ - if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) - log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) { + log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m"); + goto use_saved_argv; + } + } else { + /* And update the end pointer to the new end, too. If this fails, we don't really know what + * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure, + * and continue. */ + if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) + log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m"); + } if (mm) (void) munmap(mm, mm_size); @@ -496,8 +515,8 @@ int get_process_exe(pid_t pid, char **name) { static int get_process_id(pid_t pid, const char *field, uid_t *uid) { _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; const char *p; + int r; assert(field); assert(uid); @@ -515,9 +534,16 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - FOREACH_LINE(line, f, return -errno) { + for (;;) { + _cleanup_free_ char *line = NULL; char *l; + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + l = strstrip(line); if (startswith(l, field)) { diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index 7ef45dc92b..ca4e4401a9 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -134,13 +134,6 @@ static inline bool pid_is_valid(pid_t p) { return p > 0; } -static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { - if (!sched_policy_is_valid(n)) - return -EINVAL; - - return sched_policy_to_string_alloc(n, s); -} - int ioprio_parse_priority(const char *s, int *ret); pid_t getpid_cached(void); @@ -166,7 +159,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { return safe_fork_full(name, NULL, 0, flags, ret_pid); } -int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...); +int fork_agent(const char *name, const int except[], size_t n_except, pid_t *pid, const char *path, ...) _sentinel_; int set_oom_score_adjust(int value); diff --git a/src/systemd/src/basic/set.h b/src/systemd/src/basic/set.h index 664713810d..0d99d5550d 100644 --- a/src/systemd/src/basic/set.h +++ b/src/systemd/src/basic/set.h @@ -86,7 +86,7 @@ static inline void set_clear_free(Set *s) { /* no set_clear_free_free */ static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL); } #define set_clear_with_destructor(_s, _f) \ @@ -105,7 +105,7 @@ static inline void *set_steal_first(Set *s) { /* no set_first_key */ static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); + return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), false, NULL); } /* no set_next */ diff --git a/src/systemd/src/basic/siphash24.h b/src/systemd/src/basic/siphash24.h index 54e2420cc6..70a4a03f6a 100644 --- a/src/systemd/src/basic/siphash24.h +++ b/src/systemd/src/basic/siphash24.h @@ -3,6 +3,7 @@ #include <inttypes.h> #include <stddef.h> #include <stdint.h> +#include <string.h> #include <sys/types.h> struct siphash { @@ -21,3 +22,7 @@ void siphash24_compress(const void *in, size_t inlen, struct siphash *state); uint64_t siphash24_finalize(struct siphash *state); uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); + +static inline uint64_t siphash24_string(const char *s, const uint8_t k[16]) { + return siphash24(s, strlen(s) + 1, k); +} diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 4e8b2baf82..a156566511 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -57,8 +57,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { assert(a); assert(s); - zero(*a); - a->type = SOCK_STREAM; + *a = (SocketAddress) { + .type = SOCK_STREAM, + }; if (*s == '[') { uint16_t port; @@ -96,7 +97,9 @@ int socket_address_parse(SocketAddress *a, const char *s) { size_t l; l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) + if (l >= sizeof(a->sockaddr.un.sun_path)) /* Note that we refuse non-NUL-terminated sockets when + * parsing (the kernel itself is less strict here in what it + * accepts) */ return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; @@ -108,7 +111,11 @@ int socket_address_parse(SocketAddress *a, const char *s) { size_t l; l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) + if (l >= sizeof(a->sockaddr.un.sun_path) - 1) /* Note that we refuse non-NUL-terminate sockets here + * when parsing, even though abstract namespace sockets + * explicitly allow embedded NUL bytes and don't consider + * them special. But it's simply annoying to debug such + * sockets. */ return -EINVAL; a->sockaddr.un.sun_family = AF_UNIX; @@ -286,19 +293,28 @@ int socket_address_verify(const SocketAddress *a) { case AF_UNIX: if (a->size < offsetof(struct sockaddr_un, sun_path)) return -EINVAL; + if (a->size > sizeof(struct sockaddr_un)+1) /* Allow one extra byte, since getsockname() on Linux will + * append a NUL byte if we have path sockets that are above + * sun_path' full size */ + return -EINVAL; - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; + if (a->size > offsetof(struct sockaddr_un, sun_path) && + a->sockaddr.un.sun_path[0] != 0) { /* Only validate file system sockets here */ - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; + const char *e; + e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); + if (e) { + /* If there's an embedded NUL byte, make sure the size of the socket addresses matches it */ if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) return -EINVAL; + } else { + /* If there's no embedded NUL byte, then then the size needs to match the whole + * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful, + * and considers both equivalent: getsockname() even extends sockaddr_un beyond its + * size if the path is non NUL terminated.)*/ + if (!IN_SET(a->size, sizeof(a->sockaddr.un.sun_path), sizeof(a->sockaddr.un.sun_path)+1)) + return -EINVAL; } } @@ -482,6 +498,10 @@ const char* socket_address_get_path(const SocketAddress *a) { if (a->sockaddr.un.sun_path[0] == 0) return NULL; + /* Note that this is only safe because we know that there's an extra NUL byte after the sockaddr_un + * structure. On Linux AF_UNIX file system socket addresses don't have to be NUL terminated if they take up the + * full sun_path space. */ + assert_cc(sizeof(union sockaddr_union) >= sizeof(struct sockaddr_un)+1); return a->sockaddr.un.sun_path; } @@ -747,21 +767,6 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) return 0; } -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - static const char* const netlink_family_table[] = { [NETLINK_ROUTE] = "route", [NETLINK_FIREWALL] = "firewall", @@ -834,10 +839,11 @@ int fd_inc_sndbuf(int fd, size_t n) { /* If we have the privileges we will ignore the kernel limit. */ - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) - return -errno; + if (setsockopt_int(fd, SOL_SOCKET, SO_SNDBUF, n) < 0) { + r = setsockopt_int(fd, SOL_SOCKET, SO_SNDBUFFORCE, n); + if (r < 0) + return r; + } return 1; } @@ -852,10 +858,12 @@ int fd_inc_rcvbuf(int fd, size_t n) { /* If we have the privileges we will ignore the kernel limit. */ - value = (int) n; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) - return -errno; + if (setsockopt_int(fd, SOL_SOCKET, SO_RCVBUF, n) < 0) { + r = setsockopt_int(fd, SOL_SOCKET, SO_RCVBUFFORCE, n); + if (r < 0) + return r; + } + return 1; } @@ -1246,3 +1254,71 @@ int socket_ioctl_fd(void) { return fd; } + +int sockaddr_un_unlink(const struct sockaddr_un *sa) { + const char *p, * nul; + + assert(sa); + + if (sa->sun_family != AF_UNIX) + return -EPROTOTYPE; + + if (sa->sun_path[0] == 0) /* Nothing to do for abstract sockets */ + return 0; + + /* The path in .sun_path is not necessarily NUL terminated. Let's fix that. */ + nul = memchr(sa->sun_path, 0, sizeof(sa->sun_path)); + if (nul) + p = sa->sun_path; + else + p = memdupa_suffix0(sa->sun_path, sizeof(sa->sun_path)); + + if (unlink(p) < 0) + return -errno; + + return 1; +} + +int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { + size_t l; + + assert(ret); + assert(path); + + /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as + * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept + * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to + * reference paths in the abstract namespace that include NUL bytes in the name. */ + + l = strlen(path); + if (l == 0) + return -EINVAL; + if (!IN_SET(path[0], '/', '@')) + return -EINVAL; + if (path[1] == 0) + return -EINVAL; + + /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than + * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket + * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that + * do not expect non-NUL terminated file system path*/ + if (l+1 > sizeof(ret->sun_path)) + return -EINVAL; + + *ret = (struct sockaddr_un) { + .sun_family = AF_UNIX, + }; + + if (path[0] == '@') { + /* Abstract namespace socket */ + memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */ + return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */ + + } else { + assert(path[0] == '/'); + + /* File system socket */ + memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */ + return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ + } +} diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index 82781a0de1..8090e21657 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -71,7 +71,12 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); int socket_address_print(const SocketAddress *a, char **p); int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); + +int sockaddr_un_unlink(const struct sockaddr_un *sa); + +static inline int socket_address_unlink(const SocketAddress *a) { + return socket_address_family(a) == AF_UNIX ? sockaddr_un_unlink(&a->sockaddr.un) : 0; +} bool socket_address_can_accept(const SocketAddress *a) _pure_; @@ -179,7 +184,16 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng offsetof(struct sockaddr_un, sun_path) + \ (_sa->sun_path[0] == 0 ? \ 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ + strnlen(_sa->sun_path, sizeof(_sa->sun_path))+1); \ }) int socket_ioctl_fd(void); + +int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path); + +static inline int setsockopt_int(int fd, int level, int optname, int value) { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) + return -errno; + + return 0; +} diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index dfa739996f..05469ac01f 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -128,7 +128,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { } /* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { const char *current; current = *state; @@ -144,20 +144,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo return NULL; } - if (quoted && strchr("\'\"", *current)) { + if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) { char quotechars[2] = {*current, '\0'}; *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { /* right quote missing or garbage at the end */ + if (flags & SPLIT_RELAX) { + *state = current + *l + 1 + (current[*l + 1] != '\0'); + return current + 1; + } *state = current; return NULL; } *state = current++ + *l + 2; - } else if (quoted) { + } else if (flags & SPLIT_QUOTES) { *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l])) { + if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) { /* unfinished escape */ *state = current; return NULL; @@ -394,12 +398,7 @@ int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { if (r != 0) return r; - if (n < m) - return -1; - else if (n > m) - return 1; - else - return 0; + return CMP(n, m); } bool chars_intersect(const char *a, const char *b) { @@ -1060,8 +1059,11 @@ typedef void *(*memset_t)(void *,int,size_t); static volatile memset_t memset_func = memset; -void explicit_bzero(void *p, size_t l) { - memset_func(p, '\0', l); +void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + memset_func(p, '\0', l); + + return p; } #endif @@ -1071,7 +1073,7 @@ char* string_erase(char *x) { /* A delicious drop of snake-oil! To be called on memory where * we stored passphrases or so, after we used them. */ - explicit_bzero(x, strlen(x)); + explicit_bzero_safe(x, strlen(x)); return x; } diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h index 72c075aa39..a5b5a16a5d 100644 --- a/src/systemd/src/basic/string-util.h +++ b/src/systemd/src/basic/string-util.h @@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; -const char* split(const char **state, size_t *l, const char *separator, bool quoted); +typedef enum SplitFlags { + SPLIT_QUOTES = 0x01 << 0, + SPLIT_RELAX = 0x01 << 1, +} SplitFlags; + +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); #define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) + _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) + _FOREACH_WORD(word, length, s, separator, 0, state) -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) +#define _FOREACH_WORD(word, length, s, separator, flags, state) \ + for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags))) char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -193,8 +198,15 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -#if !HAVE_EXPLICIT_BZERO -void explicit_bzero(void *p, size_t l); +#if HAVE_EXPLICIT_BZERO +static inline void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + explicit_bzero(p, l); + + return p; +} +#else +void *explicit_bzero_safe(void *p, size_t l); #endif char *string_erase(char *x); diff --git a/src/systemd/src/basic/strv.c b/src/systemd/src/basic/strv.c index dc72f036ac..1bab723e88 100644 --- a/src/systemd/src/basic/strv.c +++ b/src/systemd/src/basic/strv.c @@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { return 0; } -char **strv_split(const char *s, const char *separator) { +char **strv_split_full(const char *s, const char *separator, SplitFlags flags) { const char *word, *state; size_t l; size_t n, i; @@ -253,12 +253,15 @@ char **strv_split(const char *s, const char *separator) { assert(s); + if (!separator) + separator = WHITESPACE; + s += strspn(s, separator); if (isempty(s)) return new0(char*, 1); n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) + _FOREACH_WORD(word, l, s, separator, flags, state) n++; r = new(char*, n+1); @@ -266,7 +269,7 @@ char **strv_split(const char *s, const char *separator) { return NULL; i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { + _FOREACH_WORD(word, l, s, separator, flags, state) { r[i] = strndup(word, l); if (!r[i]) { strv_free(r); diff --git a/src/systemd/src/basic/strv.h b/src/systemd/src/basic/strv.h index 34a660cb92..e9e6063f58 100644 --- a/src/systemd/src/basic/strv.h +++ b/src/systemd/src/basic/strv.h @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "extract-word.h" #include "macro.h" +#include "string-util.h" #include "util.h" char *strv_find(char **l, const char *name) _pure_; @@ -66,7 +67,10 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split(const char *s, const char *separator); +char **strv_split_full(const char *s, const char *separator, SplitFlags flags); +static inline char **strv_split(const char *s, const char *separator) { + return strv_split_full(s, separator, 0); +} char **strv_split_newlines(const char *s); int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 9ac739b42a..e351684219 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -14,6 +14,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "def.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -23,6 +24,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "serialize.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -279,7 +281,7 @@ static char *format_timestamp_internal( /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { - assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1); + assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); return buf; } @@ -529,64 +531,6 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { return buf; } -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - uint64_t a, b; - int r, pos; - - assert(value); - assert(t); - - pos = strspn(value, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - pos += strspn(value + pos, DIGITS); - pos += strspn(value + pos, WHITESPACE); - if (value[pos] == '-') - return -EINVAL; - - r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); - if (r != 2) { - log_debug("Failed to parse dual timestamp value \"%s\".", value); - return -EINVAL; - } - - if (value[pos] != '\0') - /* trailing garbage */ - return -EINVAL; - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { static const struct { const char *name; @@ -1025,7 +969,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { char *b = e + 1; /* Don't allow "0.-0", "3.+1" or "3. 1" */ - if (*b == '-' || *b == '+' || isspace(*b)) + if (IN_SET(*b, '-', '+') || isspace(*b)) return -EINVAL; errno = 0; @@ -1047,12 +991,21 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { something = true; + + k = ((usec_t) -1) / multiplier; + if ((usec_t) l + 1 >= k || (usec_t) z >= k) + return -ERANGE; + k = (usec_t) z * multiplier; for (; n > 0; n--) k /= 10; - r += (usec_t) l * multiplier + k; + k += (usec_t) l * multiplier; + if (k >= ((usec_t) -1) - r) + return -ERANGE; + + r += k; } *usec = r; @@ -1064,18 +1017,19 @@ int parse_sec(const char *t, usec_t *usec) { return parse_time(t, usec, USEC_PER_SEC); } -int parse_sec_fix_0(const char *t, usec_t *usec) { - assert(t); - assert(usec); +int parse_sec_fix_0(const char *t, usec_t *ret) { + usec_t k; + int r; - t += strspn(t, WHITESPACE); + assert(t); + assert(ret); - if (streq(t, "0")) { - *usec = USEC_INFINITY; - return 0; - } + r = parse_sec(t, &k); + if (r < 0) + return r; - return parse_sec(t, usec); + *ret = k == 0 ? USEC_INFINITY : k; + return r; } int parse_nsec(const char *t, nsec_t *nsec) { @@ -1163,7 +1117,7 @@ int parse_nsec(const char *t, nsec_t *nsec) { if (*e == '.') { char *b = e + 1; - if (*b == '-' || *b == '+' || isspace(*b)) + if (IN_SET(*b, '-', '+') || isspace(*b)) return -EINVAL; errno = 0; @@ -1184,12 +1138,22 @@ int parse_nsec(const char *t, nsec_t *nsec) { for (i = 0; i < ELEMENTSOF(table); i++) if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; + nsec_t k; + + k = ((nsec_t) -1) / table[i].nsec; + if ((nsec_t) l + 1 >= k || (nsec_t) z >= k) + return -ERANGE; + + k = (nsec_t) z * table[i].nsec; for (; n > 0; n--) k /= 10; - r += (nsec_t) l * table[i].nsec + k; + k += (nsec_t) l * table[i].nsec; + if (k >= ((nsec_t) -1) - r) + return -ERANGE; + + r += k; p = e + strlen(table[i].suffix); something = true; @@ -1222,6 +1186,7 @@ int get_timezones(char ***ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **zones = NULL; size_t n_zones = 0, n_allocated = 0; + int r; assert(ret); @@ -1234,13 +1199,18 @@ int get_timezones(char ***ret) { f = fopen("/usr/share/zoneinfo/zone.tab", "re"); if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { + for (;;) { + _cleanup_free_ char *line = NULL; char *p, *w; size_t k; - p = strstrip(l); + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return r; + if (r == 0) + break; + + p = strstrip(line); if (isempty(p) || *p == '#') continue; diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h index 344f2dc52e..5316305062 100644 --- a/src/systemd/src/basic/time-util.h +++ b/src/systemd/src/basic/time-util.h @@ -108,10 +108,6 @@ char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); char *format_timestamp_relative(char *buf, size_t l, usec_t t); char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 081c63c898..0da963f4af 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -23,6 +23,7 @@ #include "def.h" #include "device-nodes.h" #include "dirent-util.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -106,6 +107,7 @@ int prot_from_flags(int flags) { bool in_initrd(void) { struct statfs s; + int r; if (saved_in_initrd >= 0) return saved_in_initrd; @@ -120,9 +122,16 @@ bool in_initrd(void) { * emptying when transititioning to the main systemd. */ - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); + r = getenv_bool_secure("SYSTEMD_IN_INITRD"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); + + if (r >= 0) + saved_in_initrd = r > 0; + else + saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && + statfs("/", &s) >= 0 && + is_temporary_fs(&s); return saved_in_initrd; } diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index 5f3f982190..8cba4ed726 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -150,7 +150,13 @@ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { int on_ac_power(void); -#define memzero(x,l) (memset((x), 0, (l))) +#define memzero(x,l) \ + ({ \ + size_t _l_ = (l); \ + void *_x_ = (x); \ + _l_ == 0 ? _x_ : memset(_x_, 0, _l_); \ + }) + #define zero(x) (memzero(&(x), sizeof(x))) static inline void *mempset(void *s, int c, size_t n) { diff --git a/src/systemd/src/libsystemd-network/dhcp-network.c b/src/systemd/src/libsystemd-network/dhcp-network.c index cf59f14958..0e5b4147a9 100644 --- a/src/systemd/src/libsystemd-network/dhcp-network.c +++ b/src/systemd/src/libsystemd-network/dhcp-network.c @@ -78,7 +78,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, .filter = filter }; _cleanup_close_ int s = -1; - int r, on = 1; + int r; assert(ifindex > 0); assert(link); @@ -87,9 +87,9 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, if (s < 0) return -errno; - r = setsockopt(s, SOL_PACKET, PACKET_AUXDATA, &on, sizeof(on)); + r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true); if (r < 0) - return -errno; + return r; r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) @@ -149,19 +149,19 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { }; _cleanup_close_ int s = -1; char ifname[IF_NAMESIZE] = ""; - int r, on = 1, tos = IPTOS_CLASS_CS6; + int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); + r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; if (ifindex > 0) { if (if_indextoname(ifindex, ifname) == 0) @@ -173,18 +173,18 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { } if (address == INADDR_ANY) { - r = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true); if (r < 0) - return -errno; + return r; } else { - r = setsockopt(s, IPPROTO_IP, IP_FREEBIND, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true); if (r < 0) - return -errno; + return r; } r = bind(s, &src.sa, sizeof(src.in)); diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h index 63d8fe35f8..157fc0aadd 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-internal.h +++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h @@ -84,8 +84,8 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); diff --git a/src/systemd/src/libsystemd-network/dhcp6-network.c b/src/systemd/src/libsystemd-network/dhcp6-network.c index 78cd383669..580f43ba40 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-network.c +++ b/src/systemd/src/libsystemd-network/dhcp6-network.c @@ -25,7 +25,7 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { .in6.sin6_scope_id = index, }; _cleanup_close_ int s = -1; - int r, off = 0, on = 1; + int r; assert(index > 0); assert(local_address); @@ -36,17 +36,17 @@ int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { if (s < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_V6ONLY, true); if (r < 0) - return -errno; + return r; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, false); if (r < 0) - return -errno; + return r; - r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true); if (r < 0) - return -errno; + return r; r = bind(s, &src.sa, sizeof(src.in6)); if (r < 0) diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c index cf19d366d3..a2aac9a793 100644 --- a/src/systemd/src/libsystemd-network/dhcp6-option.c +++ b/src/systemd/src/libsystemd-network/dhcp6-option.c @@ -37,9 +37,9 @@ typedef struct DHCP6PDPrefixOption { uint8_t options[]; } _packed_ DHCP6PDPrefixOption; -#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) -#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) -#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) +#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) +#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) +#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -49,14 +49,14 @@ static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) + if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; option->code = htobe16(optcode); option->len = htobe16(optlen); - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); return 0; } @@ -79,14 +79,17 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, return 0; } -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; size_t iaid_offset, ia_buflen, ia_addrlen = 0; DHCP6Address *addr; int r; - assert_return(buf && *buf && buflen && ia, -EINVAL); + assert_return(buf, -EINVAL); + assert_return(*buf, -EINVAL); + assert_return(buflen, -EINVAL); + assert_return(ia, -EINVAL); switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: @@ -103,14 +106,14 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { return -EINVAL; } - if (*buflen < len) + if (*buflen < offsetof(DHCP6Option, data) + len) return -ENOBUFS; ia_hdr = *buf; ia_buflen = *buflen; - *buf += sizeof(DHCP6Option); - *buflen -= sizeof(DHCP6Option); + *buf += offsetof(DHCP6Option, data); + *buflen -= offsetof(DHCP6Option, data); memcpy(*buf, (char*) ia + iaid_offset, len); @@ -128,7 +131,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(addr->iaaddr); *buflen -= sizeof(addr->iaaddr); - ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); + ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr); } r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); @@ -165,7 +168,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } -int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { +int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd) { DHCP6Option *option = (DHCP6Option *)buf; size_t i = sizeof(*option) + sizeof(pd->ia_pd); DHCP6Address *prefix; @@ -210,7 +213,7 @@ static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, si assert_return(optcode, -EINVAL); assert_return(optlen, -EINVAL); - if (*buflen < sizeof(DHCP6Option)) + if (*buflen < offsetof(DHCP6Option, data)) return -ENOMSG; len = be16toh(option->len); @@ -251,7 +254,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; if (len < sizeof(DHCP6StatusOption) || - be16toh(option->len) + sizeof(DHCP6Option) < sizeof(DHCP6StatusOption)) + be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption)) return -ENOBUFS; return be16toh(statusopt->status); @@ -264,7 +267,7 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option)) return -ENOBUFS; lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); @@ -277,8 +280,8 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*addr_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); if (r != 0) return r < 0 ? r: 0; } @@ -304,7 +307,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t lt_valid, lt_pref; int r; - if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option)) + if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option)) return -ENOBUFS; lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); @@ -317,8 +320,8 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, return 0; } - if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + sizeof(DHCP6Option) - sizeof(*pdprefix_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); if (r != 0) return r < 0 ? r: 0; } @@ -354,10 +357,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (len < DHCP6_OPTION_IA_NA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_NA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); @@ -368,18 +369,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_PD: - if (len < sizeof(ia->ia_pd)) { - r = -ENOBUFS; - goto error; - } + if (len < sizeof(ia->ia_pd)) + return -ENOBUFS; iaaddr_offset = sizeof(ia->ia_pd); memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); @@ -390,17 +388,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); - r = -EINVAL; - goto error; + return -EINVAL; } break; case SD_DHCP6_OPTION_IA_TA: - if (len < DHCP6_OPTION_IA_TA_LEN) { - r = -ENOBUFS; - goto error; - } + if (len < DHCP6_OPTION_IA_TA_LEN) + return -ENOBUFS; iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); @@ -408,8 +403,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; default: - r = -ENOMSG; - goto error; + return -ENOMSG; } ia->type = iatype; @@ -418,10 +412,8 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { while (i < len) { DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { - r = -ENOBUFS; - goto error; - } + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) + return -ENOBUFS; opt = be16toh(option->code); optlen = be16toh(option->len); @@ -431,13 +423,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_address(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -448,13 +439,12 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); - r = -EINVAL; - goto error; + return -EINVAL; } r = dhcp6_option_parse_pdprefix(option, ia, <_valid); if (r < 0) - goto error; + return r; if (lt_valid < lt_min) lt_min = lt_valid; @@ -463,15 +453,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); + if (status < 0) + return status; + if (status > 0) { log_dhcp6_client(client, "IA status %d", status); - dhcp6_lease_free_ia(ia); - - r = -EINVAL; - goto error; + return -EINVAL; } break; @@ -515,8 +504,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { break; } -error: - return r; + return 0; } int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, @@ -551,6 +539,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * bool first = true; for (;;) { + const char *label; uint8_t c; c = optval[pos++]; @@ -558,47 +547,41 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (c == 0) /* End of name */ break; - else if (c <= 63) { - const char *label; - - /* Literal label */ - label = (const char *)&optval[pos]; - pos += c; - if (pos >= optlen) - return -EMSGSIZE; - - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } - - if (first) - first = false; - else - ret[n++] = '.'; - - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); - if (r < 0) - goto fail; - - n += r; - continue; - } else { - r = -EBADMSG; - goto fail; - } - } + if (c > 63) + return -EBADMSG; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos >= optlen) + return -EMSGSIZE; + + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + ret[n++] = '.'; - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; } + if (n == 0) + continue; + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; + ret[n] = 0; r = strv_extend(&names, ret); if (r < 0) - goto fail; + return r; idx++; } @@ -606,7 +589,4 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * *str_arr = TAKE_PTR(names); return idx; - -fail: - return r; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c index 3e8d985aa7..dde2d0ceb0 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c @@ -86,7 +86,7 @@ struct sd_dhcp_client { uint32_t mtu; uint32_t xid; usec_t start_time; - unsigned int attempt; + unsigned attempt; usec_t request_sent; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -1683,6 +1683,8 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED); + r = client_initialize(client); if (r < 0) goto error; diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c index c07d831f7d..2ddfead4b0 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c @@ -829,10 +829,11 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + + uint32_t lt_t1 = ~0, lt_t2 = ~0; + bool clientid = false; size_t pos = 0; int r; - bool clientid = false; - uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); @@ -842,20 +843,22 @@ static int client_parse_message( len -= sizeof(DHCP6Message); while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; - int status; - uint8_t *optval; be32_t iaid_lease; + uint8_t *optval; + int status; - if (len < pos + offsetof(DHCP6Option, data) || - len < pos + offsetof(DHCP6Option, data) + be16toh(option->len)) + if (len < pos + offsetof(DHCP6Option, data)) return -ENOBUFS; optcode = be16toh(option->code); optlen = be16toh(option->len); optval = option->data; + if (len < pos + offsetof(DHCP6Option, data) + optlen) + return -ENOBUFS; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -900,13 +903,14 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen); - if (status) { + status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); + if (status < 0) + return status; + + if (status > 0) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); return -EINVAL; } diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c index 15fec2d851..8b424811ad 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c @@ -52,15 +52,16 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { + uint8_t *serverid; + assert_return(lease, -EINVAL); assert_return(id, -EINVAL); - free(lease->serverid); - - lease->serverid = memdup(id, len); - if (!lease->serverid) - return -EINVAL; + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + free_and_replace(lease->serverid, serverid); lease->serverid_len = len; return 0; diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c index 880e127686..f44e6b4cca 100644 --- a/src/systemd/src/libsystemd/sd-event/sd-event.c +++ b/src/systemd/src/libsystemd/sd-event/sd-event.c @@ -314,6 +314,7 @@ static sd_event *event_resolve(sd_event *e) { static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->pending); assert(y->pending); @@ -325,22 +326,17 @@ static int pending_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; + r = CMP(x->priority, y->priority); + if (r != 0) + return r; /* Older entries first */ - if (x->pending_iteration < y->pending_iteration) - return -1; - if (x->pending_iteration > y->pending_iteration) - return 1; - - return 0; + return CMP(x->pending_iteration, y->pending_iteration); } static int prepare_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; + int r; assert(x->prepare); assert(y->prepare); @@ -354,18 +350,12 @@ static int prepare_prioq_compare(const void *a, const void *b) { /* Move most recently prepared ones last, so that we can stop * preparing as soon as we hit one that has already been * prepared in the current iteration */ - if (x->prepare_iteration < y->prepare_iteration) - return -1; - if (x->prepare_iteration > y->prepare_iteration) - return 1; + r = CMP(x->prepare_iteration, y->prepare_iteration); + if (r != 0) + return r; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static int earliest_time_prioq_compare(const void *a, const void *b) { @@ -387,12 +377,7 @@ static int earliest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (x->time.next < y->time.next) - return -1; - if (x->time.next > y->time.next) - return 1; - - return 0; + return CMP(x->time.next, y->time.next); } static usec_t time_event_source_latest(const sd_event_source *s) { @@ -418,12 +403,7 @@ static int latest_time_prioq_compare(const void *a, const void *b) { return 1; /* Order by time */ - if (time_event_source_latest(x) < time_event_source_latest(y)) - return -1; - if (time_event_source_latest(x) > time_event_source_latest(y)) - return 1; - - return 0; + return CMP(time_event_source_latest(x), time_event_source_latest(y)); } static int exit_prioq_compare(const void *a, const void *b) { @@ -439,12 +419,7 @@ static int exit_prioq_compare(const void *a, const void *b) { return 1; /* Lower priority values first */ - if (x->priority < y->priority) - return -1; - if (x->priority > y->priority) - return 1; - - return 0; + return CMP(x->priority, y->priority); } static void free_clock_data(struct clock_data *d) { @@ -1590,21 +1565,16 @@ static int event_make_inotify_data( static int inode_data_compare(const void *a, const void *b) { const struct inode_data *x = a, *y = b; + int r; assert(x); assert(y); - if (x->dev < y->dev) - return -1; - if (x->dev > y->dev) - return 1; - - if (x->ino < y->ino) - return -1; - if (x->ino > y->ino) - return 1; + r = CMP(x->dev, y->dev); + if (r != 0) + return r; - return 0; + return CMP(x->ino, y->ino); } static void inode_data_hash_func(const void *p, struct siphash *state) { diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h index eb35b83431..c38eb84beb 100644 --- a/src/systemd/src/systemd/sd-event.h +++ b/src/systemd/src/systemd/sd-event.h @@ -33,7 +33,8 @@ - Supports event source prioritization - Scales better with a large number of time events because it does not require one timerfd each - Automatically tries to coalesce timer events system-wide - - Handles signals and child PIDs + - Handles signals, child PIDs, inotify events + - Supports systemd-style automatic watchdog event generation */ _SD_BEGIN_DECLARATIONS; |