From: Junio C Hamano Date: Tue, 30 Jun 2026 19:59:13 +0000 (-0700) Subject: Merge branch 'ps/reftable-hardening' into jch X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=db58c70be09037412b41dd23eb8bede1f10b90be;p=thirdparty%2Fgit.git Merge branch 'ps/reftable-hardening' into jch The reftable code has been hardened against corrupted tables by fixing out-of-bounds writes, out-of-bounds reads, and abort calls during parsing. * ps/reftable-hardening: reftable/table: fix OOB read on truncated table reftable/table: fix NULL pointer access when seeking to bogus offsets reftable/block: fix OOB read with bogus restart offset reftable/block: fix use of uninitialized memory when binsearch fails reftable/block: fix OOB read with bogus restart count reftable/block: fix OOB read with bogus block size reftable/block: fix OOB write with bogus inflated log size t/unit-tests: introduce test helper to write reftable blocks reftable/record: don't abort when decoding invalid ref value type reftable/basics: fix OOB read on binary search of empty range oss-fuzz: add fuzzer for parsing reftables meson: support building fuzzers with libFuzzer --- db58c70be09037412b41dd23eb8bede1f10b90be diff --cc t/unit-tests/u-reftable-table.c index fae478ee04,28b0ef5258..d4251fae6f --- a/t/unit-tests/u-reftable-table.c +++ b/t/unit-tests/u-reftable-table.c @@@ -201,3 -202,91 +204,92 @@@ void test_reftable_table__block_iterato reftable_buf_release(&buf); reftable_free(records); } + + void test_reftable_table__seek_invalid_log_offset(void) + { + struct reftable_ref_record refs[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_log_record logs[] = { + { + .refname = (char *) "refs/heads/main", + .update_index = 1, + .value_type = REFTABLE_LOG_UPDATE, + .value.update = { + .name = (char *) "user", + .email = (char *) "user@example.com", + .message = (char *) "message\n", + }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_log_record log = { 0 }; + struct reftable_iterator it = { 0 }; + struct reftable_table *table; + struct reftable_buf buf = REFTABLE_BUF_INIT; + size_t fsize = footer_size(1); + uint8_t *footer; + + cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), - logs, ARRAY_SIZE(logs), NULL); ++ logs, ARRAY_SIZE(logs), REFTABLE_HASH_SHA1, NULL); + + /* + * Corrupt the log section offset stored in the footer so that it points + * past the end of the table. The footer is checksummed, so we also have + * to recompute and rewrite the CRC. + */ + footer = (uint8_t *) buf.buf + buf.len - fsize; + reftable_put_be64(footer + header_size(1) + 24, UINT64_MAX); + reftable_put_be32(footer + fsize - 4, crc32(0, footer, fsize - 4)); + + block_source_from_buf(&source, &buf); + cl_must_pass(reftable_table_new(&table, &source, "name")); + + /* + * Seeking the log iterator must not crash even though the log section + * offset is bogus. As the offset points past the end of the table we + * know that the table is corrupt, so the seek must report a format + * error instead of pretending that the section is empty. + */ + reftable_table_init_log_iterator(table, &it); + cl_assert_equal_i(reftable_iterator_seek_log(&it, ""), + REFTABLE_FORMAT_ERROR); + + reftable_log_record_release(&log); + reftable_iterator_destroy(&it); + reftable_table_decref(table); + reftable_buf_release(&buf); + } + + void test_reftable_table__new_with_truncated_table(void) + { + struct reftable_ref_record refs[] = { + { + .refname = (char *) "refs/heads/main", + .value_type = REFTABLE_REF_VAL1, + .value.val1 = { 42 }, + }, + }; + struct reftable_block_source source = { 0 }; + struct reftable_table *table; + struct reftable_buf buf = REFTABLE_BUF_INIT; + - cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), NULL, 0, NULL); ++ cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), NULL, 0, ++ REFTABLE_HASH_SHA1, NULL); + + /* + * Truncate the table so that it is large enough to read the header, but + * too small to also contain the footer. + */ + buf.len = footer_size(1) - 1; + block_source_from_buf(&source, &buf); + + cl_assert_equal_i(reftable_table_new(&table, &source, "name"), + REFTABLE_FORMAT_ERROR); + + reftable_buf_release(&buf); + }