diff --git a/reftable/block.c b/reftable/block.c index 4d6b11c2e7..4d285aefd7 100644 --- a/reftable/block.c +++ b/reftable/block.c @@ -351,6 +351,10 @@ int reftable_block_init(struct reftable_block *block, restart_count = reftable_get_be16(block->block_data.data + block_size - 2); restart_off = block_size - 2 - 3 * restart_count; + if (restart_off < header_size + 4 || restart_off > block_size - 2) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } block->block_type = block_type; block->hash_size = hash_size; diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c index 6995eb8bf1..d77048cdd0 100644 --- a/t/unit-tests/u-reftable-block.c +++ b/t/unit-tests/u-reftable-block.c @@ -535,3 +535,36 @@ void test_reftable_block__corrupt_block_size(void) reftable_block_release(&block); reftable_buf_release(&data); } + +void test_reftable_block__corrupt_restart_count(void) +{ + struct reftable_block_source source = { 0 }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_REF, + .u.ref = { + .value_type = REFTABLE_REF_VAL1, + .refname = (char *) "refs/heads/main", + }, + }; + struct reftable_block block = { 0 }; + struct reftable_buf data = REFTABLE_BUF_INIT; + int block_size; + + block_size = cl_reftable_write_block(&data, REFTABLE_BLOCK_TYPE_REF, &rec, 1); + + /* + * Corrupt the restart count to claim a bogus number of restart points. + * Note that this would only cause us to perform an out-of-bounds + * access when seeking into the block, but we want to refuse such a + * block outright. + */ + reftable_put_be16((uint8_t *) data.buf + block_size - 2, 0xffff); + + block_source_from_buf(&source, &data); + cl_assert_equal_i(reftable_block_init(&block, &source, 0, 0, data.len, + REFTABLE_HASH_SIZE_SHA1, REFTABLE_BLOCK_TYPE_REF), + REFTABLE_FORMAT_ERROR); + + reftable_block_release(&block); + reftable_buf_release(&data); +}