git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano" <gitster@pobox.com>,
	"Bagas Sanjaya" <bagasdotme@gmail.com>,
	"Emily Shaffer" <emilyshaffer@google.com>,
	"Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
Subject: [PATCH v2 0/2] hooks: fix a race in hook execution
Date: Mon,  7 Mar 2022 13:33:44 +0100	[thread overview]
Message-ID: <cover-v2-0.2-00000000000-20220307T123244Z-avarab@gmail.com> (raw)
In-Reply-To: <cover-0.2-00000000000-20220218T203834Z-avarab@gmail.com>

A documentation & commit-message only change to this v1 which fixes an
obscure race condition in hook execution. For v1 see:
https://lore.kernel.org/git/cover-0.2-00000000000-20220218T203834Z-avarab@gmail.com/

Junio: This topic wasn't picked up yet, but hopefully will be with the
below, which should address coments you & Taylor had on the v1.

Ævar Arnfjörð Bjarmason (2):
  merge: don't run post-hook logic on --no-verify
  hooks: fix an obscure TOCTOU "did we just run a hook?" race

 builtin/commit.c       | 18 +++++++++++-------
 builtin/merge.c        | 28 +++++++++++++++++-----------
 builtin/receive-pack.c |  8 +++++---
 commit.c               |  2 +-
 commit.h               |  3 ++-
 hook.c                 |  7 +++++++
 hook.h                 | 12 ++++++++++++
 sequencer.c            |  4 ++--
 8 files changed, 57 insertions(+), 25 deletions(-)

Range-diff against v1:
1:  9b5144daee6 ! 1:  8f7b01ed758 merge: don't run post-hook logic on --no-verify
    @@ Commit message
         hand. There's no point in invoking discard_cache() here if the hook
         couldn't have possibly updated the index.
     
    +    It's buggy that we use "hook_exist()" here, and as discussed in the
    +    subsequent commit it's subject to obscure race conditions that we're
    +    about to fix, but for now this change is a strict improvement that
    +    retains any caveats to do with the use of "hooks_exist()" as-is.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/merge.c ##
