Skip to content

Commit 7ed6238

Browse files
KarthikNayakdscho
authored andcommitted
reftable: prevent 'update_index' changes after adding records
The function `reftable_writer_set_limits()` allows updating the 'min_update_index' and 'max_update_index' of a reftable writer. These values are written to both the writer's header and footer. Since the header is written during the first block write, any subsequent changes to the update index would create a mismatch between the header and footer values. The footer would contain the newer values while the header retained the original ones. To fix this bug, prevent callers from updating these values after any record is written. To do this, modify the function to return an error whenever the limits are modified after any record adds. Check for record adds within `reftable_writer_set_limits()` by checking the `last_key` variable, which is set whenever a new record is added. Modify all callers of the function to anticipate a return type and handle it accordingly. Helped-by: Patrick Steinhardt <[email protected]> Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 17681ec commit 7ed6238

File tree

6 files changed

+50
-22
lines changed

6 files changed

+50
-22
lines changed

refs/reftable-backend.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
14441444
* multiple entries. Each entry will contain a different update_index,
14451445
* so set the limits accordingly.
14461446
*/
1447-
reftable_writer_set_limits(writer, ts, ts + arg->max_index);
1447+
ret = reftable_writer_set_limits(writer, ts, ts + arg->max_index);
1448+
if (ret < 0)
1449+
goto done;
14481450

14491451
for (i = 0; i < arg->updates_nr; i++) {
14501452
struct reftable_transaction_update *tx_update = &arg->updates[i];
@@ -1766,7 +1768,9 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
17661768
deletion_ts = creation_ts = reftable_stack_next_update_index(arg->be->stack);
17671769
if (arg->delete_old)
17681770
creation_ts++;
1769-
reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1771+
ret = reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1772+
if (ret < 0)
1773+
goto done;
17701774

17711775
/*
17721776
* Add the new reference. If this is a rename then we also delete the
@@ -2298,7 +2302,9 @@ static int write_reflog_existence_table(struct reftable_writer *writer,
22982302
if (ret <= 0)
22992303
goto done;
23002304

2301-
reftable_writer_set_limits(writer, ts, ts);
2305+
ret = reftable_writer_set_limits(writer, ts, ts);
2306+
if (ret < 0)
2307+
goto done;
23022308

23032309
/*
23042310
* The existence entry has both old and new object ID set to the
@@ -2357,7 +2363,9 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
23572363
uint64_t ts = reftable_stack_next_update_index(arg->stack);
23582364
int ret;
23592365

2360-
reftable_writer_set_limits(writer, ts, ts);
2366+
ret = reftable_writer_set_limits(writer, ts, ts);
2367+
if (ret < 0)
2368+
goto out;
23612369

23622370
ret = reftable_stack_init_log_iterator(arg->stack, &it);
23632371
if (ret < 0)
@@ -2434,7 +2442,9 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
24342442
if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
24352443
live_records++;
24362444

2437-
reftable_writer_set_limits(writer, ts, ts);
2445+
ret = reftable_writer_set_limits(writer, ts, ts);
2446+
if (ret < 0)
2447+
return ret;
24382448

24392449
if (!is_null_oid(&arg->update_oid)) {
24402450
struct reftable_ref_record ref = {0};

reftable/reftable-error.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum reftable_error {
3030

3131
/* Misuse of the API:
3232
* - on writing a record with NULL refname.
33+
* - on writing a record before setting the writer limits.
3334
* - on writing a reftable_ref_record outside the table limits
3435
* - on writing a ref or log record before the stack's
3536
* next_update_inde*x

reftable/reftable-writer.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,17 +124,21 @@ int reftable_writer_new(struct reftable_writer **out,
124124
int (*flush_func)(void *),
125125
void *writer_arg, const struct reftable_write_options *opts);
126126

127-
/* Set the range of update indices for the records we will add. When writing a
128-
table into a stack, the min should be at least
129-
reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
130-
131-
For transactional updates to a stack, typically min==max, and the
132-
update_index can be obtained by inspeciting the stack. When converting an
133-
existing ref database into a single reftable, this would be a range of
134-
update-index timestamps.
127+
/*
128+
* Set the range of update indices for the records we will add. When writing a
129+
* table into a stack, the min should be at least
130+
* reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
131+
*
132+
* For transactional updates to a stack, typically min==max, and the
133+
* update_index can be obtained by inspeciting the stack. When converting an
134+
* existing ref database into a single reftable, this would be a range of
135+
* update-index timestamps.
136+
*
137+
* The function should be called before adding any records to the writer. If not
138+
* it will fail with REFTABLE_API_ERROR.
135139
*/
136-
void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
137-
uint64_t max);
140+
int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
141+
uint64_t max);
138142

139143
/*
140144
Add a reftable_ref_record. The record should have names that come after

reftable/stack.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,8 +1058,10 @@ static int stack_write_compact(struct reftable_stack *st,
10581058

10591059
for (size_t i = first; i <= last; i++)
10601060
st->stats.bytes += st->readers[i]->size;
1061-
reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
1062-
st->readers[last]->max_update_index);
1061+
err = reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
1062+
st->readers[last]->max_update_index);
1063+
if (err < 0)
1064+
goto done;
10631065

10641066
err = reftable_merged_table_new(&mt, st->readers + first, subtabs_len,
10651067
st->opts.hash_id);

reftable/writer.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,20 @@ int reftable_writer_new(struct reftable_writer **out,
179179
return 0;
180180
}
181181

182-
void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
183-
uint64_t max)
182+
int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
183+
uint64_t max)
184184
{
185+
/*
186+
* The limits should be set before any records are added to the writer.
187+
* Check if any records were added by checking if `last_key` was set.
188+
*/
189+
if (w->last_key.len)
190+
return REFTABLE_API_ERROR;
191+
185192
w->min_update_index = min;
186193
w->max_update_index = max;
194+
195+
return 0;
187196
}
188197

189198
static void writer_release(struct reftable_writer *w)

t/unit-tests/t-reftable-stack.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ static void t_read_file(void)
103103
static int write_test_ref(struct reftable_writer *wr, void *arg)
104104
{
105105
struct reftable_ref_record *ref = arg;
106-
reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
106+
check(!reftable_writer_set_limits(wr, ref->update_index,
107+
ref->update_index));
107108
return reftable_writer_add_ref(wr, ref);
108109
}
109110

@@ -143,7 +144,8 @@ static int write_test_log(struct reftable_writer *wr, void *arg)
143144
{
144145
struct write_log_arg *wla = arg;
145146

146-
reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
147+
check(!reftable_writer_set_limits(wr, wla->update_index,
148+
wla->update_index));
147149
return reftable_writer_add_log(wr, wla->log);
148150
}
149151

@@ -961,7 +963,7 @@ static void t_reflog_expire(void)
961963

962964
static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
963965
{
964-
reftable_writer_set_limits(wr, 1, 1);
966+
check(!reftable_writer_set_limits(wr, 1, 1));
965967
return 0;
966968
}
967969

0 commit comments

Comments
 (0)