Skip to content

Commit 148560f

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 a89e12d commit 148560f

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
@@ -1317,7 +1317,9 @@ static int write_transaction_table(struct reftable_writer *writer, void *cb_data
13171317
* multiple entries. Each entry will contain a different update_index,
13181318
* so set the limits accordingly.
13191319
*/
1320-
reftable_writer_set_limits(writer, ts, ts + arg->max_index);
1320+
ret = reftable_writer_set_limits(writer, ts, ts + arg->max_index);
1321+
if (ret < 0)
1322+
goto done;
13211323

13221324
for (i = 0; i < arg->updates_nr; i++) {
13231325
struct reftable_transaction_update *tx_update = &arg->updates[i];
@@ -1640,7 +1642,9 @@ static int write_copy_table(struct reftable_writer *writer, void *cb_data)
16401642
deletion_ts = creation_ts = reftable_stack_next_update_index(arg->stack);
16411643
if (arg->delete_old)
16421644
creation_ts++;
1643-
reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1645+
ret = reftable_writer_set_limits(writer, deletion_ts, creation_ts);
1646+
if (ret < 0)
1647+
goto done;
16441648

16451649
/*
16461650
* Add the new reference. If this is a rename then we also delete the
@@ -2160,7 +2164,9 @@ static int write_reflog_existence_table(struct reftable_writer *writer,
21602164
if (ret <= 0)
21612165
goto done;
21622166

2163-
reftable_writer_set_limits(writer, ts, ts);
2167+
ret = reftable_writer_set_limits(writer, ts, ts);
2168+
if (ret < 0)
2169+
goto done;
21642170

21652171
/*
21662172
* The existence entry has both old and new object ID set to the
@@ -2219,7 +2225,9 @@ static int write_reflog_delete_table(struct reftable_writer *writer, void *cb_da
22192225
uint64_t ts = reftable_stack_next_update_index(arg->stack);
22202226
int ret;
22212227

2222-
reftable_writer_set_limits(writer, ts, ts);
2228+
ret = reftable_writer_set_limits(writer, ts, ts);
2229+
if (ret < 0)
2230+
goto out;
22232231

22242232
ret = reftable_stack_init_log_iterator(arg->stack, &it);
22252233
if (ret < 0)
@@ -2295,7 +2303,9 @@ static int write_reflog_expiry_table(struct reftable_writer *writer, void *cb_da
22952303
if (arg->records[i].value_type == REFTABLE_LOG_UPDATE)
22962304
live_records++;
22972305

2298-
reftable_writer_set_limits(writer, ts, ts);
2306+
ret = reftable_writer_set_limits(writer, ts, ts);
2307+
if (ret < 0)
2308+
return ret;
22992309

23002310
if (!is_null_oid(&arg->update_oid)) {
23012311
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
@@ -109,17 +109,21 @@ int reftable_writer_new(struct reftable_writer **out,
109109
int (*flush_func)(void *),
110110
void *writer_arg, const struct reftable_write_options *opts);
111111

112-
/* Set the range of update indices for the records we will add. When writing a
113-
table into a stack, the min should be at least
114-
reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
115-
116-
For transactional updates to a stack, typically min==max, and the
117-
update_index can be obtained by inspeciting the stack. When converting an
118-
existing ref database into a single reftable, this would be a range of
119-
update-index timestamps.
112+
/*
113+
* Set the range of update indices for the records we will add. When writing a
114+
* table into a stack, the min should be at least
115+
* reftable_stack_next_update_index(), or REFTABLE_API_ERROR is returned.
116+
*
117+
* For transactional updates to a stack, typically min==max, and the
118+
* update_index can be obtained by inspeciting the stack. When converting an
119+
* existing ref database into a single reftable, this would be a range of
120+
* update-index timestamps.
121+
*
122+
* The function should be called before adding any records to the writer. If not
123+
* it will fail with REFTABLE_API_ERROR.
120124
*/
121-
void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
122-
uint64_t max);
125+
int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
126+
uint64_t max);
123127

124128
/*
125129
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
@@ -1045,8 +1045,10 @@ static int stack_write_compact(struct reftable_stack *st,
10451045

10461046
for (size_t i = first; i <= last; i++)
10471047
st->stats.bytes += st->readers[i]->size;
1048-
reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
1049-
st->readers[last]->max_update_index);
1048+
err = reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
1049+
st->readers[last]->max_update_index);
1050+
if (err < 0)
1051+
goto done;
10501052

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

reftable/writer.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,20 @@ int reftable_writer_new(struct reftable_writer **out,
165165
return 0;
166166
}
167167

168-
void reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
169-
uint64_t max)
168+
int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
169+
uint64_t max)
170170
{
171+
/*
172+
* The limits should be set before any records are added to the writer.
173+
* Check if any records were added by checking if `last_key` was set.
174+
*/
175+
if (w->last_key.len)
176+
return REFTABLE_API_ERROR;
177+
171178
w->min_update_index = min;
172179
w->max_update_index = max;
180+
181+
return 0;
173182
}
174183

175184
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
@@ -98,7 +98,8 @@ static void t_read_file(void)
9898
static int write_test_ref(struct reftable_writer *wr, void *arg)
9999
{
100100
struct reftable_ref_record *ref = arg;
101-
reftable_writer_set_limits(wr, ref->update_index, ref->update_index);
101+
check(!reftable_writer_set_limits(wr, ref->update_index,
102+
ref->update_index));
102103
return reftable_writer_add_ref(wr, ref);
103104
}
104105

@@ -138,7 +139,8 @@ static int write_test_log(struct reftable_writer *wr, void *arg)
138139
{
139140
struct write_log_arg *wla = arg;
140141

141-
reftable_writer_set_limits(wr, wla->update_index, wla->update_index);
142+
check(!reftable_writer_set_limits(wr, wla->update_index,
143+
wla->update_index));
142144
return reftable_writer_add_log(wr, wla->log);
143145
}
144146

@@ -956,7 +958,7 @@ static void t_reflog_expire(void)
956958

957959
static int write_nothing(struct reftable_writer *wr, void *arg UNUSED)
958960
{
959-
reftable_writer_set_limits(wr, 1, 1);
961+
check(!reftable_writer_set_limits(wr, 1, 1));
960962
return 0;
961963
}
962964

0 commit comments

Comments
 (0)