diff mbox series

[v3] refs: fix uninitialized memory access of `max_index`

Message ID 20250124140203.886324-1-karthik.188@gmail.com (mailing list archive)
State Accepted
Commit f11f0a5a2db955f68776ea95aec42df7fcb8ce1b
Headers show
Series [v3] refs: fix uninitialized memory access of `max_index` | expand

Commit Message

Karthik Nayak Jan. 24, 2025, 2:02 p.m. UTC
When migrating reflogs between reference backends, maintaining the
original order of the reflog entries is crucial. To achieve this, an
`index` field is stored within the `ref_update` struct.

In the reftable backend, before writing any references, the writer must
be configured with the minimum and maximum update index values. The
`max_update_index` is derived from the maximum `ref_update.index` value
in a transaction . The commit bc67b4ab5f (reftable: write correct
max_update_index to header, 2025-01-15) addressed this by propagating the
`max_update_index` value from the transaction to
`write_transaction_table_arg` and, ultimately, to
`reftable_writer_set_limits()`, which sets the min and max index for the
reftable writer.

However, that commit introduced an issue:

  - In `reftable_transaction_data`, which contains an array of
  `write_transaction_table_arg`, only the first element was assigned the
  `max_index` value.

As a result, any elements beyond the first in the array contained
uninitialized `max_index`. The writer contains multiple elements of
`write_transaction_table_arg` to correspond to different worktrees being
written. This uninitialized value was later used to set the
`max_update_index` for the writer, potentially causing overflow or
undefined behavior.

Fix this by:

  - Initializing the `max_index` field to 0.
  - Moving the assignment of `max_index` in
  `reftable_be_transaction_finish()` inside the loop, ensuring all
  elements of the array are correctly initialized.

Initializing `max_index` to `0` is not strictly necessary, as all
elements of `write_transaction_table_arg.max_index` are now assigned
correctly. However, this initialization is added for consistency and to
safeguard against potential future changes that might inadvertently
introduce uninitialized memory access.

Reported-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
Hello,

As suggested, I've redone my patch to make this a relative patch on top of
'kn/reflog-migration-fix'. 

This is based on top of maint with 'kn/reflog-migration-fix' merged in.

Thanks
---
 refs/reftable-backend.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Comments

Patrick Steinhardt Jan. 24, 2025, 2:46 p.m. UTC | #1
On Fri, Jan 24, 2025 at 03:02:03PM +0100, Karthik Nayak wrote:
> When migrating reflogs between reference backends, maintaining the
> original order of the reflog entries is crucial. To achieve this, an
> `index` field is stored within the `ref_update` struct.
> 
> In the reftable backend, before writing any references, the writer must
> be configured with the minimum and maximum update index values. The
> `max_update_index` is derived from the maximum `ref_update.index` value
> in a transaction . The commit bc67b4ab5f (reftable: write correct
> max_update_index to header, 2025-01-15) addressed this by propagating the
> `max_update_index` value from the transaction to
> `write_transaction_table_arg` and, ultimately, to
> `reftable_writer_set_limits()`, which sets the min and max index for the
> reftable writer.
> 
> However, that commit introduced an issue:
> 
>   - In `reftable_transaction_data`, which contains an array of
>   `write_transaction_table_arg`, only the first element was assigned the
>   `max_index` value.
> 
> As a result, any elements beyond the first in the array contained
> uninitialized `max_index`. The writer contains multiple elements of
> `write_transaction_table_arg` to correspond to different worktrees being
> written. This uninitialized value was later used to set the
> `max_update_index` for the writer, potentially causing overflow or
> undefined behavior.

It reads a bit funny as a bulleted list with a single item, only. A
suggestion for the above:

    However, we only set the update index for the first
    `write_transaction_table_arg`, even though there can be multiple
    such arguments. This is the case when we write to multiple stacks in
    a single transaction, e.g. when updating references in two different
    worktrees at once. And, if so, we wouldn't have initialized the
    update index for any but the first such argument. This uninitialized
    value was later used to set the `max_update_index` for the writer,
    potentially causing undefined behaviour.

Other than that this is nicely described, and the fix looks reasonable,
too.

Patrick
Karthik Nayak Jan. 24, 2025, 3:48 p.m. UTC | #2
Patrick Steinhardt <ps@pks.im> writes:

