reftable: signal overflow

reflog entries have unbounded size. In theory, each log ('g') block in reftable can have an arbitrary size, so the format allows for arbitrarily sized reflog messages. However, in the implementation, we are not scaling the log blocks up with the message, and writing a large message fails. This triggers a failure for reftable in t7006-pager.sh. Until this is fixed more structurally, report an error from within the reftable library for easier debugging. Signed-off-by: Han-Wen Nienhuys <hanwen@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Han-Wen Nienhuys committed Dec 23, 2021 at 19:29 UTC 0dd44584abf3fee0ba19b5edf856be2a79974228
4 files changed +44
reftable/error.c
+2
@@ -32,6 +32,8 @@ const char *reftable_error_str(int err)
32 return "wrote empty table";
33 case REFTABLE_REFNAME_ERROR:
34 return "invalid refname";
35 + case REFTABLE_ENTRY_TOO_BIG_ERROR:
36 + return "entry too large";
37 case -1:
38 return "general error";
39 default:
reftable/readwrite_test.c
+35
@@ -155,6 +155,40 @@ static void test_log_buffer_size(void)
155 strbuf_release(&buf);
156 }
157
158 +static void test_log_overflow(void)
159 +{
160 + struct strbuf buf = STRBUF_INIT;
161 + char msg[256] = { 0 };
162 + struct reftable_write_options opts = {
163 + .block_size = ARRAY_SIZE(msg),
164 + };
165 + int err;
166 + struct reftable_log_record
167 + log = { .refname = "refs/heads/master",
168 + .update_index = 0xa,
169 + .value_type = REFTABLE_LOG_UPDATE,
170 + .value = { .update = {
171 + .name = "Han-Wen Nienhuys",
172 + .email = "hanwen@google.com",
173 + .tz_offset = 100,
174 + .time = 0x5e430672,
175 + .message = msg,
176 + } } };
177 + struct reftable_writer *w =
178 + reftable_new_writer(&strbuf_add_void, &buf, &opts);
179 +
180 + uint8_t hash1[GIT_SHA1_RAWSZ] = {1}, hash2[GIT_SHA1_RAWSZ] = { 2 };
181 +
182 + memset(msg, 'x', sizeof(msg) - 1);
183 + log.value.update.old_hash = hash1;
184 + log.value.update.new_hash = hash2;
185 + reftable_writer_set_limits(w, update_index, update_index);
186 + err = reftable_writer_add_log(w, &log);
187 + EXPECT(err == REFTABLE_ENTRY_TOO_BIG_ERROR);
188 + reftable_writer_free(w);
189 + strbuf_release(&buf);
190 +}
191 +
192 static void test_log_write_read(void)
193 {
194 int N = 2;
@@ -648,5 +682,6 @@ int readwrite_test_main(int argc, const char *argv[])
682 RUN_TEST(test_table_refs_for_no_index);
683 RUN_TEST(test_table_refs_for_obj_index);
684 RUN_TEST(test_write_empty_table);
685 + RUN_TEST(test_log_overflow);
686 return 0;
687 }
reftable/reftable-error.h
+4
@@ -53,6 +53,10 @@ enum reftable_error {
53
54 /* Invalid ref name. */
55 REFTABLE_REFNAME_ERROR = -10,
56 +
57 + /* Entry does not fit. This can happen when writing outsize reflog
58 + messages. */
59 + REFTABLE_ENTRY_TOO_BIG_ERROR = -11,
60 };
61
62 /* convert the numeric error code to a string. The string should not be
reftable/writer.c
+3
@@ -239,6 +239,9 @@ static int writer_add_record(struct reftable_writer *w,
239 writer_reinit_block_writer(w, reftable_record_type(rec));
240 err = block_writer_add(w->block_writer, rec);
241 if (err < 0) {
242 + /* we are writing into memory, so an error can only mean it
243 + * doesn't fit. */
244 + err = REFTABLE_ENTRY_TOO_BIG_ERROR;
245 goto done;
246 }
247