git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [BUG] [PATCH] infinite loop due to broken symlink

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Mar 25, 2015, 22:53 UTC
Message-ID
<55133C63.2010805@alum.mit.edu>
In-Reply-To
<5510399D.4020007@redhat.com>
On 03/23/2015 05:04 PM, Petr Stodulka wrote:
Show 5 quoted lines
> git goes into an infinite loop due to broken symlink (minimal reproducer
> [0]).  Affected code is in function
> "resolve_ref_unsafe" in file refs.c - notice 'stat_ref'. There is comment
> about problem with race condition, hovewer in that case it's regular broken
> symlink which cause infinite loop.

Thanks for the bug report. I can confirm the problem. In fact, I noticed the same problem when I was working on a refactoring in the area, but I still haven't submitted those patches. Luckily, modern Git doesn't use symlinks in the loose reference hierarchy, so most users will never encounter this problem.

In fact I think it is only the open() call that can lead to the infinite loop. Meanwhile, there is another problem caused by falling through the symlink-handling code, namely that st reflects the lstat() of the symlink rather than the stat() of the file that it points to.

My approach was to run stat() on the path if the symlink-handling code falls through. You can see my work in progress in my GitHub repo [1].

Show 52 quoted lines
> Possible patch could be something
> like this:
> 
> -------------------------------------------------------
> diff --git a/refs.c b/refs.c
> index e23542b..9efe8d2 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -1356,6 +1356,7 @@ static struct ref_dir *get_loose_refs(struct
> ref_cache *refs)
>  /* We allow "recursive" symbolic refs. Only within reason, though */
>  #define MAXDEPTH 5
>  #define MAXREFLEN (1024)
> +#define MAXLOOP 1024
> 
>  /*
>   * Called by resolve_gitlink_ref_recursive() after it failed to read
> @@ -1482,6 +1483,7 @@ const char *resolve_ref_unsafe(const char
> *refname, int resolve_flags, unsigned
>         char buffer[256];
>         static char refname_buffer[256];
>         int bad_name = 0;
> +    int loop_counter = 0;
> 
>         if (flags)
>                 *flags = 0;
> @@ -1546,7 +1548,8 @@ const char *resolve_ref_unsafe(const char
> *refname, int resolve_flags, unsigned
>                 if (S_ISLNK(st.st_mode)) {
>                         len = readlink(path, buffer, sizeof(buffer)-1);
>                         if (len < 0) {
> -                               if (errno == ENOENT || errno == EINVAL)
> +                               if (loop_counter++ < MAXLOOP &&
> +                    (errno == ENOENT || errno == EINVAL))
>                                         /* inconsistent with lstat;
> retry */
>                                         goto stat_ref;
>                                 else
> @@ -1579,7 +1582,7 @@ const char *resolve_ref_unsafe(const char
> *refname, int resolve_flags, unsigned
>                  */
>                 fd = open(path, O_RDONLY);
>                 if (fd < 0) {
> -                       if (errno == ENOENT)
> +                       if (loop_counter++ < MAXLOOP && errno == ENOENT)
>                                 /* inconsistent with lstat; retry */
>                                 goto stat_ref;
>                         else
> -------------------------------------------------------
> 
> If I understand well that simple check of broken symlink is not possible
> due to race conditions.

This change also prevents the infinite loop, in fact in a more failproof way that doesn't depend on errno values being interpreted correctly. I would suggest a few stylistic changes, like for example here [2]. On the other hand, this change doesn't solve the lstat()/stat() problem.

Nevertheless, I wouldn't object to a fix like this being accepted in addition to the stat() fix when it's ready. It doesn't hurt to wear both a belt *and* suspenders.

Michael
[1] https://github.com/mhagger/git, branch "wip/refactor-resolve-ref".
    See especially commit d2425d8ac8cf73494b318078b92f5b1c510a68fb.
[2] https://github.com/mhagger/git, branch "ref-broken-symlinks"
-- 
Michael Haggerty
mhagger@alum.mit.edu
Previous: Petr StodulkaNext: Junio C Hamano
Message 2 of 4 in “infinite loop due to broken symlink”
  1. Petr StodulkaMar 23, 2015
  2. Michael HaggertyMar 25, 2015
  3. Junio C HamanoMar 25, 2015
  4. Petr StodulkaMar 26, 2015

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.