From c21b316964357f40b8785a8869cbb280e59d3b79 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 7 Dec 2022 06:49:17 +0900 Subject: [PATCH 1/6] hexdecoct: several cleanups for base64_append() - add missing assertions, - use size_t for buffser size or memory index, - handle empty input more gracefully, - return the length or the result string, - fix off-by-one issue when the prefix is already long enough. --- src/basic/hexdecoct.c | 64 +++++++++++++++++++++++++++---------------- src/basic/hexdecoct.h | 10 +++++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 190fca8ee4..0922a49bb1 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -649,58 +649,74 @@ ssize_t base64mem_full( return z - r; } -static int base64_append_width( - char **prefix, int plen, - char sep, int indent, - const void *p, size_t l, - int width) { +static ssize_t base64_append_width( + char **prefix, + size_t plen, + char sep, + size_t indent, + const void *p, + size_t l, + size_t width) { _cleanup_free_ char *x = NULL; char *t, *s; - ssize_t len, avail, line, lines; + size_t lines; + ssize_t len; + + assert(prefix); + assert(*prefix || plen == 0); + assert(p || l == 0); len = base64mem(p, l, &x); - if (len <= 0) + if (len < 0) return len; + if (len == 0) + return plen; lines = DIV_ROUND_UP(len, width); - if ((size_t) plen >= SSIZE_MAX - 1 - 1 || + if (plen >= SSIZE_MAX - 1 - 1 || lines > (SSIZE_MAX - plen - 1 - 1) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, (ssize_t) plen + 1 + 1 + (indent + width + 1) * lines); + t = realloc(*prefix, plen + 1 + 1 + (indent + width + 1) * lines); if (!t) return -ENOMEM; - t[plen] = sep; + s = t + plen; + for (size_t line = 0; line < lines; line++) { + size_t act = MIN(width, (size_t) len); - for (line = 0, s = t + plen + 1, avail = len; line < lines; line++) { - int act = MIN(width, avail); + if (line > 0) + sep = '\n'; - if (line > 0 || sep == '\n') { - memset(s, ' ', indent); - s += indent; + if (s > t) { + *s++ = sep; + if (sep == '\n') + s = mempset(s, ' ', indent); } s = mempcpy(s, x + width * line, act); - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; + len -= act; } - assert(avail == 0); + assert(len == 0); + *s = '\0'; *prefix = t; - return 0; + return s - t; } -int base64_append( - char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { +ssize_t base64_append( + char **prefix, + size_t plen, + const void *p, + size_t l, + size_t indent, + size_t width) { if (plen > width / 2 || plen + indent > width) /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent - 1); + return base64_append_width(prefix, plen, '\n', indent, p, l, width - indent); else /* leave plen on the left, keep last column free */ return base64_append_width(prefix, plen, ' ', plen + 1, p, l, width - plen - 1); diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index 5218f78665..319b21a17c 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -38,9 +38,13 @@ static inline ssize_t base64mem(const void *p, size_t l, char **ret) { return base64mem_full(p, l, SIZE_MAX, ret); } -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); +ssize_t base64_append( + char **prefix, + size_t plen, + const void *p, + size_t l, + size_t margin, + size_t width); int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { return unbase64mem_full(p, l, false, mem, len); From 6b0df52e583feba49948f02bb6fedf8dedc5fd87 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 7 Dec 2022 07:01:48 +0900 Subject: [PATCH 2/6] test: add tests for base64_append() --- src/test/test-hexdecoct.c | 114 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index cc9a7cb838..4657307580 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -308,6 +308,120 @@ TEST(base64mem_linebreak) { } } +static void test_base64_append_one(char **buf, size_t *len, const char *in, const char *expected) { + ssize_t new_len; + + new_len = base64_append(buf, *len, in, strlen_ptr(in), 8, 12); + assert_se(new_len >= 0); + log_debug("base64_append_one(\"%s\")\nresult:\n%s\nexpected:\n%s", in, strnull(*buf), strnull(expected)); + assert_se((size_t) new_len == strlen_ptr(*buf)); + assert_se(streq_ptr(*buf, expected)); + *len = new_len; +} + +TEST(base64_append) { + _cleanup_free_ char *buf = NULL; + size_t len = 0; + + test_base64_append_one(&buf, &len, "", NULL); + test_base64_append_one(&buf, &len, "f", + "Zg=="); + test_base64_append_one(&buf, &len, "fo", + "Zg== Zm8="); + test_base64_append_one(&buf, &len, "foo", + "Zg== Zm8=\n" + " Zm9v"); + test_base64_append_one(&buf, &len, "foob", + "Zg== Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg=="); + test_base64_append_one(&buf, &len, "fooba", + "Zg== Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg==\n" + " Zm9v\n" + " YmE="); + test_base64_append_one(&buf, &len, "foobar", + "Zg== Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg==\n" + " Zm9v\n" + " YmE=\n" + " Zm9v\n" + " YmFy"); + + assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0); + len = strlen(buf); + + test_base64_append_one(&buf, &len, "", + "hogehogehogehoge"); + test_base64_append_one(&buf, &len, "f", + "hogehogehogehoge\n" + " Zg=="); + test_base64_append_one(&buf, &len, "fo", + "hogehogehogehoge\n" + " Zg==\n" + " Zm8="); + test_base64_append_one(&buf, &len, "foo", + "hogehogehogehoge\n" + " Zg==\n" + " Zm8=\n" + " Zm9v"); + test_base64_append_one(&buf, &len, "foob", + "hogehogehogehoge\n" + " Zg==\n" + " Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg=="); + test_base64_append_one(&buf, &len, "fooba", + "hogehogehogehoge\n" + " Zg==\n" + " Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg==\n" + " Zm9v\n" + " YmE="); + test_base64_append_one(&buf, &len, "foobar", + "hogehogehogehoge\n" + " Zg==\n" + " Zm8=\n" + " Zm9v\n" + " Zm9v\n" + " Yg==\n" + " Zm9v\n" + " YmE=\n" + " Zm9v\n" + " YmFy"); + + assert_se(free_and_strdup(&buf, "hogehogehogehoge") >= 0); + len = strlen(buf); + + test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar", + "hogehogehogehoge\n" + " Zm9v\n" + " YmFy\n" + " Zm9v\n" + " YmFy\n" + " Zm9v\n" + " YmFy\n" + " Zm9v\n" + " YmFy"); + + assert_se(free_and_strdup(&buf, "aaa") >= 0); + len = strlen(buf); + + test_base64_append_one(&buf, &len, "foobarfoobarfoobarfoobar", + "aaa Zm9vYmFy\n" + " Zm9vYmFy\n" + " Zm9vYmFy\n" + " Zm9vYmFy"); +} + static void test_unbase64mem_one(const char *input, const char *output, int ret) { _cleanup_free_ void *buffer = NULL; size_t size = 0; From 45655e776f3a4deaf68cc21a7716eba5181cbd49 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 7 Dec 2022 06:58:23 +0900 Subject: [PATCH 3/6] hexdecoct: add missing NULL check Fixes oss-fuzz#54065 (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=54065). Fixes #25650. --- src/basic/hexdecoct.c | 2 +- test/fuzz/fuzz-resource-record/oss-fuzz-54065 | Bin 0 -> 11 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 test/fuzz/fuzz-resource-record/oss-fuzz-54065 diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 0922a49bb1..b9de5bfcda 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -606,7 +606,7 @@ ssize_t base64mem_full( if (!r) return -ENOMEM; - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { + for (x = p; x && x < (const uint8_t*) p + (l / 3) * 3; x += 3) { /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ maybe_line_break(&z, r, line_break); *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54065 b/test/fuzz/fuzz-resource-record/oss-fuzz-54065 new file mode 100644 index 0000000000000000000000000000000000000000..943945213f2510f72d757a10bd6d63c1bc745095 GIT binary patch literal 11 OcmZQzuvGv91_l5IPyqe_ literal 0 HcmV?d00001 From 7d34567444304ea0acec7ed3c44c09bb65cea32c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 7 Dec 2022 09:06:48 +0900 Subject: [PATCH 4/6] hexdecoct: fix NULL pointer dereferences in hexmem() Fixes oss-fuzz#54090 (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=54090). Fixes #25655. --- src/basic/hexdecoct.c | 4 +++- src/test/test-hexdecoct.c | 19 ++++++++++++++++++ test/fuzz/fuzz-resource-record/oss-fuzz-54090 | Bin 0 -> 110 bytes 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 test/fuzz/fuzz-resource-record/oss-fuzz-54090 diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index b9de5bfcda..0ff8eb3256 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -59,11 +59,13 @@ char *hexmem(const void *p, size_t l) { const uint8_t *x; char *r, *z; + assert(p || l == 0); + z = r = new(char, l * 2 + 1); if (!r) return NULL; - for (x = p; x < (const uint8_t*) p + l; x++) { + for (x = p; x && x < (const uint8_t*) p + l; x++) { *(z++) = hexchar(*x >> 4); *(z++) = hexchar(*x & 15); } diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index 4657307580..afdc3b5436 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -73,6 +73,25 @@ TEST(undecchar) { assert_se(undecchar('9') == 9); } +static void test_hexmem_one(const char *in, const char *expected) { + _cleanup_free_ char *result = NULL; + _cleanup_free_ void *mem = NULL; + size_t len; + + assert_se(result = hexmem(in, strlen_ptr(in))); + log_debug("hexmem(\"%s\") → \"%s\" (expected: \"%s\")", strnull(in), result, expected); + assert_se(streq(result, expected)); + + assert_se(unhexmem(result, SIZE_MAX, &mem, &len) >= 0); + assert_se(memcmp_safe(mem, in, len) == 0); +} + +TEST(hexmem) { + test_hexmem_one(NULL, ""); + test_hexmem_one("", ""); + test_hexmem_one("foo", "666f6f"); +} + static void test_unhexmem_one(const char *s, size_t l, int retval) { _cleanup_free_ char *hex = NULL; _cleanup_free_ void *mem = NULL; diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54090 b/test/fuzz/fuzz-resource-record/oss-fuzz-54090 new file mode 100644 index 0000000000000000000000000000000000000000..994d908d0f3753c12a3084576224086cab94cde4 GIT binary patch literal 110 zcmZQzFk)fgnDgI}A(nxGNt=O@5r{YhuY!RV0|UeM{~*BpUQY9znVFHf8BmCU;Rhol mD@+Yo`u~49O#=|HFv-*Y4^;6FNN8y-(b7swV*~-PHU Date: Wed, 7 Dec 2022 09:35:41 +0900 Subject: [PATCH 5/6] resolve: optimize conversion of TXT fields to json Fixes oss-fuzz#54080 (https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=54080). Fixes #25654. --- src/resolve/resolved-dns-rr.c | 50 ++++++++++++------ test/fuzz/fuzz-resource-record/oss-fuzz-54080 | Bin 0 -> 29703 bytes 2 files changed, 33 insertions(+), 17 deletions(-) create mode 100644 test/fuzz/fuzz-resource-record/oss-fuzz-54080 diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index f4fa219ab7..a4ba6bdc6d 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -1889,6 +1889,36 @@ static int type_bitmap_to_json(Bitmap *b, JsonVariant **ret) { return 0; } +static int txt_to_json(DnsTxtItem *items, JsonVariant **ret) { + JsonVariant **elements = NULL; + size_t n = 0; + int r; + + assert(ret); + + LIST_FOREACH(items, i, items) { + if (!GREEDY_REALLOC(elements, n + 1)) { + r = -ENOMEM; + goto finalize; + } + + r = json_variant_new_octescape(elements + n, i->data, i->length); + if (r < 0) + goto finalize; + + n++; + } + + r = json_variant_new_array(ret, elements, n); + +finalize: + for (size_t i = 0; i < n; i++) + json_variant_unref(elements[i]); + + free(elements); + return r; +} + int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *k = NULL; int r; @@ -1931,23 +1961,9 @@ int dns_resource_record_to_json(DnsResourceRecord *rr, JsonVariant **ret) { case DNS_TYPE_TXT: { _cleanup_(json_variant_unrefp) JsonVariant *l = NULL; - LIST_FOREACH(items, i, rr->txt.items) { - _cleanup_(json_variant_unrefp) JsonVariant *b = NULL; - - r = json_variant_new_octescape(&b, i->data, i->length); - if (r < 0) - return r; - - r = json_variant_append_array(&l, b); - if (r < 0) - return r; - } - - if (!l) { - r = json_variant_new_array(&l, NULL, 0); - if (r < 0) - return r; - } + r = txt_to_json(rr->txt.items, &l); + if (r < 0) + return r; return json_build(ret, JSON_BUILD_OBJECT( diff --git a/test/fuzz/fuzz-resource-record/oss-fuzz-54080 b/test/fuzz/fuzz-resource-record/oss-fuzz-54080 new file mode 100644 index 0000000000000000000000000000000000000000..2577e653d44bf96c1d5d5d3d8460c59f9cf2256a GIT binary patch literal 29703 zcmeI(&1%9x5Ww-#y?7}I1<&HC^w3x6*+&q(Sm<-)>Z|k_?7OAeB!w8VxMR>{Xa9Q$ zn*DI*7tk+KmY**0s?*~pw|K;IgNk~fj1jzL}syw9LF#O<|{zG zZob!s$PhpP0R(avP?YNqxm%Gx0tg_W6X@0I{9D}uj7A_afu!qo%|lEa6BlsKy{>#~ zpGr$Sosr?Yk@K0300LtLoLi1vBBS|{H$9XvBB2Fa9n8i6SUsPt*nBkx41^hH833;_fXPz&&9B{dln5HKsi_kr0 Date: Fri, 9 Dec 2022 04:58:01 +0900 Subject: [PATCH 6/6] fuzz: shorten filename of testcase Follow-up for 46dc071985ff487f5ccf20808531168a6add73d3 and 76519cecc749a3d0e2054fd6db8a99143666e123. --- ...uzz-bootspec-5731869371269120 => oss-fuzz-53578} | 0 ...ource-record-5844226627993600 => oss-fuzz-54059} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename test/fuzz/fuzz-bootspec/{clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120 => oss-fuzz-53578} (100%) rename test/fuzz/fuzz-resource-record/{clusterfuzz-testcase-minimized-fuzz-resource-record-5844226627993600 => oss-fuzz-54059} (100%) diff --git a/test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120 b/test/fuzz/fuzz-bootspec/oss-fuzz-53578 similarity index 100% rename from test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120 rename to test/fuzz/fuzz-bootspec/oss-fuzz-53578 diff --git a/test/fuzz/fuzz-resource-record/clusterfuzz-testcase-minimized-fuzz-resource-record-5844226627993600 b/test/fuzz/fuzz-resource-record/oss-fuzz-54059 similarity index 100% rename from test/fuzz/fuzz-resource-record/clusterfuzz-testcase-minimized-fuzz-resource-record-5844226627993600 rename to test/fuzz/fuzz-resource-record/oss-fuzz-54059