|
|
8be66a |
From 1d11e79fefea34b4395043e8e951414c5b7817ba Mon Sep 17 00:00:00 2001
|
|
|
8be66a |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
8be66a |
Date: Mon, 1 Jun 2020 17:06:19 +0200
|
|
|
8be66a |
Subject: [PATCH] parse-util: allow tweaking how to parse integers
|
|
|
8be66a |
|
|
|
8be66a |
This allows disabling a few alternative ways to decode integers
|
|
|
8be66a |
formatted as strings, for safety reasons.
|
|
|
8be66a |
|
|
|
8be66a |
See: #15991
|
|
|
8be66a |
(cherry picked from commit 707e93aff8f358f8a62117e54b857530d6594e4b)
|
|
|
8be66a |
|
|
|
8be66a |
Related: #1848373
|
|
|
8be66a |
---
|
|
|
8be66a |
src/basic/parse-util.c | 65 +++++++++++++++++++++++++++++++++---------
|
|
|
8be66a |
src/basic/parse-util.h | 6 ++++
|
|
|
8be66a |
2 files changed, 58 insertions(+), 13 deletions(-)
|
|
|
8be66a |
|
|
|
8be66a |
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
|
|
|
8be66a |
index 67056c0434..6cc4fc3e57 100644
|
|
|
8be66a |
--- a/src/basic/parse-util.c
|
|
|
8be66a |
+++ b/src/basic/parse-util.c
|
|
|
8be66a |
@@ -383,20 +383,35 @@ int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
|
|
|
8be66a |
unsigned long l;
|
|
|
8be66a |
|
|
|
8be66a |
assert(s);
|
|
|
8be66a |
- assert(base <= 16);
|
|
|
8be66a |
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
|
|
|
8be66a |
|
|
|
8be66a |
- /* strtoul() is happy to parse negative values, and silently
|
|
|
8be66a |
- * converts them to unsigned values without generating an
|
|
|
8be66a |
- * error. We want a clean error, hence let's look for the "-"
|
|
|
8be66a |
- * prefix on our own, and generate an error. But let's do so
|
|
|
8be66a |
- * only after strtoul() validated that the string is clean
|
|
|
8be66a |
- * otherwise, so that we return EINVAL preferably over
|
|
|
8be66a |
- * ERANGE. */
|
|
|
8be66a |
+ /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without
|
|
|
8be66a |
+ * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and
|
|
|
8be66a |
+ * generate an error. But let's do so only after strtoul() validated that the string is clean
|
|
|
8be66a |
+ * otherwise, so that we return EINVAL preferably over ERANGE. */
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
|
|
|
8be66a |
+ strchr(WHITESPACE, s[0]))
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
|
|
|
8be66a |
s += strspn(s, WHITESPACE);
|
|
|
8be66a |
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
|
|
|
8be66a |
+ IN_SET(s[0], '+', '-'))
|
|
|
8be66a |
+ return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a
|
|
|
8be66a |
+ * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we
|
|
|
8be66a |
+ * blanket refuse +/- prefixed integers, while if it is missing we'll just
|
|
|
8be66a |
+ * return ERANGE, because the string actually parses correctly, but doesn't
|
|
|
8be66a |
+ * fit in the return type. */
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
|
|
|
8be66a |
+ s[0] == '0' && !streq(s, "0"))
|
|
|
8be66a |
+ return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal
|
|
|
8be66a |
+ * notation and assumed-to-be-decimal integers with a leading zero. */
|
|
|
8be66a |
+
|
|
|
8be66a |
errno = 0;
|
|
|
8be66a |
- l = strtoul(s, &x, base);
|
|
|
8be66a |
+ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual
|
|
|
8be66a |
+ * base is left */);
|
|
|
8be66a |
if (errno > 0)
|
|
|
8be66a |
return -errno;
|
|
|
8be66a |
if (!x || x == s || *x != 0)
|
|
|
8be66a |
@@ -438,11 +453,24 @@ int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu)
|
|
|
8be66a |
unsigned long long l;
|
|
|
8be66a |
|
|
|
8be66a |
assert(s);
|
|
|
8be66a |
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
|
|
|
8be66a |
+ strchr(WHITESPACE, s[0]))
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
|
|
|
8be66a |
s += strspn(s, WHITESPACE);
|
|
|
8be66a |
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
|
|
|
8be66a |
+ IN_SET(s[0], '+', '-'))
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
|
|
|
8be66a |
+ s[0] == '0' && s[1] != 0)
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
+
|
|
|
8be66a |
errno = 0;
|
|
|
8be66a |
- l = strtoull(s, &x, base);
|
|
|
8be66a |
+ l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base));
|
|
|
8be66a |
if (errno > 0)
|
|
|
8be66a |
return -errno;
|
|
|
8be66a |
if (!x || x == s || *x != 0)
|
|
|
8be66a |
@@ -504,13 +532,24 @@ int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
|
|
|
8be66a |
unsigned long l;
|
|
|
8be66a |
|
|
|
8be66a |
assert(s);
|
|
|
8be66a |
- assert(ret);
|
|
|
8be66a |
- assert(base <= 16);
|
|
|
8be66a |
+ assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
|
|
|
8be66a |
+ strchr(WHITESPACE, s[0]))
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
|
|
|
8be66a |
s += strspn(s, WHITESPACE);
|
|
|
8be66a |
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
|
|
|
8be66a |
+ IN_SET(s[0], '+', '-'))
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
+
|
|
|
8be66a |
+ if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
|
|
|
8be66a |
+ s[0] == '0' && s[1] != 0)
|
|
|
8be66a |
+ return -EINVAL;
|
|
|
8be66a |
+
|
|
|
8be66a |
errno = 0;
|
|
|
8be66a |
- l = strtoul(s, &x, base);
|
|
|
8be66a |
+ l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base));
|
|
|
8be66a |
if (errno > 0)
|
|
|
8be66a |
return -errno;
|
|
|
8be66a |
if (!x || x == s || *x != 0)
|
|
|
8be66a |
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
|
|
|
8be66a |
index 8a49257050..c6bbc98dff 100644
|
|
|
8be66a |
--- a/src/basic/parse-util.h
|
|
|
8be66a |
+++ b/src/basic/parse-util.h
|
|
|
8be66a |
@@ -26,6 +26,12 @@ int parse_syscall_and_errno(const char *in, char **name, int *error);
|
|
|
8be66a |
#define FORMAT_BYTES_MAX 8
|
|
|
8be66a |
char *format_bytes(char *buf, size_t l, uint64_t t);
|
|
|
8be66a |
|
|
|
8be66a |
+#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
|
|
|
8be66a |
+#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)
|
|
|
8be66a |
+#define SAFE_ATO_REFUSE_LEADING_WHITESPACE (1U << 28)
|
|
|
8be66a |
+#define SAFE_ATO_ALL_FLAGS (SAFE_ATO_REFUSE_PLUS_MINUS|SAFE_ATO_REFUSE_LEADING_ZERO|SAFE_ATO_REFUSE_LEADING_WHITESPACE)
|
|
|
8be66a |
+#define SAFE_ATO_MASK_FLAGS(base) ((base) & ~SAFE_ATO_ALL_FLAGS)
|
|
|
8be66a |
+
|
|
|
8be66a |
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
|
|
|
8be66a |
|
|
|
8be66a |
static inline int safe_atou(const char *s, unsigned *ret_u) {
|