diff options
author | Lennart Poettering <lennart@poettering.net> | 2020-06-01 17:06:19 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2020-06-05 15:56:31 +0200 |
commit | 707e93aff8f358f8a62117e54b857530d6594e4b (patch) | |
tree | b00c0a53ea7ef4a03b4e50ac9d061d289d25f8c9 /src/basic | |
parent | 0ce80921099fd113eb94e72249375555022079ac (diff) | |
download | systemd-707e93aff8f358f8a62117e54b857530d6594e4b.tar.gz |
parse-util: allow tweaking how to parse integers
This allows disabling a few alternative ways to decode integers
formatted as strings, for safety reasons.
See: #15991
Diffstat (limited to 'src/basic')
-rw-r--r-- | src/basic/parse-util.c | 65 | ||||
-rw-r--r-- | src/basic/parse-util.h | 6 |
2 files changed, 58 insertions, 13 deletions
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 59f8a31cec..15818958e4 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -359,20 +359,35 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { unsigned long l; assert(s); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); - /* strtoul() is happy to parse negative values, and silently - * converts them to unsigned values without generating an - * error. We want a clean error, hence let's look for the "-" - * prefix on our own, and generate an error. But let's do so - * only after strtoul() validated that the string is clean - * otherwise, so that we return EINVAL preferably over - * ERANGE. */ + /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without + * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and + * generate an error. But let's do so only after strtoul() validated that the string is clean + * otherwise, so that we return EINVAL preferably over ERANGE. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a + * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we + * blanket refuse +/- prefixed integers, while if it is missing we'll just + * return ERANGE, because the string actually parses correctly, but doesn't + * fit in the return type. */ + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && !streq(s, "0")) + return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal + * notation and assumed-to-be-decimal integers with a leading zero. */ + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual + * base is left */); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -414,11 +429,24 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) unsigned long long l; assert(s); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + errno = 0; - l = strtoull(s, &x, base); + l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) @@ -480,13 +508,24 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { unsigned long l; assert(s); - assert(ret); - assert(base <= 16); + assert(SAFE_ATO_MASK_FLAGS(base) <= 16); + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) && + strchr(WHITESPACE, s[0])) + return -EINVAL; s += strspn(s, WHITESPACE); + if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) && + IN_SET(s[0], '+', '-')) + return -EINVAL; + + if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) && + s[0] == '0' && s[1] != 0) + return -EINVAL; + errno = 0; - l = strtoul(s, &x, base); + l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base)); if (errno > 0) return -errno; if (!x || x == s || *x != 0) diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 970bdefbf0..9a516ce5f6 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -21,6 +21,12 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_errno(const char *t); int parse_syscall_and_errno(const char *in, char **name, int *error); +#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) +#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) +#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28) +#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE) +#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS) + int safe_atou_full(const char *s, unsigned base, unsigned *ret_u); static inline int safe_atou(const char *s, unsigned *ret_u) { |