> On Fri, Jan 24, 2025 at 03:02:03PM +0100, Karthik Nayak wrote:
>> When migrating reflogs between reference backends, maintaining the
>> original order of the reflog entries is crucial. To achieve this, an
>> `index` field is stored within the `ref_update` struct.
>>
>> In the reftable backend, before writing any references, the writer must
>> be configured with the minimum and maximum update index values. The
>> `max_update_index` is derived from the maximum `ref_update.index` value
>> in a transaction . The commit bc67b4ab5f (reftable: write correct
>> max_update_index to header, 2025-01-15) addressed this by propagating the
>> `max_update_index` value from the transaction to
>> `write_transaction_table_arg` and, ultimately, to
>> `reftable_writer_set_limits()`, which sets the min and max index for the
>> reftable writer.
>>
>> However, that commit introduced an issue:
>>
>>   - In `reftable_transaction_data`, which contains an array of
>>   `write_transaction_table_arg`, only the first element was assigned the
>>   `max_index` value.
>>
>> As a result, any elements beyond the first in the array contained
>> uninitialized `max_index`. The writer contains multiple elements of
>> `write_transaction_table_arg` to correspond to different worktrees being
>> written. This uninitialized value was later used to set the
>> `max_update_index` for the writer, potentially causing overflow or
>> undefined behavior.
>
> It reads a bit funny as a bulleted list with a single item, only. A
> suggestion for the above:
>
>     However, we only set the update index for the first
>     `write_transaction_table_arg`, even though there can be multiple
>     such arguments. This is the case when we write to multiple stacks in
>     a single transaction, e.g. when updating references in two different
>     worktrees at once. And, if so, we wouldn't have initialized the
>     update index for any but the first such argument. This uninitialized
>     value was later used to set the `max_update_index` for the writer,
>     potentially causing undefined behaviour.
>
> Other than that this is nicely described, and the fix looks reasonable,
> too.
>
> Patrick

Thanks Patrick for taking the time, this seems much better. Let me add
this in for the next version.
Patrick Steinhardt Jan. 27, 2025, 10:20 a.m. UTC | #3
On Fri, Jan 24, 2025 at 07:48:43AM -0800, Karthik Nayak wrote:
> Thanks Patrick for taking the time, this seems much better. Let me add
> this in for the next version.

I've sent a v4 of this patch, but forgot to set the In-reply-to header.
The patch can be found at [1].

Patrick

[1]: <b7e3dd3cc870024f0e80dad26c5a7a96483c6cf4.1737970803.git.ps@pks.im>
Junio C Hamano Jan. 27, 2025, 4:24 p.m. UTC | #4
Patrick Steinhardt <ps@pks.im> writes:

> On Fri, Jan 24, 2025 at 07:48:43AM -0800, Karthik Nayak wrote:
>> Thanks Patrick for taking the time, this seems much better. Let me add
>> this in for the next version.
>
> I've sent a v4 of this patch, but forgot to set the In-reply-to header.
> The patch can be found at [1].
>
> Patrick
>
> [1]: <b7e3dd3cc870024f0e80dad26c5a7a96483c6cf4.1737970803.git.ps@pks.im>

Ah, how very considerate of you.  List-archive spelunkers have
bidirectional links to see (1) from an older discussion, how the
issues were resolved, and (2) from the final attempt, where the
issues came from.

Thanks.
diff mbox series

Patch

diff --git a/refs/reftable-backend.c b/refs/reftable-backend.c
index 289496058e..d39a14c5a4 100644
--- a/refs/reftable-backend.c
+++ b/refs/reftable-backend.c
@@ -1020,6 +1020,7 @@  static int prepare_transaction_update(struct write_transaction_table_arg **out,
 		arg->updates_nr = 0;
 		arg->updates_alloc = 0;
 		arg->updates_expected = 0;
+		arg->max_index = 0;
 	}
 
 	arg->updates_expected++;
@@ -1628,10 +1629,9 @@  static int reftable_be_transaction_finish(struct ref_store *ref_store UNUSED,
 	struct reftable_transaction_data *tx_data = transaction->backend_data;
 	int ret = 0;
 
-	if (tx_data->args)
-		tx_data->args->max_index = transaction->max_index;
-
 	for (size_t i = 0; i < tx_data->args_nr; i++) {
+		tx_data->args[i].max_index = transaction->max_index;
+
 		ret = reftable_addition_add(tx_data->args[i].addition,
 					    write_transaction_table, &tx_data->args[i]);
 		if (ret < 0)