diff --git a/reftable/block.c b/reftable/block.c index 920b3f4486..b86cb9ec5a 100644 --- a/reftable/block.c +++ b/reftable/block.c @@ -260,6 +260,15 @@ int reftable_block_init(struct reftable_block *block, goto done; } + /* + * Verify that the block size covers at least the table header, block + * header and the 2 byte restart counter. + */ + if (block_size < header_size + 4 + 2) { + err = REFTABLE_FORMAT_ERROR; + goto done; + } + if (block_type == REFTABLE_BLOCK_TYPE_LOG) { uint32_t block_header_skip = 4 + header_size; uLong dst_len = block_size - block_header_skip; diff --git a/t/unit-tests/u-reftable-block.c b/t/unit-tests/u-reftable-block.c index f4bded7d26..40274af5c0 100644 --- a/t/unit-tests/u-reftable-block.c +++ b/t/unit-tests/u-reftable-block.c @@ -456,3 +456,47 @@ void test_reftable_block__iterator(void) block_writer_release(&writer); reftable_buf_release(&data); } + +void test_reftable_block__corrupt_log_block_size(void) +{ + struct reftable_block_source source = { 0 }; + struct block_writer writer = { + .last_key = REFTABLE_BUF_INIT, + }; + struct reftable_record rec = { + .type = REFTABLE_BLOCK_TYPE_LOG, + .u.log = { + .refname = (char *) "refs/heads/main", + .update_index = 1, + .value_type = REFTABLE_LOG_UPDATE, + }, + }; + struct reftable_block block = { 0 }; + struct reftable_buf data; + + data.len = 1024; + REFTABLE_CALLOC_ARRAY(data.buf, data.len); + cl_assert(data.buf != NULL); + + cl_must_pass(block_writer_init(&writer, REFTABLE_BLOCK_TYPE_LOG, + (uint8_t *) data.buf, data.len, + 0, hash_size(REFTABLE_HASH_SHA1))); + cl_must_pass(block_writer_add(&writer, &rec)); + cl_assert(block_writer_finish(&writer) > 0); + + /* + * Log blocks store their inflated size as a big-endian 24-bit integer + * right after the one-byte block type. Rewrite it to claim a size that + * is smaller than the block header. + */ + reftable_put_be24((uint8_t *) data.buf + 1, 1); + + 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_LOG), + REFTABLE_FORMAT_ERROR); + + reftable_block_release(&block); + block_writer_release(&writer); + reftable_buf_release(&data); +}