{"thread":{"id":"62860","subject":"[PATCH v4] refs/reftable: fix uninitialized memory access of `max_index`","startedAt":"2025-01-27T09:44:18Z","lastAt":"2025-01-27T16:22:58Z","messageCount":3,"participants":["Patrick Steinhardt","Junio C Hamano"],"isPatch":true,"patchVersion":4,"patchTotal":null},"messages":[{"id":"511232","messageId":"b7e3dd3cc870024f0e80dad26c5a7a96483c6cf4.1737970803.git.ps@pks.im","threadId":"62860","inReplyTo":null,"subject":"[PATCH v4] refs/reftable: fix uninitialized memory access of `max_index`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-27T09:44:08Z","receivedAt":"2025-01-27T09:44:18Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"From: Karthik Nayak <karthik.188@gmail.com>\n\nWhen migrating reflogs between reference backends, maintaining the\noriginal order of the reflog entries is crucial. To achieve this, an\n`index` field is stored within the `ref_update` struct that encodes the\nrelative order of reflog entries. This field is used by the reftable\nbackend as update index for the respective reflog entries to maintain\nthat ordering.\n\nThese update indices must be respected when writing table headers, which\nencode the minimum and maximum update index of contained records in the\nheader and footer. This logic was added in commit bc67b4ab5f (reftable:\nwrite correct max_update_index to header, 2025-01-15), which started to\nuse `reftable_writer_set_limits()` to propagate the mininum and maximum\nupdate index of all records contained in a ref transaction.\n\nHowever, we only set the maximum update index for the first transaction\nargument, even though there can be multiple such arguments. This is the\ncase when we write to multiple stacks in a single transaction, e.g. when\nupdating references in two different worktrees at once. Consequently,\nthe update index for all but the first argument remain uninitialized,\nwhich may cause undefined behaviour.\n\nFix this by moving the assignment of the maximum update index in\n`reftable_be_transaction_finish()` inside the loop, which ensures that\nall elements of the array are correctly initialized.\n\nFurthermore, initialize the `max_index` field to 0 when queueing a new\ntransaction argument. This is not strictly necessary, as all elements of\n`write_transaction_table_arg.max_index` are now assigned correctly.\nHowever, this initialization is added for consistency and to safeguard\nagainst potential future changes that might inadvertently introduce\nuninitialized memory access.\n\nReported-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\nSigned-off-by: Karthik Nayak <karthik.188@gmail.com>\nSigned-off-by: Patrick Steinhardt <ps@pks.im>\n---\n\nHi,\n\nKarthik is out of office this week, so I'm taking over this series for\nhim to ensure that it lands soonish. The only change compared to v3 is\nan adapted commit message based on my own feedback.\n\nThanks!\n\nPatrick\n\n refs/reftable-backend.c | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/refs/reftable-backend.c b/refs/reftable-backend.c\nindex 289496058e..d39a14c5a4 100644\n--- a/refs/reftable-backend.c\n+++ b/refs/reftable-backend.c\n@@ -1020,6 +1020,7 @@ static int prepare_transaction_update(struct write_transaction_table_arg **out,\n \t\targ->updates_nr = 0;\n \t\targ->updates_alloc = 0;\n \t\targ->updates_expected = 0;\n+\t\targ->max_index = 0;\n \t}\n \n \targ->updates_expected++;\n@@ -1628,10 +1629,9 @@ static int reftable_be_transaction_finish(struct ref_store *ref_store UNUSED,\n \tstruct reftable_transaction_data *tx_data = transaction->backend_data;\n \tint ret = 0;\n \n-\tif (tx_data->args)\n-\t\ttx_data->args->max_index = transaction->max_index;\n-\n \tfor (size_t i = 0; i < tx_data->args_nr; i++) {\n+\t\ttx_data->args[i].max_index = transaction->max_index;\n+\n \t\tret = reftable_addition_add(tx_data->args[i].addition,\n \t\t\t\t\t    write_transaction_table, &tx_data->args[i]);\n \t\tif (ret < 0)\n\nRange-diff against v3:\n1:  614274cede ! 1:  b7e3dd3cc8 refs: fix uninitialized memory access of `max_index`\n    @@ Metadata\n     Author: Karthik Nayak <karthik.188@gmail.com>\n     \n      ## Commit message ##\n    -    refs: fix uninitialized memory access of `max_index`\n    +    refs/reftable: fix uninitialized memory access of `max_index`\n     \n         When migrating reflogs between reference backends, maintaining the\n         original order of the reflog entries is crucial. To achieve this, an\n    -    `index` field is stored within the `ref_update` struct.\n    +    `index` field is stored within the `ref_update` struct that encodes the\n    +    relative order of reflog entries. This field is used by the reftable\n    +    backend as update index for the respective reflog entries to maintain\n    +    that ordering.\n     \n    -    In the reftable backend, before writing any references, the writer must\n    -    be configured with the minimum and maximum update index values. The\n    -    `max_update_index` is derived from the maximum `ref_update.index` value\n    -    in a transaction . The commit bc67b4ab5f (reftable: write correct\n    -    max_update_index to header, 2025-01-15) addressed this by propagating the\n    -    `max_update_index` value from the transaction to\n    -    `write_transaction_table_arg` and, ultimately, to\n    -    `reftable_writer_set_limits()`, which sets the min and max index for the\n    -    reftable writer.\n    +    These update indices must be respected when writing table headers, which\n    +    encode the minimum and maximum update index of contained records in the\n    +    header and footer. This logic was added in commit bc67b4ab5f (reftable:\n    +    write correct max_update_index to header, 2025-01-15), which started to\n    +    use `reftable_writer_set_limits()` to propagate the mininum and maximum\n    +    update index of all records contained in a ref transaction.\n     \n    -    However, that commit introduced an issue:\n    +    However, we only set the maximum update index for the first transaction\n    +    argument, even though there can be multiple such arguments. This is the\n    +    case when we write to multiple stacks in a single transaction, e.g. when\n    +    updating references in two different worktrees at once. Consequently,\n    +    the update index for all but the first argument remain uninitialized,\n    +    which may cause undefined behaviour.\n     \n    -      - In `reftable_transaction_data`, which contains an array of\n    -      `write_transaction_table_arg`, only the first element was assigned the\n    -      `max_index` value.\n    +    Fix this by moving the assignment of the maximum update index in\n    +    `reftable_be_transaction_finish()` inside the loop, which ensures that\n    +    all elements of the array are correctly initialized.\n     \n    -    As a result, any elements beyond the first in the array contained\n    -    uninitialized `max_index`. The writer contains multiple elements of\n    -    `write_transaction_table_arg` to correspond to different worktrees being\n    -    written. This uninitialized value was later used to set the\n    -    `max_update_index` for the writer, potentially causing overflow or\n    -    undefined behavior.\n    -\n    -    Fix this by:\n    -\n    -      - Initializing the `max_index` field to 0.\n    -      - Moving the assignment of `max_index` in\n    -      `reftable_be_transaction_finish()` inside the loop, ensuring all\n    -      elements of the array are correctly initialized.\n    -\n    -    Initializing `max_index` to `0` is not strictly necessary, as all\n    -    elements of `write_transaction_table_arg.max_index` are now assigned\n    -    correctly. However, this initialization is added for consistency and to\n    -    safeguard against potential future changes that might inadvertently\n    -    introduce uninitialized memory access.\n    +    Furthermore, initialize the `max_index` field to 0 when queueing a new\n    +    transaction argument. This is not strictly necessary, as all elements of\n    +    `write_transaction_table_arg.max_index` are now assigned correctly.\n    +    However, this initialization is added for consistency and to safeguard\n    +    against potential future changes that might inadvertently introduce\n    +    uninitialized memory access.\n     \n         Reported-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>\n         Signed-off-by: Karthik Nayak <karthik.188@gmail.com>\n    +    Signed-off-by: Patrick Steinhardt <ps@pks.im>\n     \n      ## refs/reftable-backend.c ##\n     @@ refs/reftable-backend.c: static int prepare_transaction_update(struct write_transaction_table_arg **out,\n-- \n2.48.1.362.g079036d154.dirty\n\n"},{"id":"511233","messageId":"Z5ddq3DE071XLzEg@pks.im","threadId":"62860","inReplyTo":"b7e3dd3cc870024f0e80dad26c5a7a96483c6cf4.1737970803.git.ps@pks.im","subject":"Re: [PATCH v4] refs/reftable: fix uninitialized memory access of `max_index`","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-01-27T10:19:23Z","receivedAt":"2025-01-27T10:19:28Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Mon, Jan 27, 2025 at 10:44:08AM +0100, Patrick Steinhardt wrote:\n> Karthik is out of office this week, so I'm taking over this series for\n> him to ensure that it lands soonish. The only change compared to v3 is\n> an adapted commit message based on my own feedback.\n\nUgh, sorry, forgot to set the In-reply-to header. This was meant as a\nreply to [1].\n\nPatrick\n\n[1]: <CAOLa=ZR=Hz+LU0n-uC2dpk8_sLqAyaxO0NswJH8bP_kEdDdbUQ@mail.gmail.com>\n"},{"id":"511279","messageId":"xmqqmsfcusyo.fsf@gitster.g","threadId":"62860","inReplyTo":"Z5ddq3DE071XLzEg@pks.im","subject":"Re: [PATCH v4] refs/reftable: fix uninitialized memory access of `max_index`","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-01-27T16:22:55Z","receivedAt":"2025-01-27T16:22:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Patrick Steinhardt <ps@pks.im> writes:\n\n> On Mon, Jan 27, 2025 at 10:44:08AM +0100, Patrick Steinhardt wrote:\n>> Karthik is out of office this week, so I'm taking over this series for\n>> him to ensure that it lands soonish. The only change compared to v3 is\n>> an adapted commit message based on my own feedback.\n>\n> Ugh, sorry, forgot to set the In-reply-to header. This was meant as a\n> reply to [1].\n>\n> Patrick\n>\n> [1]: <CAOLa=ZR=Hz+LU0n-uC2dpk8_sLqAyaxO0NswJH8bP_kEdDdbUQ@mail.gmail.com>\n\nThanks, that was very helpful.  This turns the v3 that was a\nreplacement for a patch that was already in 'next' into an\nincremental form, and replaces the kn/reflog-migration-fix-fix topic\nwhich was my tentative attempt.\n\nWill queue.\n"}]}