2:  d01d088073b ! 2:  9d16984898c hooks: fix a TOCTOU in "did we run a hook?" heuristic
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    hooks: fix a TOCTOU in "did we run a hook?" heuristic
    +    hooks: fix an obscure TOCTOU "did we just run a hook?" race
     
         Fix a Time-of-check to time-of-use (TOCTOU) race in code added in
         680ee550d72 (commit: skip discarding the index if there is no
         pre-commit hook, 2017-08-14).
     
    -    We can fix the race passing around information about whether or not we
    -    ran the hook in question, instead of running hook_exists() after the
    -    fact to check if the hook in question exists. This problem has been
    +    This obscure race condition can occur if we e.g. ran the "pre-commit"
    +    hook and it modified the index, but hook_exists() returns false later
    +    on (e.g., because the hook itself went away, the directory became
    +    unreadable, etc.). Then we won't call discard_cache() when we should
    +    have.
    +
    +    The race condition itself probably doesn't matter, and users would
    +    have been unlikely to run into it in practice. This problem has been
         noted on-list when 680ee550d72 was discussed[1], but had not been
         fixed.
     
    -    In addition to fixing this for the pre-commit hook as suggested there
    -    I'm also fixing this for the pre-merge-commit hook. See
    -    6098817fd7f (git-merge: honor pre-merge-commit hook, 2019-08-07) for
    -    the introduction of its previous behavior.
    +    This change is mainly intended to improve the readability of the code
    +    involved, and to make reasoning about it more straightforward. It
    +    wasn't as obvious what we were trying to do here, but by having an
    +    "invoked_hook" it's clearer that e.g. our discard_cache() is happening
    +    because of the earlier hook execution.
     
         Let's also change this for the push-to-checkout hook. Now instead of
         checking if the hook exists and either doing a push to checkout or a
    @@ Commit message
         This leaves uses of hook_exists() in two places that matter. The
         "reference-transaction" check in refs.c, see 67541597670 (refs:
         implement reference transaction hook, 2020-06-19), and the
    -    prepare-commit-msg hook, see 66618a50f9c (sequencer: run
    +    "prepare-commit-msg" hook, see 66618a50f9c (sequencer: run
         'prepare-commit-msg' hook, 2018-01-24).
     
         In both of those cases we're saving ourselves CPU time by not
    @@ Commit message
         don't have the hook. So using this "invoked_hook" pattern doesn't make
         sense in those cases.
     
    -    More importantly, in those cases the worst we'll do is miss that we
    -    "should" run the hook because a new hook appeared, whereas in the
    -    pre-commit and pre-merge-commit cases we'll skip an important
    -    discard_cache() on the bases of our faulty guess.
    -
    -    I do think none of these races really matter in practice. It would be
    -    some one-off issue as a hook was added or removed. I did think it was
    -    stupid that we didn't pass a "did this run?" flag instead of doing
    -    this guessing at a distance though, so now we're not guessing anymore.
    +    The "reference-transaction" and "prepare-commit-msg" hook also aren't
    +    racy. In those cases we'll skip the hook runs if we race with a new
    +    hook being added, whereas in the TOCTOU races being fixed here we were
    +    incorrectly skipping the required post-hook logic.
     
         1. https://lore.kernel.org/git/20170810191613.kpmhzg4seyxy3cpq@sigill.intra.peff.net/
     
    @@ hook.h: struct run_hooks_opt
     +
     +	/**
     +	 * A pointer which if provided will be set to 1 or 0 depending
    -+	 * on if a hook was invoked (i.e. existed), regardless of
    -+	 * whether or not that was successful. Used for avoiding
    -+	 * TOCTOU races in code that would otherwise call hook_exist()
    -+	 * after a "maybe hook run" to see if a hook was invoked.
    ++	 * on if a hook was started, regardless of whether or not that
    ++	 * was successful. I.e. if the underlying start_command() was
    ++	 * successful this will be set to 1.
    ++	 *
    ++	 * Used for avoiding TOCTOU races in code that would otherwise
    ++	 * call hook_exist() after a "maybe hook run" to see if a hook
    ++	 * was invoked.
     +	 */
     +	int *invoked_hook;
      };
-- 
2.35.1.1242.gfeba0eae32b


  parent reply	other threads:[~2022-03-07 12:34 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-18 20:43 [PATCH 0/2] hooks: fix a race in hook execution Ævar Arnfjörð Bjarmason
2022-02-18 20:43 ` [PATCH 1/2] merge: don't run post-hook logic on --no-verify Ævar Arnfjörð Bjarmason
2022-02-18 23:57   ` Junio C Hamano
2022-02-18 20:43 ` [PATCH 2/2] hooks: fix a TOCTOU in "did we run a hook?" heuristic Ævar Arnfjörð Bjarmason
2022-02-19  0:11   ` Junio C Hamano
2022-02-19  4:48     ` Ævar Arnfjörð Bjarmason
2022-02-19  4:08   ` Taylor Blau
2022-02-19 10:46     ` Ævar Arnfjörð Bjarmason
2022-03-07 12:33 ` Ævar Arnfjörð Bjarmason [this message]
2022-03-07 12:33   ` [PATCH v2 1/2] merge: don't run post-hook logic on --no-verify Ævar Arnfjörð Bjarmason
2022-03-07 12:33   ` [PATCH v2 2/2] hooks: fix an obscure TOCTOU "did we just run a hook?" race Ævar Arnfjörð Bjarmason
2022-03-21 20:30     ` Jonathan Tan

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

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cover-v2-0.2-00000000000-20220307T123244Z-avarab@gmail.com \
    --to=avarab@gmail.com \
    --cc=bagasdotme@gmail.com \
    --cc=emilyshaffer@google.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.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.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

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 read-only IMAP folder(s) and NNTP newsgroup(s).