Skip to content

Commit 2b08618

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 <ps@pks.im> Signed-off-by: Karthik Nayak <karthik.188@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 80d74ed commit 2b08618

File tree

6 files changed

+50
-22
lines changed

6 files changed

+50
-22
lines changed

Diff for: refs/reftable-backend.c

+15-5
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];
@@ -1769,7 +1771,9 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
17691771
deletion_ts = creation_ts = reftable_stack_next_update_index(arg->be->stack);
17701772
if (arg->delete_old)
17711773
creation_ts++;
1772-
reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1774+
ret = reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1775+
if (ret < 0)
1776+
goto done;
17731777

17741778
/*
17751779
* Add the new reference. If this is a rename then we also delete the
@@ -2301,7 +2305,9 @@ static int write_reflog_existence_table(struct reftable_writer *writer,
23012305
if (ret <= 0)
23022306
goto done;
23032307

2304-
reftable_writer_set_limits(writer, ts, ts);
2308+
ret = reftable_writer_set_limits(writer, ts, ts);
2309+
if (ret < 0)
2310+
goto done;
23052311

23062312
/*
23072313
* The existence entry has both old and new object ID set to the
@@ -2360,7 +2366,9 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
23602366
uint64_t ts = reftable_stack_next_update_index(arg->stack);
23612367
int ret;
23622368

2363-
reftable_writer_set_limits(writer, ts, ts);
2369+
ret = reftable_writer_set_limits(writer, ts, ts);
2370+
if (ret < 0)
2371+
goto out;
23642372

23652373
ret = reftable_stack_init_log_iterator(arg->stack, &it);
23662374
if (ret < 0)
@@ -2437,7 +2445,9 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
24372445
if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
24382446
live_records++;
24392447

2440-
reftable_writer_set_limits(writer, ts, ts);
2448+
ret = reftable_writer_set_limits(writer, ts, ts);
2449+
if (ret < 0)
2450+
return ret;
24412451

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

Diff for: reftable/reftable-error.h

+1
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

Diff for: reftable/reftable-writer.h

+14-10
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

Diff for: reftable/stack.c

+4-2
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);

Diff for: reftable/writer.c

+11-2
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)

Diff for: t/unit-tests/t-reftable-stack.c

+5-3
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)