From: Mark Andrews Date: Wed, 11 Mar 2026 22:23:00 +0000 (+1100) Subject: Handle NUL in text files better X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4ac15c2c6ece184e4c130c32bfdc4759b7cfcce0;p=thirdparty%2Fbind9.git Handle NUL in text files better NULs in text files indicate corruption. Additionally embedded NULs can cause text files to be interpreted differently to the way they appear to be when read by humans. NUL in text files now returns token type isc_tokentype_unknown. --- diff --git a/fuzz/dns_rdata_fromwire_text.c b/fuzz/dns_rdata_fromwire_text.c index 0e0f2447664..98ff4d46209 100644 --- a/fuzz/dns_rdata_fromwire_text.c +++ b/fuzz/dns_rdata_fromwire_text.c @@ -49,7 +49,6 @@ LLVMFuzzerInitialize(int *argc ISC_ATTR_UNUSED, char ***argv ISC_ATTR_UNUSED) { isc_lex_create(mctx, 64, &lex); memset(specials, 0, sizeof(specials)); - specials[0] = 1; specials['('] = 1; specials[')'] = 1; specials['"'] = 1; diff --git a/lib/dns/master.c b/lib/dns/master.c index b70fb4d99d3..c43a47d04c0 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -534,7 +534,6 @@ loadctx_create(dns_masterformat_t format, isc_mem_t *mctx, unsigned int options, * in lib/dns/tests/dnstest.c. */ memset(specials, 0, sizeof(specials)); - specials[0] = 1; specials['('] = 1; specials[')'] = 1; specials['"'] = 1; diff --git a/lib/isc/include/isc/lex.h b/lib/isc/include/isc/lex.h index a4faf25667a..684d8b416df 100644 --- a/lib/isc/include/isc/lex.h +++ b/lib/isc/include/isc/lex.h @@ -201,7 +201,7 @@ void isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials); /*!< * The characters in 'specials' are returned as tokens. Along with - * whitespace, they delimit strings and numbers. + * whitespace and NUL, they delimit strings and numbers. * * Note: *\li Comment processing takes precedence over special character diff --git a/lib/isc/lex.c b/lib/isc/lex.c index 588ffa6bf1e..db7bc85d3ec 100644 --- a/lib/isc/lex.c +++ b/lib/isc/lex.c @@ -540,6 +540,10 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { lex->last_was_eol = false; no_comments = true; state = lexstate_qstring; + } else if (c == '\0') { + tokenp->type = isc_tokentype_unknown; + tokenp->value.as_char = c; + done = true; } else if (lex->specials[c]) { lex->last_was_eol = false; if ((c == '(' || c == ')') && @@ -607,7 +611,8 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { case lexstate_number: if (c == EOF || !isdigit((unsigned char)c)) { if (c == ' ' || c == '\t' || c == '\r' || - c == '\n' || c == EOF || lex->specials[c]) + c == '\n' || c == '\0' || c == EOF || + lex->specials[c]) { int base; if ((options & ISC_LEXOPT_OCTAL) != 0) { @@ -700,8 +705,8 @@ isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { * as lex->specials[EOF] is not a good idea. */ if (c == '\r' || c == '\n' || c == EOF || - (!escaped && - (c == ' ' || c == '\t' || lex->specials[c]))) + (!escaped && (c == ' ' || c == '\t' || c == '\0' || + lex->specials[c]))) { pushback(source, c); if (source->result != ISC_R_SUCCESS) { diff --git a/tests/dns/skr_test.c b/tests/dns/skr_test.c index 8f29f304f31..54e9b1311d6 100644 --- a/tests/dns/skr_test.c +++ b/tests/dns/skr_test.c @@ -167,7 +167,6 @@ write_record(FILE *fp, dns_rdatatype_t rdtype, const char *rdatastr, /* Create a lexer as one is required by dns_rdata_fromtext(). */ isc_lex_create(isc_g_mctx, 64, &lex); - specials[0] = 1; specials['('] = 1; specials[')'] = 1; specials['"'] = 1; diff --git a/tests/isc/lex_test.c b/tests/isc/lex_test.c index 8db4d609444..764a8102709 100644 --- a/tests/isc/lex_test.c +++ b/tests/isc/lex_test.c @@ -33,6 +33,72 @@ #define AS_STR(x) (x).value.as_textregion.base +/* check handling of 0x00 */ +ISC_RUN_TEST_IMPL(lex_0x00) { + isc_result_t result; + isc_lex_t *lex = NULL; + isc_buffer_t buf; + isc_token_t token; + + unsigned char nul_then_A[] = { '\0', 'A' }; + unsigned char embedded_null[] = { '"', 'a', '\0', 'b', '"' }; + unsigned char escaped_null[] = { 'a', '\\', '\0', 'b' }; + + UNUSED(state); + + isc_lex_create(isc_g_mctx, 1024, &lex); + + isc_buffer_init(&buf, &nul_then_A[0], sizeof(nul_then_A)); + isc_buffer_add(&buf, sizeof(nul_then_A)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_gettoken(lex, 0, &token); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(token.type, isc_tokentype_unknown); + + result = isc_lex_gettoken(lex, 0, &token); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(token.type, isc_tokentype_string); + + isc_lex_close(lex); + + /* + * Check that an embedded NUL is preserved in a quoted string. + */ + isc_buffer_init(&buf, &embedded_null[0], sizeof(embedded_null)); + isc_buffer_add(&buf, sizeof(embedded_null)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_gettoken(lex, ISC_LEXOPT_QSTRING, &token); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(token.type, isc_tokentype_qstring); + assert_int_equal(token.value.as_textregion.length, 3); + assert_memory_equal(token.value.as_textregion.base, "a\0b", 3); + + isc_lex_close(lex); + + /* + * Check that an escaped NUL is preserved. + */ + isc_buffer_init(&buf, &escaped_null[0], sizeof(escaped_null)); + isc_buffer_add(&buf, sizeof(escaped_null)); + + result = isc_lex_openbuffer(lex, &buf); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_lex_gettoken(lex, ISC_LEXOPT_ESCAPE, &token); + assert_int_equal(result, ISC_R_SUCCESS); + assert_int_equal(token.type, isc_tokentype_string); + assert_int_equal(token.value.as_textregion.length, 4); + assert_memory_equal(token.value.as_textregion.base, "a\\\0b", 4); + + isc_lex_destroy(&lex); +} + /* check handling of 0xff */ ISC_RUN_TEST_IMPL(lex_0xff) { isc_result_t result; @@ -72,8 +138,8 @@ ISC_RUN_TEST_IMPL(lex_setline) { isc_lex_create(isc_g_mctx, 1024, &lex); - isc_buffer_init(&buf, &text[0], sizeof(text)); - isc_buffer_add(&buf, sizeof(text)); + isc_buffer_init(&buf, &text[0], sizeof(text) - 1); + isc_buffer_add(&buf, sizeof(text) - 1); result = isc_lex_openbuffer(lex, &buf); assert_int_equal(result, ISC_R_SUCCESS); @@ -340,6 +406,7 @@ ISC_RUN_TEST_IMPL(lex_keypair) { } ISC_TEST_LIST_START +ISC_TEST_ENTRY(lex_0x00) ISC_TEST_ENTRY(lex_0xff) ISC_TEST_ENTRY(lex_keypair) ISC_TEST_ENTRY(lex_setline) diff --git a/tests/libtest/dns.c b/tests/libtest/dns.c index 9b6b8dc655d..e98b0e15b18 100644 --- a/tests/libtest/dns.c +++ b/tests/libtest/dns.c @@ -350,7 +350,6 @@ dns_test_rdatafromstring(dns_rdata_t *rdata, dns_rdataclass_t rdclass, * delimiters while reading the source string. These should match * specials from lib/dns/master.c. */ - specials[0] = 1; specials['('] = 1; specials[')'] = 1; specials['"'] = 1;