From: Junio C Hamano <gitster@pobox.com>
To: Patrick Steinhardt <ps@pks.im>
Cc: git@vger.kernel.org, "Karthik Nayak" <karthik.188@gmail.com>,
"Justin Tobler" <jltobler@gmail.com>,
"SZEDER Gábor" <szeder.dev@gmail.com>,
"Toon Claes" <toon@iotcl.com>, "Jeff King" <peff@peff.net>,
"Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com>,
"Ben Knoble" <ben.knoble@gmail.com>
Subject: Re: [PATCH v3 7/9] refs/files: detect race when generating reflog entry for HEAD
Date: Tue, 29 Jul 2025 09:16:51 -0700 [thread overview]
Message-ID: <xmqqpldjotu4.fsf@gitster.g> (raw)
In-Reply-To: <20250729-pks-reflog-append-v3-7-9614d310f073@pks.im> (Patrick Steinhardt's message of "Tue, 29 Jul 2025 10:55:25 +0200")
Patrick Steinhardt <ps@pks.im> writes:
> When updating a reference that is being pointed to HEAD we don't only
> write a reflog message for that particular reference, but also generate
> one for HEAD. This logic is handled by `split_head_update()`, where we:
>
> 1. Verify that the condition actually triggered. This is done by
> reading HEAD at the start of the transaction so that we can then
> check whether a given reference update refers to its target.
>
> 2. Queue a new log-only update for HEAD in case it did.
>
> But the logic is unfortunately not free of races, as we do not lock the
> HEAD reference after we have read its target. This can lead to the
> following two scenarios:
>
> - HEAD gets concurrently updated to point to one of the references we
> have already processed. This causes us not writing a reflog message
> even though we should have done so.
>
> - HEAD gets concurrently updated to point to not point to a reference
> anymore that we have already processed. This causes us to write a
> reflog message even though we should _not_ have done so.
>
> Improve the situation by introducing a new `REF_LOG_VIA_SPLIT` flag that
> is specific to the "files" backend. If set, we will double check that
> the HEAD reference still points to the reference that we are creating
> the reflog entry for after we have locked HEAD. Furthermore, instead of
> manually resolving the old object ID of that entry, we now use the same
> old state as for the parent update.
>
> Unfortunately, this change only helps with the second race. We cannot
> reliably plug the first race without locking the HEAD reference at the
> start of the transaction. Locking HEAD unconditionally would effectively
> serialize all writes though, and that doesn't seem like an option. Also,
> double checking its value at the end of the transaction is not an option
> either, as its target may have flip-flopped during the transaction.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> refs/files-backend.c | 40 ++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 38 insertions(+), 2 deletions(-)
This is a step new in this iteration. I sometimes wonder if the
world were in a much better shape if I didn't record the updates to
underlying branch in the reflog of HEAD (and limited only to record
switching branches), as this is a fallout from the (mis)design.
Anyway, I agree that the change in this patch matches the above
description and takes us in a better place ;-)
Thanks.
> diff --git a/refs/files-backend.c b/refs/files-backend.c
> index bf6f89b1d19..ba018b0984a 100644
> --- a/refs/files-backend.c
> +++ b/refs/files-backend.c
> @@ -68,6 +68,12 @@
> */
> #define REF_DELETED_RMDIR (1 << 9)
>
> +/*
> + * Used to indicate that the reflog-only update has been created via
> + * `split_head_update()`.
> + */
> +#define REF_LOG_VIA_SPLIT (1 << 14)
> +
> struct ref_lock {
> char *ref_name;
> struct lock_file lk;
> @@ -2420,9 +2426,10 @@ static enum ref_transaction_error split_head_update(struct ref_update *update,
>
> new_update = ref_transaction_add_update(
> transaction, "HEAD",
> - update->flags | REF_LOG_ONLY | REF_NO_DEREF,
> + update->flags | REF_LOG_ONLY | REF_NO_DEREF | REF_LOG_VIA_SPLIT,
> &update->new_oid, &update->old_oid,
> NULL, NULL, update->committer_info, update->msg);
> + new_update->parent_update = update;
>
> /*
> * Add "HEAD". This insertion is O(N) in the transaction
> @@ -2600,7 +2607,36 @@ static enum ref_transaction_error lock_ref_for_update(struct files_ref_store *re
>
> update->backend_data = lock;
>
> - if (update->type & REF_ISSYMREF) {
> + if (update->flags & REF_LOG_VIA_SPLIT) {
> + struct ref_lock *parent_lock;
> +
> + if (!update->parent_update)
> + BUG("split update without a parent");
> +
> + parent_lock = update->parent_update->backend_data;
> +
> + /*
> + * Check that "HEAD" didn't racily change since we have looked
> + * it up. If it did we must refuse to write the reflog entry.
> + *
> + * Note that this does not catch all races: if "HEAD" was
> + * racily changed to point to one of the refs part of the
> + * transaction then we would miss writing the split reflog
> + * entry for "HEAD".
> + */
> + if (!(update->type & REF_ISSYMREF) ||
> + strcmp(update->parent_update->refname, referent.buf)) {
> + strbuf_addstr(err, "HEAD has been racily updated");
> + ret = REF_TRANSACTION_ERROR_GENERIC;
> + goto out;
> + }
> +
> + if (update->flags & REF_HAVE_OLD) {
> + oidcpy(&lock->old_oid, &update->old_oid);
> + } else {
> + oidcpy(&lock->old_oid, &parent_lock->old_oid);
> + }
> + } else if (update->type & REF_ISSYMREF) {
> if (update->flags & REF_NO_DEREF) {
> /*
> * We won't be reading the referent as part of
next prev parent reply other threads:[~2025-07-29 16:16 UTC|newest]
Thread overview: 114+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-22 11:20 [PATCH 0/8] refs: fix migration of reflog entries Patrick Steinhardt
2025-07-22 11:20 ` [PATCH 1/8] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-07-22 22:04 ` Junio C Hamano
2025-07-22 11:20 ` [PATCH 2/8] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-07-23 18:14 ` Justin Tobler
2025-07-24 7:42 ` Patrick Steinhardt
2025-07-24 16:45 ` Junio C Hamano
2025-07-22 11:20 ` [PATCH 3/8] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-07-23 18:25 ` Justin Tobler
2025-07-24 8:36 ` Karthik Nayak
2025-07-24 12:55 ` Toon Claes
2025-07-22 11:20 ` [PATCH 4/8] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-07-23 19:00 ` Justin Tobler
2025-07-24 7:42 ` Patrick Steinhardt
2025-07-24 12:54 ` Toon Claes
2025-07-25 5:36 ` Patrick Steinhardt
2025-07-24 16:20 ` SZEDER Gábor
2025-07-24 21:10 ` Junio C Hamano
2025-07-25 5:36 ` Patrick Steinhardt
2025-07-25 14:35 ` Junio C Hamano
2025-07-22 11:20 ` [PATCH 5/8] ident: fix type of string length parameter Patrick Steinhardt
2025-07-22 11:20 ` [PATCH 6/8] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-07-23 19:41 ` Justin Tobler
2025-07-24 7:42 ` Patrick Steinhardt
2025-07-24 9:41 ` Karthik Nayak
2025-07-24 12:56 ` Toon Claes
2025-07-22 11:20 ` [PATCH 7/8] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-07-23 20:31 ` Justin Tobler
2025-07-24 7:42 ` Patrick Steinhardt
2025-07-24 10:21 ` Karthik Nayak
2025-07-24 11:35 ` Patrick Steinhardt
2025-07-22 11:20 ` [PATCH 8/8] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
2025-07-22 22:09 ` Junio C Hamano
2025-07-23 4:04 ` Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 0/8] refs: fix migration of reflog entries Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 1/8] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 2/8] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 3/8] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 4/8] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-07-28 15:33 ` Kristoffer Haugsbakk
2025-07-28 18:49 ` Junio C Hamano
2025-07-28 20:39 ` Karthik Nayak
2025-07-28 20:59 ` Junio C Hamano
2025-07-30 7:55 ` Karthik Nayak
2025-07-29 0:25 ` Ben Knoble
2025-07-29 6:14 ` Kristoffer Haugsbakk
2025-07-29 6:51 ` Patrick Steinhardt
2025-07-29 15:00 ` Junio C Hamano
2025-07-30 5:33 ` Patrick Steinhardt
2025-07-30 10:33 ` Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 5/8] ident: fix type of string length parameter Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 6/8] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 7/8] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-07-25 11:36 ` Jeff King
2025-07-28 14:43 ` Patrick Steinhardt
2025-07-29 7:14 ` Jeff King
2025-07-29 7:54 ` Patrick Steinhardt
2025-07-25 6:58 ` [PATCH v2 8/8] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 0/9] refs: fix migration of reflog entries Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 1/9] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 2/9] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 3/9] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-08-01 11:38 ` Toon Claes
2025-08-04 7:37 ` Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 4/9] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-07-29 16:07 ` Junio C Hamano
2025-08-01 11:37 ` Toon Claes
2025-08-04 7:38 ` Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 5/9] ident: fix type of string length parameter Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 6/9] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 7/9] refs/files: detect race when generating reflog entry for HEAD Patrick Steinhardt
2025-07-29 16:16 ` Junio C Hamano [this message]
2025-08-01 11:55 ` Toon Claes
2025-08-02 11:11 ` Jeff King
2025-08-04 7:38 ` Patrick Steinhardt
2025-08-04 14:47 ` Jeff King
2025-07-29 8:55 ` [PATCH v3 8/9] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-07-29 8:55 ` [PATCH v3 9/9] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 0/9] refs: fix migration of reflog entries Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 1/9] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 2/9] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 3/9] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 4/9] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 5/9] ident: fix type of string length parameter Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 6/9] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 7/9] refs/files: detect race when generating reflog entry for HEAD Patrick Steinhardt
2025-08-04 15:38 ` Jeff King
2025-08-04 9:46 ` [PATCH v4 8/9] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-08-04 9:46 ` [PATCH v4 9/9] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 0/9] refs: fix migration of reflog entries Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 1/9] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-08-05 17:04 ` Jean-Noël AVILA
2025-08-05 21:47 ` Junio C Hamano
2025-08-06 5:53 ` Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 2/9] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 3/9] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 4/9] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 5/9] ident: fix type of string length parameter Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 6/9] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 7/9] refs/files: detect race when generating reflog entry for HEAD Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 8/9] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-08-05 15:11 ` [PATCH v5 9/9] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
2025-08-05 18:47 ` [PATCH v5 0/9] refs: fix migration of reflog entries Jeff King
2025-08-06 5:53 ` Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 " Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 1/9] Documentation/git-reflog: convert to use synopsis type Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 2/9] builtin/reflog: improve grouping of subcommands Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 3/9] refs: export `ref_transaction_update_reflog()` Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 4/9] builtin/reflog: implement subcommand to write new entries Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 5/9] ident: fix type of string length parameter Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 6/9] refs: fix identity for migrated reflogs Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 7/9] refs/files: detect race when generating reflog entry for HEAD Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 8/9] refs: stop unsetting REF_HAVE_OLD for log-only updates Patrick Steinhardt
2025-08-06 5:54 ` [PATCH v6 9/9] refs: fix invalid old object IDs when migrating reflogs Patrick Steinhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=xmqqpldjotu4.fsf@gitster.g \
--to=gitster@pobox.com \
--cc=ben.knoble@gmail.com \
--cc=git@vger.kernel.org \
--cc=jltobler@gmail.com \
--cc=karthik.188@gmail.com \
--cc=kristofferhaugsbakk@fastmail.com \
--cc=peff@peff.net \
--cc=ps@pks.im \
--cc=szeder.dev@gmail.com \
--cc=toon@iotcl.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).