From 2bb6208c57aa036f9ad58fecf7ab08d7bb79072f Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Wed, 9 Nov 2022 17:32:05 +0000 Subject: [PATCH 1/4] Avoid unspecified behaviour in name_test C does not make any guarantees about the value of padding in a structure, so bytewise comparison of two semantically equal structures with padding can be spuriously non-equal due to non-equal padding bytes. Compare each member of name.attributes individually to avoid this problem. --- tests/dns/name_test.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/dns/name_test.c b/tests/dns/name_test.c index 441c3c3923..d38ab7cea0 100644 --- a/tests/dns/name_test.c +++ b/tests/dns/name_test.c @@ -500,11 +500,20 @@ ISC_RUN_TEST_IMPL(istat) { } } +static bool +name_attr_zero(struct dns_name_attrs attributes) { + return (!(attributes.absolute | attributes.readonly | + attributes.dynamic | attributes.dynoffsets | + attributes.nocompress | attributes.cache | attributes.answer | + attributes.ncache | attributes.chaining | attributes.chase | + attributes.wildcard | attributes.prerequisite | + attributes.update | attributes.hasupdaterec)); +} + /* dns_nane_init */ ISC_RUN_TEST_IMPL(init) { dns_name_t name; unsigned char offsets[1]; - struct dns_name_attrs zeroes = {}; UNUSED(state); @@ -513,16 +522,15 @@ ISC_RUN_TEST_IMPL(init) { assert_null(name.ndata); assert_int_equal(name.length, 0); assert_int_equal(name.labels, 0); - assert_memory_equal(&name.attributes, &zeroes, sizeof(zeroes)); assert_ptr_equal(name.offsets, offsets); assert_null(name.buffer); + assert_true(name_attr_zero(name.attributes)); } /* dns_nane_invalidate */ ISC_RUN_TEST_IMPL(invalidate) { dns_name_t name; unsigned char offsets[1]; - struct dns_name_attrs zeroes = {}; UNUSED(state); @@ -532,9 +540,9 @@ ISC_RUN_TEST_IMPL(invalidate) { assert_null(name.ndata); assert_int_equal(name.length, 0); assert_int_equal(name.labels, 0); - assert_memory_equal(&name.attributes, &zeroes, sizeof(zeroes)); assert_null(name.offsets); assert_null(name.buffer); + assert_true(name_attr_zero(name.attributes)); } /* dns_nane_setbuffer/hasbuffer */ From e0c969234174e3c3d93016ea4a12a1c467df0698 Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Wed, 9 Nov 2022 16:51:34 +0000 Subject: [PATCH 2/4] Clean up remnants of label types There were a few comments referring obliquely to different kinds of labels, which became obsolete a long time ago. --- lib/dns/include/dns/name.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 1cf7129b81..036a151f30 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -31,9 +31,9 @@ * makes adding names to messages easy. Having much of the server know * the representation would be perilous, and we certainly don't want each * user of names to be manipulating such a low-level structure. This is - * where the Names and Labels module comes in. The module allows name or - * label handles to be created and attached to uncompressed wire format - * regions. All name operations and conversions are done through these + * where the Names and Labels module comes in. The module allows name + * handles to be created and attached to uncompressed wire format + * regions. All name operations and conversions are done through these * handles. * * MP: @@ -56,7 +56,6 @@ * Standards: *\li RFC1035 *\li Draft EDNS0 (0) - *\li Draft Binary Labels (2) * */ @@ -76,18 +75,11 @@ ISC_LANG_BEGINDECLS -/***** -***** Labels -***** -***** A 'label' is basically a region. It contains one DNS wire format -***** label of type 00 (ordinary). -*****/ - /***** ***** Names ***** ***** A 'name' is a handle to a binary region. It contains a sequence of one -***** or more DNS wire format labels of type 00 (ordinary). +***** or more DNS wire format labels. ***** Note that all names are not required to end with the root label, ***** as they are in the actual DNS wire protocol. *****/ From 1c0f6078116607075a375e9c75304551ebd2fd44 Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Mon, 7 Nov 2022 14:00:45 +0000 Subject: [PATCH 3/4] Simplify and speed up DNS name decompression The aim is to do less work per byte: * Check the bounds for each label, instead of checking the bounds for each character. * Instead of copying one character at a time from the wire to the name, copy entire runs of sequential labels using memmove() to make the most of its fast loop. * To remember where the name ends, we only need to set the end marker when we see a compression pointer or when we reach the root label. There is no need to check if we jumped back and conditionally update the counter for every character. * To parse a compression pointer, we no longer take a diversion around the outer loop in between reading the upper byte of the pointer and the lower byte. * The parser state machine is now implicit in the instruction pointer, instead of being an explicit variable. Similarly, when we reach the root label we break directly out of the loop instead of setting a second state machine variable. * DNS_NAME_DOWNCASE is never used with dns_name_fromwire() so that option is no longer supported. I have removed this comment which dated from January 1999 when dns_name_fromwire() was first introduced: /* * Note: The following code is not optimized for speed, but * rather for correctness. Speed will be addressed in the future. */ No functional change, apart from removing support for the unused DNS_NAME_DOWNCASE option. The new code is about 2x faster than the old code: best case 11x faster, worst case 1.4x faster. --- CHANGES | 3 + lib/dns/include/dns/name.h | 11 +- lib/dns/name.c | 281 ++++++++++++++++++------------------- 3 files changed, 145 insertions(+), 150 deletions(-) diff --git a/CHANGES b/CHANGES index 076989f185..c39458f765 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +6022. [performance] The decompression implementation in dns_name_fromwire() + is now smaller and faster. [GL #3655] + 6021. [bug] Use the current domain name when checking answers from a dual-stack-server. [GL #3607] diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 036a151f30..423f6fea09 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -689,9 +689,6 @@ dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, * Notes: * \li Decompression policy is controlled by 'dctx'. * - * \li If DNS_NAME_DOWNCASE is set, any uppercase letters in 'source' will be - * downcased when they are copied into 'target'. - * * Security: * * \li *** WARNING *** @@ -712,13 +709,12 @@ dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, * * \li 'dctx' is a valid decompression context. * + * \li DNS_NAME_DOWNCASE is not set. + * * Ensures: * * If result is success: - * \li If 'target' is not NULL, 'name' is attached to it. - * - * \li Uppercase letters are downcased in the copy iff - * DNS_NAME_DOWNCASE is set in options. + * \li If 'target' is not NULL, 'name' is attached to it. * * \li The current location in source is advanced, and the used space * in target is updated. @@ -731,7 +727,6 @@ dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, * \li Bad Form: Compression type not allowed * \li Bad Form: Bad compression pointer * \li Bad Form: Input too short - * \li Resource Limit: Too many compression pointers * \li Resource Limit: Not enough space in buffer */ diff --git a/lib/dns/name.c b/lib/dns/name.c index 96586def8c..5ce5969815 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -47,8 +47,6 @@ typedef enum { ft_at } ft_state; -typedef enum { fw_start = 0, fw_ordinary, fw_newcurrent } fw_state; - #define INIT_OFFSETS(name, var, default_offsets) \ if ((name)->offsets != NULL) \ var = (name)->offsets; \ @@ -1520,175 +1518,174 @@ set_offsets(const dns_name_t *name, unsigned char *offsets, } isc_result_t -dns_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, - unsigned int options, isc_buffer_t *target) { - unsigned char *cdata, *ndata; - unsigned int cused; /* Bytes of compressed name data used */ - unsigned int nused, labels, n, nmax; - unsigned int current, new_current, biggest_pointer; - bool done; - fw_state state = fw_start; - unsigned int c; - unsigned char *offsets; - dns_offsets_t odata; - bool downcase; - bool seen_pointer; - +dns_name_fromwire(dns_name_t *const name, isc_buffer_t *const source, + const dns_decompress_t dctx, unsigned int options, + isc_buffer_t *target) { /* - * Copy the possibly-compressed name at source into target, - * decompressing it. Loop prevention is performed by checking - * the new pointer against biggest_pointer. + * Copy the name at source into target, decompressing it. + * + * *** WARNING *** + * + * dns_name_fromwire() deals with raw network data. An error in this + * routine could result in the failure or hijacking of the server. + * + * The description of name compression in RFC 1035 section 4.1.4 is + * subtle wrt certain edge cases. The first important sentence is: + * + * > In this scheme, an entire domain name or a list of labels at the + * > end of a domain name is replaced with a pointer to a prior + * > occurance of the same name. + * + * The key word is "prior". This says that compression pointers must + * point strictly earlier in the message (before our "marker" variable), + * which is enough to prevent DoS attacks due to compression loops. + * + * The next important sentence is: + * + * > If a domain name is contained in a part of the message subject to a + * > length field (such as the RDATA section of an RR), and compression + * > is used, the length of the compressed name is used in the length + * > calculation, rather than the length of the expanded name. + * + * When decompressing, this means that the amount of the source buffer + * that we consumed (which is checked wrt the container's length field) + * is the length of the compressed name. A compressed name is defined as + * a sequence of labels ending with the root label or a compression + * pointer, that is, the segment of the name that dns_name_fromwire() + * examines first. + * + * This matters when handling names that play dirty tricks, like: + * + * +---+---+---+---+---+---+ + * | 4 | 1 |'a'|192| 0 | 0 | + * +---+---+---+---+---+---+ + * + * We start at octet 1. There is an ordinary single character label "a", + * followed by a compression pointer that refers back to octet zero. + * Here there is a label of length 4, which weirdly re-uses the octets + * we already examined as the data for the label. It is followed by the + * root label, + * + * The specification says that the compressed name ends after the first + * zero octet (after the compression pointer) not the second zero octet, + * even though the second octet is later in the message. This shows the + * correct way to set our "consumed" variable. */ + REQUIRE((options & DNS_NAME_DOWNCASE) == 0); REQUIRE(VALID_NAME(name)); + REQUIRE(BINDABLE(name)); REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || (target == NULL && ISC_BUFFER_VALID(name->buffer))); - downcase = ((options & DNS_NAME_DOWNCASE) != 0); - if (target == NULL && name->buffer != NULL) { target = name->buffer; isc_buffer_clear(target); } - REQUIRE(BINDABLE(name)); + uint8_t *const name_buf = isc_buffer_used(target); + const uint32_t name_max = ISC_MIN(DNS_NAME_MAXWIRE, + isc_buffer_availablelength(target)); + uint32_t name_len = 0; + MAKE_EMPTY(name); /* in case of failure */ + dns_offsets_t odata; + uint8_t *offsets = NULL; + uint32_t labels = 0; INIT_OFFSETS(name, offsets, odata); /* - * Make 'name' empty in case of failure. + * After chasing a compression pointer, these variables refer to the + * source buffer as follows: + * + * sb --- mr --- cr --- st --- cd --- sm + * + * sb = source_buf (const) + * mr = marker + * cr = cursor + * st = start (const) + * cd = consumed + * sm = source_max (const) + * + * The marker hops backwards for each pointer. + * The cursor steps forwards for each label. + * The amount of the source we consumed is set once. */ - MAKE_EMPTY(name); + const uint8_t *const source_buf = isc_buffer_base(source); + const uint8_t *const source_max = isc_buffer_used(source); + const uint8_t *const start = isc_buffer_current(source); + const uint8_t *marker = start; + const uint8_t *cursor = start; + const uint8_t *consumed = NULL; /* - * Initialize things to make the compiler happy; they're not required. + * One iteration per label. */ - n = 0; - new_current = 0; - - /* - * Set up. - */ - labels = 0; - done = false; - - ndata = isc_buffer_used(target); - nused = 0; - seen_pointer = false; - - /* - * Find the maximum number of uncompressed target name - * bytes we are willing to generate. This is the smaller - * of the available target buffer length and the - * maximum legal domain name length (255). - */ - nmax = isc_buffer_availablelength(target); - if (nmax > DNS_NAME_MAXWIRE) { - nmax = DNS_NAME_MAXWIRE; - } - - cdata = isc_buffer_current(source); - cused = 0; - - current = source->current; - biggest_pointer = current; - - /* - * Note: The following code is not optimized for speed, but - * rather for correctness. Speed will be addressed in the future. - */ - - while (current < source->active && !done) { - c = *cdata++; - current++; - if (!seen_pointer) { - cused++; - } - - switch (state) { - case fw_start: - if (c < 64) { - offsets[labels] = nused; - labels++; - if (nused + c + 1 > nmax) { - goto full; - } - nused += c + 1; - *ndata++ = c; - if (c == 0) { - done = true; - } - n = c; - state = fw_ordinary; - } else if (c >= 192) { - /* - * 14-bit compression pointer - */ - if (!dns_decompress_getpermitted(dctx)) { - return (DNS_R_DISALLOWED); - } - new_current = c & 0x3F; - state = fw_newcurrent; - } else { - return (DNS_R_BADLABELTYPE); + while (cursor < source_max) { + const uint8_t label_len = *cursor++; + if (label_len < 64) { + /* + * Normal label: record its offset, and check bounds on + * the name length, which also ensures we don't overrun + * the offsets array. Don't touch any source bytes yet! + * The source bounds check will happen when we loop. + */ + offsets[labels++] = name_len; + /* and then a step to the ri-i-i-i-i-ight */ + cursor += label_len; + name_len += label_len + 1; + if (name_len > name_max) { + return (name_max == DNS_NAME_MAXWIRE + ? DNS_R_NAMETOOLONG + : ISC_R_NOSPACE); + } else if (label_len == 0) { + goto root_label; } - break; - case fw_ordinary: - if (downcase) { - c = isc_ascii_tolower(c); - } - *ndata++ = c; - n--; - if (n == 0) { - state = fw_start; - } - break; - case fw_newcurrent: - new_current *= 256; - new_current += c; - if (new_current >= biggest_pointer) { + } else if (label_len < 192) { + return (DNS_R_BADLABELTYPE); + } else if (!dns_decompress_getpermitted(dctx)) { + return (DNS_R_DISALLOWED); + } else if (cursor < source_max) { + /* + * Compression pointer. Ensure it does not loop. + * + * Copy multiple labels in one go, to make the most of + * memmove() performance. Start at the marker and finish + * just before the pointer's hi+lo bytes, before the + * cursor. Bounds were already checked. + */ + const uint32_t hi = label_len & 0x3F; + const uint32_t lo = *cursor++; + const uint8_t *pointer = source_buf + (256 * hi + lo); + if (pointer >= marker) { return (DNS_R_BADPOINTER); } - biggest_pointer = new_current; - current = new_current; - cdata = (unsigned char *)source->base + current; - seen_pointer = true; - state = fw_start; - break; - default: - FATAL_ERROR("Unknown state %d", state); - /* Does not return. */ + const uint32_t copy_len = (cursor - 2) - marker; + uint8_t *const dest = name_buf + name_len - copy_len; + memmove(dest, marker, copy_len); + consumed = consumed != NULL ? consumed : cursor; + /* it's just a jump to the left */ + cursor = marker = pointer; } } + return (ISC_R_UNEXPECTEDEND); +root_label:; + /* + * Copy labels almost like we do for compression pointers, + * from the marker up to and including the root label. + */ + const uint32_t copy_len = cursor - marker; + memmove(name_buf + name_len - copy_len, marker, copy_len); + consumed = consumed != NULL ? consumed : cursor; + isc_buffer_forward(source, consumed - start); - if (!done) { - return (ISC_R_UNEXPECTEDEND); - } - - name->ndata = (unsigned char *)target->base + target->used; - name->labels = labels; - name->length = nused; name->attributes.absolute = true; - - isc_buffer_forward(source, cused); - isc_buffer_add(target, name->length); + name->ndata = name_buf; + name->labels = labels; + name->length = name_len; + isc_buffer_add(target, name_len); return (ISC_R_SUCCESS); - -full: - if (nmax == DNS_NAME_MAXWIRE) { - /* - * The name did not fit even though we had a buffer - * big enough to fit a maximum-length name. - */ - return (DNS_R_NAMETOOLONG); - } else { - /* - * The name might fit if only the caller could give us a - * big enough buffer. - */ - return (ISC_R_NOSPACE); - } } isc_result_t From 04f3000dfc4c1081c9e1e4752cf47c792e853d6f Mon Sep 17 00:00:00 2001 From: Tony Finch Date: Mon, 7 Nov 2022 16:22:48 +0000 Subject: [PATCH 4/4] Fuzzing and benchmarking for dns_name_fromwire() Since this is very sensitive code which has often had security problems in many DNS implementations, it needs a decent amount of validation. This fuzzer ensures that the new code has the same output as the old code, and that it doesn't take longer than a second. The benchmark uses the fuzzer's copy of the old dns_name_fromwire() code to compare a number of scenarios: many compression pointers, many labels, long labels, random data, with/without downcasing. --- fuzz/.gitignore | 1 + fuzz/Makefile.am | 7 + fuzz/dns_name_fromwire.c | 104 ++++++++ .../00b28ff06b788b9b67c6b259800f404f9f3761fd | Bin 0 -> 2 bytes .../02440c9e09b9ccb3c36d1ae3b1dc395d39dd2e61 | Bin 0 -> 19 bytes .../02a01a03cc76d29a0d3f819e9f6e95266f4ee4eb | Bin 0 -> 45 bytes .../03de4865464d9fee5cf614c2e8196cb0246b60f3 | Bin 0 -> 9 bytes .../0c932286228aafdb4bbcf9d39f4554d814e7c1cb | Bin 0 -> 67 bytes .../12248b2bb3063f4f6aca8ec227aa26f24299e53b | Bin 0 -> 9 bytes .../2c17e4e73e0ee81d3ed7279f3fbc51d35e8837a1 | Bin 0 -> 41 bytes .../302f0fe41e58a99c91f22062cda18d4683dc702f | Bin 0 -> 103 bytes .../43f90bb183e988637abbcbc50be4a89ebff60d9a | Bin 0 -> 26 bytes .../448daf1db0846c67ecb7a533361647cb41e5dce2 | Bin 0 -> 6 bytes .../477cdb78a23d07e2fc8103c4c79c9a545bcec06b | Bin 0 -> 16 bytes .../4b345f6450981c1ed938081331eeabd03598cb20 | Bin 0 -> 7 bytes .../5821576d8de31746675cc0ea7f9e652438070dfe | Bin 0 -> 4 bytes .../608fa82ab128dfb43c9a064bfab677d9b5f3f72b | 1 + .../61c70af9c1b0588393243703eb472271290f88eb | Bin 0 -> 256 bytes .../680357a1752af34a7f882cced6b1e5fd8ecc4bcc | Bin 0 -> 4 bytes .../69abe6811b3437087b53d4c4e9157d4ba7a0890b | Bin 0 -> 7 bytes .../69d86c5a071a2d76679d54da225e955c35c40db1 | Bin 0 -> 60 bytes .../6cd63cd014fa252d9f5e62129e05dc6cbd75a7e3 | Bin 0 -> 25 bytes .../7c20e8ac688f92b55099a7f63ff2a4f4bcd7704a | 1 + .../7d8a23740ae15d7d3dfde25ad1538ae9b9d52265 | Bin 0 -> 18 bytes .../88188ee25030661f21a4dacca02caeb7eff34235 | Bin 0 -> 7 bytes .../93dccf807dc4df3bfc1711ef3ad3bbd65a7d8c67 | 1 + .../955cad73af85bdac5a701aea20a086c75585ca74 | Bin 0 -> 8 bytes .../9a279b27d765ca2eccc67e1fc8bafb0fa8702208 | 2 + .../9d30975bf630a3203ed71154257ebcfc19d04067 | Bin 0 -> 109 bytes .../9feb4af1c2bb4444e9417099b21c6b25f7221dee | Bin 0 -> 20 bytes .../a3f75fec735667f1c1218f53e073f25586dc0813 | Bin 0 -> 24 bytes .../b473a861dafebb3cd6ca6b92609da9e40eeea68e | Bin 0 -> 32 bytes .../b8811cd9f785b9fb458236875fda90d048383689 | Bin 0 -> 14 bytes .../bf546f247bb7a3672a04bb4c9e3b5049f981d435 | Bin 0 -> 16 bytes .../bf7b68e9cc0467f7bbbfe1dc0dfd7c3124b3695c | 1 + .../bfb829dd666f7fb4c6d9ae55f58503967cf1b53b | Bin 0 -> 64 bytes .../c351ad42ccc7485a0ad6e1ae8cc07d66ee9b045a | Bin 0 -> 6 bytes .../cc6cba815d062e0e250463aad2113fdcafd48571 | Bin 0 -> 9 bytes .../db6cca49e957261d7a262fbf6dafcac01353f1e4 | Bin 0 -> 4 bytes .../de4e7188e5540bd15eb83ddb5a784c6dc35d8d94 | Bin 0 -> 79 bytes .../df58248c414f342c81e056b40bee12d17a08bf61 | 1 + .../df7467bb9625092ba9d23c0d8e4f29c7f4e23dd1 | Bin 0 -> 4 bytes .../e05d045edfa8ebe33c73b6459e7d704e0c958c74 | Bin 0 -> 46 bytes .../e23b6fdeadc0379b095bc1ffb8a1d2101ebe76b1 | Bin 0 -> 9 bytes .../e333716351a0ded128bbb67ba46b5017a16ae6bf | Bin 0 -> 4 bytes .../e52912805d2e9212d53a46426e160813124be117 | Bin 0 -> 15 bytes .../f4a59b7257dbd0777e57e6e25689d374f4d771bc | Bin 0 -> 83 bytes .../fc712b19cbed4a8b4db3226dfbfdf3fb7be7690a | Bin 0 -> 37 bytes .../fda8ce1c09f3e385a5c0798f9ee158a4ca7d449f | Bin 0 -> 7 bytes .../fe38c27050137b9ce57189c681beb2b59a539a98 | Bin 0 -> 10 bytes fuzz/old.c | 223 ++++++++++++++++++ fuzz/old.h | 21 ++ tests/bench/.gitignore | 1 + tests/bench/Makefile.am | 9 +- tests/bench/dns_name_fromwire.c | 145 ++++++++++++ 55 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 fuzz/dns_name_fromwire.c create mode 100644 fuzz/dns_name_fromwire.in/00b28ff06b788b9b67c6b259800f404f9f3761fd create mode 100644 fuzz/dns_name_fromwire.in/02440c9e09b9ccb3c36d1ae3b1dc395d39dd2e61 create mode 100644 fuzz/dns_name_fromwire.in/02a01a03cc76d29a0d3f819e9f6e95266f4ee4eb create mode 100644 fuzz/dns_name_fromwire.in/03de4865464d9fee5cf614c2e8196cb0246b60f3 create mode 100644 fuzz/dns_name_fromwire.in/0c932286228aafdb4bbcf9d39f4554d814e7c1cb create mode 100644 fuzz/dns_name_fromwire.in/12248b2bb3063f4f6aca8ec227aa26f24299e53b create mode 100644 fuzz/dns_name_fromwire.in/2c17e4e73e0ee81d3ed7279f3fbc51d35e8837a1 create mode 100644 fuzz/dns_name_fromwire.in/302f0fe41e58a99c91f22062cda18d4683dc702f create mode 100644 fuzz/dns_name_fromwire.in/43f90bb183e988637abbcbc50be4a89ebff60d9a create mode 100644 fuzz/dns_name_fromwire.in/448daf1db0846c67ecb7a533361647cb41e5dce2 create mode 100644 fuzz/dns_name_fromwire.in/477cdb78a23d07e2fc8103c4c79c9a545bcec06b create mode 100644 fuzz/dns_name_fromwire.in/4b345f6450981c1ed938081331eeabd03598cb20 create mode 100644 fuzz/dns_name_fromwire.in/5821576d8de31746675cc0ea7f9e652438070dfe create mode 100644 fuzz/dns_name_fromwire.in/608fa82ab128dfb43c9a064bfab677d9b5f3f72b create mode 100644 fuzz/dns_name_fromwire.in/61c70af9c1b0588393243703eb472271290f88eb create mode 100644 fuzz/dns_name_fromwire.in/680357a1752af34a7f882cced6b1e5fd8ecc4bcc create mode 100644 fuzz/dns_name_fromwire.in/69abe6811b3437087b53d4c4e9157d4ba7a0890b create mode 100644 fuzz/dns_name_fromwire.in/69d86c5a071a2d76679d54da225e955c35c40db1 create mode 100644 fuzz/dns_name_fromwire.in/6cd63cd014fa252d9f5e62129e05dc6cbd75a7e3 create mode 100644 fuzz/dns_name_fromwire.in/7c20e8ac688f92b55099a7f63ff2a4f4bcd7704a create mode 100644 fuzz/dns_name_fromwire.in/7d8a23740ae15d7d3dfde25ad1538ae9b9d52265 create mode 100644 fuzz/dns_name_fromwire.in/88188ee25030661f21a4dacca02caeb7eff34235 create mode 100644 fuzz/dns_name_fromwire.in/93dccf807dc4df3bfc1711ef3ad3bbd65a7d8c67 create mode 100644 fuzz/dns_name_fromwire.in/955cad73af85bdac5a701aea20a086c75585ca74 create mode 100644 fuzz/dns_name_fromwire.in/9a279b27d765ca2eccc67e1fc8bafb0fa8702208 create mode 100644 fuzz/dns_name_fromwire.in/9d30975bf630a3203ed71154257ebcfc19d04067 create mode 100644 fuzz/dns_name_fromwire.in/9feb4af1c2bb4444e9417099b21c6b25f7221dee create mode 100644 fuzz/dns_name_fromwire.in/a3f75fec735667f1c1218f53e073f25586dc0813 create mode 100644 fuzz/dns_name_fromwire.in/b473a861dafebb3cd6ca6b92609da9e40eeea68e create mode 100644 fuzz/dns_name_fromwire.in/b8811cd9f785b9fb458236875fda90d048383689 create mode 100644 fuzz/dns_name_fromwire.in/bf546f247bb7a3672a04bb4c9e3b5049f981d435 create mode 100644 fuzz/dns_name_fromwire.in/bf7b68e9cc0467f7bbbfe1dc0dfd7c3124b3695c create mode 100644 fuzz/dns_name_fromwire.in/bfb829dd666f7fb4c6d9ae55f58503967cf1b53b create mode 100644 fuzz/dns_name_fromwire.in/c351ad42ccc7485a0ad6e1ae8cc07d66ee9b045a create mode 100644 fuzz/dns_name_fromwire.in/cc6cba815d062e0e250463aad2113fdcafd48571 create mode 100644 fuzz/dns_name_fromwire.in/db6cca49e957261d7a262fbf6dafcac01353f1e4 create mode 100644 fuzz/dns_name_fromwire.in/de4e7188e5540bd15eb83ddb5a784c6dc35d8d94 create mode 100644 fuzz/dns_name_fromwire.in/df58248c414f342c81e056b40bee12d17a08bf61 create mode 100644 fuzz/dns_name_fromwire.in/df7467bb9625092ba9d23c0d8e4f29c7f4e23dd1 create mode 100644 fuzz/dns_name_fromwire.in/e05d045edfa8ebe33c73b6459e7d704e0c958c74 create mode 100644 fuzz/dns_name_fromwire.in/e23b6fdeadc0379b095bc1ffb8a1d2101ebe76b1 create mode 100644 fuzz/dns_name_fromwire.in/e333716351a0ded128bbb67ba46b5017a16ae6bf create mode 100644 fuzz/dns_name_fromwire.in/e52912805d2e9212d53a46426e160813124be117 create mode 100644 fuzz/dns_name_fromwire.in/f4a59b7257dbd0777e57e6e25689d374f4d771bc create mode 100644 fuzz/dns_name_fromwire.in/fc712b19cbed4a8b4db3226dfbfdf3fb7be7690a create mode 100644 fuzz/dns_name_fromwire.in/fda8ce1c09f3e385a5c0798f9ee158a4ca7d449f create mode 100644 fuzz/dns_name_fromwire.in/fe38c27050137b9ce57189c681beb2b59a539a98 create mode 100644 fuzz/old.c create mode 100644 fuzz/old.h create mode 100644 tests/bench/dns_name_fromwire.c diff --git a/fuzz/.gitignore b/fuzz/.gitignore index 7d03848c63..fe9ca76c5d 100644 --- a/fuzz/.gitignore +++ b/fuzz/.gitignore @@ -4,6 +4,7 @@ /dns_message_checksig /dns_message_parse /dns_name_fromtext_target +/dns_name_fromwire /dns_rdata_fromtext /dns_rdata_fromwire_text /isc_lex_getmastertoken diff --git a/fuzz/Makefile.am b/fuzz/Makefile.am index eb4a2f4ad6..d265c6c05f 100644 --- a/fuzz/Makefile.am +++ b/fuzz/Makefile.am @@ -26,6 +26,7 @@ check_PROGRAMS = \ dns_message_checksig \ dns_message_parse \ dns_name_fromtext_target \ + dns_name_fromwire \ dns_rdata_fromtext \ dns_rdata_fromwire_text \ isc_lex_getmastertoken \ @@ -36,11 +37,17 @@ EXTRA_DIST = \ dns_message_checksig.in \ dns_message_parse.in \ dns_name_fromtext_target.in \ + dns_name_fromwire.in \ dns_rdata_fromtext.in \ dns_rdata_fromwire_text.in \ isc_lex_getmastertoken.in \ isc_lex_gettoken.in +dns_name_fromwire_SOURCES = \ + dns_name_fromwire.c \ + old.c \ + old.h + TESTS = $(check_PROGRAMS) if HAVE_FUZZ_LOG_COMPILER diff --git a/fuzz/dns_name_fromwire.c b/fuzz/dns_name_fromwire.c new file mode 100644 index 0000000000..ec9e0d2590 --- /dev/null +++ b/fuzz/dns_name_fromwire.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "fuzz.h" +#include "old.h" + +bool debug = false; + +int +LLVMFuzzerInitialize(int *argc __attribute__((unused)), + char ***argv __attribute__((unused))) { + return (0); +} + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + isc_result_t new_result; + isc_result_t old_result; + dns_fixedname_t new_fixed; + dns_fixedname_t old_fixed; + dns_name_t *new_name = dns_fixedname_initname(&new_fixed); + dns_name_t *old_name = dns_fixedname_initname(&old_fixed); + uint8_t *new_offsets; + uint8_t *old_offsets; + dns_decompress_t dctx = DNS_DECOMPRESS_PERMITTED; + isc_buffer_t new_buf; + isc_buffer_t old_buf; + + /* + * Output buffers may be partially used or undersized. + */ + if (size > 0) { + uint8_t add = *data++; + size--; + isc_buffer_add(&new_fixed.buffer, add); + isc_buffer_add(&old_fixed.buffer, add); + } + + /* + * timeout faster if we hit a pointer loop + */ + alarm(1); + + /* + * We shift forward by half the input data to make an area + * that pointers can refer back to. + */ + + isc_buffer_constinit(&new_buf, data, size); + isc_buffer_add(&new_buf, size); + isc_buffer_setactive(&new_buf, size); + isc_buffer_forward(&new_buf, size / 2); + new_result = dns_name_fromwire(new_name, &new_buf, dctx, 0, NULL); + + isc_buffer_constinit(&old_buf, data, size); + isc_buffer_add(&old_buf, size); + isc_buffer_setactive(&old_buf, size); + isc_buffer_forward(&old_buf, size / 2); + old_result = old_name_fromwire(old_name, &old_buf, dctx, 0, NULL); + + REQUIRE(new_result == old_result); + REQUIRE(dns_name_equal(new_name, old_name)); + REQUIRE(new_name->labels == old_name->labels); + + new_offsets = new_name->offsets; + old_offsets = old_name->offsets; + REQUIRE(new_offsets != NULL && old_offsets != NULL); + REQUIRE(memcmp(new_offsets, old_offsets, old_name->labels) == 0); + + REQUIRE(new_fixed.buffer.current == old_fixed.buffer.current); + REQUIRE(new_fixed.buffer.active == old_fixed.buffer.active); + REQUIRE(new_fixed.buffer.used == old_fixed.buffer.used); + REQUIRE(new_fixed.buffer.length == old_fixed.buffer.length); + + REQUIRE(new_buf.base == old_buf.base); + REQUIRE(new_buf.current == old_buf.current); + REQUIRE(new_buf.active == old_buf.active); + REQUIRE(new_buf.used == old_buf.used); + REQUIRE(new_buf.length == old_buf.length); + + return (0); +} diff --git a/fuzz/dns_name_fromwire.in/00b28ff06b788b9b67c6b259800f404f9f3761fd b/fuzz/dns_name_fromwire.in/00b28ff06b788b9b67c6b259800f404f9f3761fd new file mode 100644 index 0000000000000000000000000000000000000000..59f144ee093d5bcf17b6de64d11c2ba0b53eacec GIT binary patch literal 2 Jcmd;L0000M01E&B literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/02440c9e09b9ccb3c36d1ae3b1dc395d39dd2e61 b/fuzz/dns_name_fromwire.in/02440c9e09b9ccb3c36d1ae3b1dc395d39dd2e61 new file mode 100644 index 0000000000000000000000000000000000000000..fd53418a007e8e9576dc589ad13bd4e9a62c7707 GIT binary patch literal 19 acmZoQ(6Z-X;J0FAWMgDx6hFXlfEfTCRRa|O literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/02a01a03cc76d29a0d3f819e9f6e95266f4ee4eb b/fuzz/dns_name_fromwire.in/02a01a03cc76d29a0d3f819e9f6e95266f4ee4eb new file mode 100644 index 0000000000000000000000000000000000000000..93a3f273a5c0197f9a911b96d1271b2800a61cb0 GIT binary patch literal 45 gcmZqWVq|3Cf&fNFE=G1nexLx5KoetN6hFWK05K^7QUCw| literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/03de4865464d9fee5cf614c2e8196cb0246b60f3 b/fuzz/dns_name_fromwire.in/03de4865464d9fee5cf614c2e8196cb0246b60f3 new file mode 100644 index 0000000000000000000000000000000000000000..0231663c06e80cdff273788e4abde19f1ab34653 GIT binary patch literal 9 Qcmd;J{Koi^k^KMz01amXx&QzG literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/0c932286228aafdb4bbcf9d39f4554d814e7c1cb b/fuzz/dns_name_fromwire.in/0c932286228aafdb4bbcf9d39f4554d814e7c1cb new file mode 100644 index 0000000000000000000000000000000000000000..b40dc8de68fd70e426b5c8ba9b3a3770af98afdb GIT binary patch literal 67 xcmexw{r~^}_y7OvA%O#ejA~p^U=N`g85!9b`B@o2^Z`kR7Z8TWH4i~y=h55fQd literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/12248b2bb3063f4f6aca8ec227aa26f24299e53b b/fuzz/dns_name_fromwire.in/12248b2bb3063f4f6aca8ec227aa26f24299e53b new file mode 100644 index 0000000000000000000000000000000000000000..a8fd239600ea14fca9210da41da1b8105b4fac26 GIT binary patch literal 9 Qcmey!!Vt;8$Z&uG01P|=5C8xG literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/2c17e4e73e0ee81d3ed7279f3fbc51d35e8837a1 b/fuzz/dns_name_fromwire.in/2c17e4e73e0ee81d3ed7279f3fbc51d35e8837a1 new file mode 100644 index 0000000000000000000000000000000000000000..7fe02eea833ea488ea5ca00c8f25d5d099645930 GIT binary patch literal 41 qcmeyb`2W8i5O8sE9bji$qUR9>QQ`WMpULXJr7<2P7HpLm0yAQpFFTsGP|KQEATr L6$Yt1U}OvcH8&I? literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/43f90bb183e988637abbcbc50be4a89ebff60d9a b/fuzz/dns_name_fromwire.in/43f90bb183e988637abbcbc50be4a89ebff60d9a new file mode 100644 index 0000000000000000000000000000000000000000..1b7df00da865b7a474c89059f97eb812b3d0e498 GIT binary patch literal 26 gcmZR!c`vHxV0%FA5El^CGaO)y*1L3|PU!#x0BX7jdH?_b literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/448daf1db0846c67ecb7a533361647cb41e5dce2 b/fuzz/dns_name_fromwire.in/448daf1db0846c67ecb7a533361647cb41e5dce2 new file mode 100644 index 0000000000000000000000000000000000000000..b3acc83f72c0c066fec73b01a83bb3f5bd43f168 GIT binary patch literal 6 Ncmd;LU}RKN0{{TK09gP4 literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/477cdb78a23d07e2fc8103c4c79c9a545bcec06b b/fuzz/dns_name_fromwire.in/477cdb78a23d07e2fc8103c4c79c9a545bcec06b new file mode 100644 index 0000000000000000000000000000000000000000..3c48f31c4e0eb3583830e85b9a7a3f24bcc8ec1a GIT binary patch literal 16 UcmdOHFyLZjWCtMzt_DU101vSNga7~l literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/4b345f6450981c1ed938081331eeabd03598cb20 b/fuzz/dns_name_fromwire.in/4b345f6450981c1ed938081331eeabd03598cb20 new file mode 100644 index 0000000000000000000000000000000000000000..863a3768b2560c60e4622310afd4680bbaacf6fe GIT binary patch literal 7 OcmXqDRA)WFzyJUQbO9a! literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/5821576d8de31746675cc0ea7f9e652438070dfe b/fuzz/dns_name_fromwire.in/5821576d8de31746675cc0ea7f9e652438070dfe new file mode 100644 index 0000000000000000000000000000000000000000..6a0ea0dca8323d1b299108dbde0ee3f2b2fe5772 GIT binary patch literal 4 LcmdO7Jiq_|0xJOP literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/608fa82ab128dfb43c9a064bfab677d9b5f3f72b b/fuzz/dns_name_fromwire.in/608fa82ab128dfb43c9a064bfab677d9b5f3f72b new file mode 100644 index 0000000000..f52278dae7 --- /dev/null +++ b/fuzz/dns_name_fromwire.in/608fa82ab128dfb43c9a064bfab677d9b5f3f72b @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/fuzz/dns_name_fromwire.in/61c70af9c1b0588393243703eb472271290f88eb b/fuzz/dns_name_fromwire.in/61c70af9c1b0588393243703eb472271290f88eb new file mode 100644 index 0000000000000000000000000000000000000000..84016db6e7d7647cac6e0615c9ce6af5cbcdf95d GIT binary patch literal 256 zcmexw{T~eUkiY@KbzCrD4`MLhXJBMxWCx0;@-yQCnwsp4{H!2_2o*rBKz&eQ_5%z= e8N{rNFaQe+Xrd-D0L@y67V!fNNFHU7WdHzOfgh0o literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/680357a1752af34a7f882cced6b1e5fd8ecc4bcc b/fuzz/dns_name_fromwire.in/680357a1752af34a7f882cced6b1e5fd8ecc4bcc new file mode 100644 index 0000000000000000000000000000000000000000..c10237751f1dc3b9808dc108373bf3d83b410c83 GIT binary patch literal 4 LcmZ4Y`~U+02Jr$` literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/69abe6811b3437087b53d4c4e9157d4ba7a0890b b/fuzz/dns_name_fromwire.in/69abe6811b3437087b53d4c4e9157d4ba7a0890b new file mode 100644 index 0000000000000000000000000000000000000000..84f94d73450897161cd33c8ea7fead92029608ed GIT binary patch literal 7 OcmZ4Y`~brNMl}EsTLV1+ literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/69d86c5a071a2d76679d54da225e955c35c40db1 b/fuzz/dns_name_fromwire.in/69d86c5a071a2d76679d54da225e955c35c40db1 new file mode 100644 index 0000000000000000000000000000000000000000..f5c9b43921dd599453612a9c676f2060648f0515 GIT binary patch literal 60 scmZqWVq}nGWMBXxMt(*HE*LO`F<>+!e;rT>Bl`gc#`^~t85m<30BzOuV`O9$KfrK+82}z412q5u literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/88188ee25030661f21a4dacca02caeb7eff34235 b/fuzz/dns_name_fromwire.in/88188ee25030661f21a4dacca02caeb7eff34235 new file mode 100644 index 0000000000000000000000000000000000000000..3caa4b67c13be0e0f044641f60e12514fe289917 GIT binary patch literal 7 Ocmb2MJ-~2)kpTb*Ndese literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/93dccf807dc4df3bfc1711ef3ad3bbd65a7d8c67 b/fuzz/dns_name_fromwire.in/93dccf807dc4df3bfc1711ef3ad3bbd65a7d8c67 new file mode 100644 index 0000000000..bf6998315d --- /dev/null +++ b/fuzz/dns_name_fromwire.in/93dccf807dc4df3bfc1711ef3ad3bbd65a7d8c67 @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/fuzz/dns_name_fromwire.in/955cad73af85bdac5a701aea20a086c75585ca74 b/fuzz/dns_name_fromwire.in/955cad73af85bdac5a701aea20a086c75585ca74 new file mode 100644 index 0000000000000000000000000000000000000000..1b30f7262a65dc1c0f3588547a6468d7613fe286 GIT binary patch literal 8 Pcmb1S(9+^kQ&R%~1F8V( literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/9a279b27d765ca2eccc67e1fc8bafb0fa8702208 b/fuzz/dns_name_fromwire.in/9a279b27d765ca2eccc67e1fc8bafb0fa8702208 new file mode 100644 index 0000000000..5f45b63295 --- /dev/null +++ b/fuzz/dns_name_fromwire.in/9a279b27d765ca2eccc67e1fc8bafb0fa8702208 @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/fuzz/dns_name_fromwire.in/9d30975bf630a3203ed71154257ebcfc19d04067 b/fuzz/dns_name_fromwire.in/9d30975bf630a3203ed71154257ebcfc19d04067 new file mode 100644 index 0000000000000000000000000000000000000000..4c93dbac73c93381a67b0a29555ea93c2e3a80c6 GIT binary patch literal 109 zcmexw{T~RJnVFe@7!OcYRlR-b|9{o@_W%FC|F5U12L}hZfWRI^aB(p*GO{!BvvM*p TfLI3@81F+E!s{4x7#TDHQ|=Zn literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/9feb4af1c2bb4444e9417099b21c6b25f7221dee b/fuzz/dns_name_fromwire.in/9feb4af1c2bb4444e9417099b21c6b25f7221dee new file mode 100644 index 0000000000000000000000000000000000000000..dd011fc5161fee470b62d75f3898f6bf95075fba GIT binary patch literal 20 ccmeyy&(FyC{}to?)dv{wGcquIQa``|09SMf1poj5 literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/a3f75fec735667f1c1218f53e073f25586dc0813 b/fuzz/dns_name_fromwire.in/a3f75fec735667f1c1218f53e073f25586dc0813 new file mode 100644 index 0000000000000000000000000000000000000000..620b966a8e5f932fbb83a122652518e454b75c59 GIT binary patch literal 24 ccmeyy&(FyC|NnnR#`~)efG`6i5FB6t0E83?ZvX%Q literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/b473a861dafebb3cd6ca6b92609da9e40eeea68e b/fuzz/dns_name_fromwire.in/b473a861dafebb3cd6ca6b92609da9e40eeea68e new file mode 100644 index 0000000000000000000000000000000000000000..ced1f37950e4bfd187f68b9c12cb54429ebd11f1 GIT binary patch literal 32 fcmZo=WdHV!osof2q@NK;8AE}gnVFdtkj(%9cE|>8 literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/b8811cd9f785b9fb458236875fda90d048383689 b/fuzz/dns_name_fromwire.in/b8811cd9f785b9fb458236875fda90d048383689 new file mode 100644 index 0000000000000000000000000000000000000000..018d57fdf84a0e65d71c876f2b83e43d8218549b GIT binary patch literal 14 VcmZo=VPt3IXFR~@$jESj0RRw*0%iaJ literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/bf546f247bb7a3672a04bb4c9e3b5049f981d435 b/fuzz/dns_name_fromwire.in/bf546f247bb7a3672a04bb4c9e3b5049f981d435 new file mode 100644 index 0000000000000000000000000000000000000000..c2b58982559e56dcdbd1d88cb6fd78c4670c8fa6 GIT binary patch literal 16 YcmZ3g^^WVymjeu}{~2ojb01&;07mi$x&QzG literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/bf7b68e9cc0467f7bbbfe1dc0dfd7c3124b3695c b/fuzz/dns_name_fromwire.in/bf7b68e9cc0467f7bbbfe1dc0dfd7c3124b3695c new file mode 100644 index 0000000000..081d7f19ce --- /dev/null +++ b/fuzz/dns_name_fromwire.in/bf7b68e9cc0467f7bbbfe1dc0dfd7c3124b3695c @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fuzz/dns_name_fromwire.in/bfb829dd666f7fb4c6d9ae55f58503967cf1b53b b/fuzz/dns_name_fromwire.in/bfb829dd666f7fb4c6d9ae55f58503967cf1b53b new file mode 100644 index 0000000000000000000000000000000000000000..c2b8c7fc7d97889e14e0e3f753118fa702f6aa2c GIT binary patch literal 64 zcmexw-TEI0xPZW34+=mWE-pq!Ms`MiRt84K*^KN5{`~`U8P6LY=we`Cj0Oq`GKe2w F002eR55E8a literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/c351ad42ccc7485a0ad6e1ae8cc07d66ee9b045a b/fuzz/dns_name_fromwire.in/c351ad42ccc7485a0ad6e1ae8cc07d66ee9b045a new file mode 100644 index 0000000000000000000000000000000000000000..82628ab41a7ddf9e66bcf92cdff55c1751ef7e93 GIT binary patch literal 6 NcmbOuXM2F50{{pB0zd!& literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/cc6cba815d062e0e250463aad2113fdcafd48571 b/fuzz/dns_name_fromwire.in/cc6cba815d062e0e250463aad2113fdcafd48571 new file mode 100644 index 0000000000000000000000000000000000000000..3fe9467ac28d225b6e998f5382bb0f5043cb999a GIT binary patch literal 9 QcmXre(lTIRWH`V800z?mb^rhX literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/db6cca49e957261d7a262fbf6dafcac01353f1e4 b/fuzz/dns_name_fromwire.in/db6cca49e957261d7a262fbf6dafcac01353f1e4 new file mode 100644 index 0000000000000000000000000000000000000000..ee55ada55d7042039d7f0d898df71defab1dad4c GIT binary patch literal 4 LcmeBTIluq_1GoWC literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/de4e7188e5540bd15eb83ddb5a784c6dc35d8d94 b/fuzz/dns_name_fromwire.in/de4e7188e5540bd15eb83ddb5a784c6dc35d8d94 new file mode 100644 index 0000000000000000000000000000000000000000..8a8fa78f91ee62b992eb69bd501a63979725f7d2 GIT binary patch literal 79 wcmexw{T~eUkiY@KbzCrD4`DDeGO{!Bvoe6_0}PD!Aq?Slsp1D15IPwc0G4+5FqXAHm0RWD;1oQv^ literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/e23b6fdeadc0379b095bc1ffb8a1d2101ebe76b1 b/fuzz/dns_name_fromwire.in/e23b6fdeadc0379b095bc1ffb8a1d2101ebe76b1 new file mode 100644 index 0000000000000000000000000000000000000000..d07176af1812671afcb67ab2b94186eb972c375d GIT binary patch literal 9 Qcmb1RWMDbK$Z&uG00w&jr2qf` literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/e333716351a0ded128bbb67ba46b5017a16ae6bf b/fuzz/dns_name_fromwire.in/e333716351a0ded128bbb67ba46b5017a16ae6bf new file mode 100644 index 0000000000000000000000000000000000000000..fc5aa04774467ac05c68880f3fb7f62f1ba0fa63 GIT binary patch literal 4 LcmdNgU}OLQ0X_g+ literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/e52912805d2e9212d53a46426e160813124be117 b/fuzz/dns_name_fromwire.in/e52912805d2e9212d53a46426e160813124be117 new file mode 100644 index 0000000000000000000000000000000000000000..f4930c533d78843a72ec939fdf7754d3da7d9e07 GIT binary patch literal 15 Wcmey*)h)-s&d7Lxg@J+L00RIYG6O&W literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/f4a59b7257dbd0777e57e6e25689d374f4d771bc b/fuzz/dns_name_fromwire.in/f4a59b7257dbd0777e57e6e25689d374f4d771bc new file mode 100644 index 0000000000000000000000000000000000000000..dff89045002dce45d6de446a59849ee0f625fe41 GIT binary patch literal 83 xcmZQ(T&W2M?2KFxHWL=W3>RZRz`(`Db$}g+7#Zxfz-&f_`;3f?><5Gy830^O2^RnW literal 0 HcmV?d00001 diff --git a/fuzz/dns_name_fromwire.in/fc712b19cbed4a8b4db3226dfbfdf3fb7be7690a b/fuzz/dns_name_fromwire.in/fc712b19cbed4a8b4db3226dfbfdf3fb7be7690a new file mode 100644 index 0000000000000000000000000000000000000000..45701d0ddf24803613da8d3c11832b346cc5bb46 GIT binary patch literal 37 qcmey*&&uG)$jESjf$=^g1Iqy*i; +#include +#include +#include +#include + +#include +#include + +/* + */ + +#include "old.h" + +/* + * code copied from lib/dns/name.c as of commit + * 6967973568fe80b03e1729259f8907ce8792be34 + */ + +typedef enum { fw_start = 0, fw_ordinary, fw_newcurrent } fw_state; + +#define VALID_NAME(n) ISC_MAGIC_VALID(n, DNS_NAME_MAGIC) + +#define INIT_OFFSETS(name, var, default_offsets) \ + if ((name)->offsets != NULL) \ + var = (name)->offsets; \ + else \ + var = (default_offsets); + +#define MAKE_EMPTY(name) \ + do { \ + name->ndata = NULL; \ + name->length = 0; \ + name->labels = 0; \ + name->attributes.absolute = false; \ + } while (0) + +#define BINDABLE(name) (!name->attributes.readonly && !name->attributes.dynamic) + +isc_result_t +old_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, + unsigned int options, isc_buffer_t *target) { + unsigned char *cdata, *ndata; + unsigned int cused; /* Bytes of compressed name data used */ + unsigned int nused, labels, n, nmax; + unsigned int current, new_current, biggest_pointer; + bool done; + fw_state state = fw_start; + unsigned int c; + unsigned char *offsets; + dns_offsets_t odata; + bool downcase; + bool seen_pointer; + + /* + * Copy the possibly-compressed name at source into target, + * decompressing it. Loop prevention is performed by checking + * the new pointer against biggest_pointer. + */ + + REQUIRE(VALID_NAME(name)); + REQUIRE((target != NULL && ISC_BUFFER_VALID(target)) || + (target == NULL && ISC_BUFFER_VALID(name->buffer))); + + downcase = ((options & DNS_NAME_DOWNCASE) != 0); + + if (target == NULL && name->buffer != NULL) { + target = name->buffer; + isc_buffer_clear(target); + } + + REQUIRE(BINDABLE(name)); + + INIT_OFFSETS(name, offsets, odata); + + /* + * Make 'name' empty in case of failure. + */ + MAKE_EMPTY(name); + + /* + * Initialize things to make the compiler happy; they're not required. + */ + n = 0; + new_current = 0; + + /* + * Set up. + */ + labels = 0; + done = false; + + ndata = isc_buffer_used(target); + nused = 0; + seen_pointer = false; + + /* + * Find the maximum number of uncompressed target name + * bytes we are willing to generate. This is the smaller + * of the available target buffer length and the + * maximum legal domain name length (255). + */ + nmax = isc_buffer_availablelength(target); + if (nmax > DNS_NAME_MAXWIRE) { + nmax = DNS_NAME_MAXWIRE; + } + + cdata = isc_buffer_current(source); + cused = 0; + + current = source->current; + biggest_pointer = current; + + /* + * Note: The following code is not optimized for speed, but + * rather for correctness. Speed will be addressed in the future. + */ + + while (current < source->active && !done) { + c = *cdata++; + current++; + if (!seen_pointer) { + cused++; + } + + switch (state) { + case fw_start: + if (c < 64) { + offsets[labels] = nused; + labels++; + if (nused + c + 1 > nmax) { + goto full; + } + nused += c + 1; + *ndata++ = c; + if (c == 0) { + done = true; + } + n = c; + state = fw_ordinary; + } else if (c >= 192) { + /* + * 14-bit compression pointer + */ + if (!dns_decompress_getpermitted(dctx)) { + return (DNS_R_DISALLOWED); + } + new_current = c & 0x3F; + state = fw_newcurrent; + } else { + return (DNS_R_BADLABELTYPE); + } + break; + case fw_ordinary: + if (downcase) { + c = isc_ascii_tolower(c); + } + *ndata++ = c; + n--; + if (n == 0) { + state = fw_start; + } + break; + case fw_newcurrent: + new_current *= 256; + new_current += c; + if (new_current >= biggest_pointer) { + return (DNS_R_BADPOINTER); + } + biggest_pointer = new_current; + current = new_current; + cdata = (unsigned char *)source->base + current; + seen_pointer = true; + state = fw_start; + break; + default: + FATAL_ERROR("Unknown state %d", state); + /* Does not return. */ + } + } + + if (!done) { + return (ISC_R_UNEXPECTEDEND); + } + + name->ndata = (unsigned char *)target->base + target->used; + name->labels = labels; + name->length = nused; + name->attributes.absolute = true; + + isc_buffer_forward(source, cused); + isc_buffer_add(target, name->length); + + return (ISC_R_SUCCESS); + +full: + if (nmax == DNS_NAME_MAXWIRE) { + /* + * The name did not fit even though we had a buffer + * big enough to fit a maximum-length name. + */ + return (DNS_R_NAMETOOLONG); + } else { + /* + * The name might fit if only the caller could give us a + * big enough buffer. + */ + return (ISC_R_NOSPACE); + } +} diff --git a/fuzz/old.h b/fuzz/old.h new file mode 100644 index 0000000000..5c5f82b418 --- /dev/null +++ b/fuzz/old.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*% + * For verifying no functional change in the rewrite of dns_name_fromwire() + */ +isc_result_t +old_name_fromwire(dns_name_t *name, isc_buffer_t *source, dns_decompress_t dctx, + unsigned int options, isc_buffer_t *target); diff --git a/tests/bench/.gitignore b/tests/bench/.gitignore index 494d04e29f..b166d01436 100644 --- a/tests/bench/.gitignore +++ b/tests/bench/.gitignore @@ -1,3 +1,4 @@ /ascii /compress +/dns_name_fromwire /siphash diff --git a/tests/bench/Makefile.am b/tests/bench/Makefile.am index 9483c73236..c5be25c8ec 100644 --- a/tests/bench/Makefile.am +++ b/tests/bench/Makefile.am @@ -2,7 +2,8 @@ include $(top_srcdir)/Makefile.top AM_CPPFLAGS += \ $(LIBISC_CFLAGS) \ - $(LIBDNS_CFLAGS) + $(LIBDNS_CFLAGS) \ + -I$(top_srcdir)/fuzz LDADD += \ $(LIBISC_LIBS) \ @@ -11,4 +12,10 @@ LDADD += \ noinst_PROGRAMS = \ ascii \ compress \ + dns_name_fromwire \ siphash + +dns_name_fromwire_SOURCES = \ + $(top_builddir)/fuzz/old.c \ + $(top_builddir)/fuzz/old.h \ + dns_name_fromwire.c diff --git a/tests/bench/dns_name_fromwire.c b/tests/bench/dns_name_fromwire.c new file mode 100644 index 0000000000..6092ac2556 --- /dev/null +++ b/tests/bench/dns_name_fromwire.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "old.h" + +static uint32_t +old_bench(const uint8_t *data, size_t size) { + isc_result_t result; + dns_fixedname_t fixed; + dns_name_t *name = dns_fixedname_initname(&fixed); + dns_decompress_t dctx = DNS_DECOMPRESS_PERMITTED; + isc_buffer_t buf; + uint32_t count = 0; + + isc_buffer_constinit(&buf, data, size); + isc_buffer_add(&buf, size); + isc_buffer_setactive(&buf, size); + + while (isc_buffer_consumedlength(&buf) < size) { + result = old_name_fromwire(name, &buf, dctx, 0, NULL); + if (result != ISC_R_SUCCESS) { + isc_buffer_forward(&buf, 1); + } + count++; + } + return (count); +} + +static uint32_t +new_bench(const uint8_t *data, size_t size) { + isc_result_t result; + dns_fixedname_t fixed; + dns_name_t *name = dns_fixedname_initname(&fixed); + dns_decompress_t dctx = DNS_DECOMPRESS_PERMITTED; + isc_buffer_t buf; + uint32_t count = 0; + + isc_buffer_constinit(&buf, data, size); + isc_buffer_add(&buf, size); + isc_buffer_setactive(&buf, size); + + while (isc_buffer_consumedlength(&buf) < size) { + result = dns_name_fromwire(name, &buf, dctx, 0, NULL); + if (result != ISC_R_SUCCESS) { + isc_buffer_forward(&buf, 1); + } + count++; + } + return (count); +} + +static void +oldnew_bench(const uint8_t *data, size_t size) { + isc_time_t t0; + isc_time_now_hires(&t0); + uint32_t n1 = old_bench(data, size); + isc_time_t t1; + isc_time_now_hires(&t1); + uint32_t n2 = new_bench(data, size); + isc_time_t t2; + isc_time_now_hires(&t2); + + double t01 = (double)isc_time_microdiff(&t1, &t0); + double t12 = (double)isc_time_microdiff(&t2, &t1); + printf(" old %u / %f ms; %f / us\n", n1, t01 / 1000.0, n1 / t01); + printf(" new %u / %f ms; %f / us\n", n2, t12 / 1000.0, n2 / t12); + printf(" old/new %f or %f\n", t01 / t12, t12 / t01); +} + +#define NAMES 1000 +static uint8_t buf[1024 * NAMES]; + +int +main(void) { + unsigned int p; + + printf("random buffer\n"); + isc_random_buf(buf, sizeof(buf)); + oldnew_bench(buf, sizeof(buf)); + + p = 0; + for (unsigned int name = 0; name < NAMES; name++) { + unsigned int start = p; + unsigned int prev = p; + buf[p++] = 0; + for (unsigned int label = 0; label < 127; label++) { + unsigned int ptr = prev - start; + prev = p; + buf[p++] = 1; + buf[p++] = 'a'; + buf[p++] = 0xC0 | (ptr >> 8); + buf[p++] = 0xFF & ptr; + } + } + printf("127 compression pointers\n"); + oldnew_bench(buf, p); + + p = 0; + for (unsigned int name = 0; name < NAMES; name++) { + for (unsigned int label = 0; label < 127; label++) { + buf[p++] = 1; + buf[p++] = 'a'; + } + buf[p++] = 0; + } + printf("127 sequential labels\n"); + oldnew_bench(buf, p); + + p = 0; + for (unsigned int name = 0; name < NAMES; name++) { + for (unsigned int label = 0; label < 4; label++) { + buf[p++] = 62; + for (unsigned int c = 0; c < 62; c++) { + buf[p++] = 'a'; + } + } + buf[p++] = 0; + } + printf("4 long sequential labels\n"); + oldnew_bench(buf, p); +}