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

Re: general protection faults with "git grep" version 1.7.7.1

From
Thomas Rast <trast@student.ethz.ch>
Date
Oct 25, 2011, 13:50 UTC
Message-ID
<201110251550.22248.trast@student.ethz.ch>
In-Reply-To
<20111024214949.GA5237@amd.home.annexia.org>

[Shawn, Peff, Nicolas: maybe you can say something on the (non)raciness of xmalloc() in parallel with read_sha1_file(). See the last paragraph below.]

Richard W.M. Jones wrote:
Show 13 quoted lines
> On Mon, Oct 24, 2011 at 10:11:53PM +0200, Markus Trippelsdorf wrote:
> > Suddenly I'm getting strange protection faults when I run "git grep" on
> > the gcc tree:
> 
> Jim Meyering and I are trying to chase what looks like a similar or
> identical bug in git-grep.  We've not got much further than gdb and
> valgrind so far, but see:
> 
> https://bugzilla.redhat.com/show_bug.cgi?id=747377
> 
> It's slightly suspicious that this bug only started to happen with the
> latest glibc, but that could be coincidence, or could be just that
> glibc exposes a latent bug in git-grep.

I'm tempted to write this off as a GCC bug. If that's ok for you, I'll leave further investigation and communication with the GCC folks to you.

My findings are as follows:

It's easy to reproduce the behavior described in the above bug report, using an F16 beta install in a VM. I gave the VM two cores, but didn't test what happens with only one. By "easy" I mean I didn't have to do any fiddling and it crashes at least one out of two times.

I looked at how git builds grep.o by saying
  rm builtin/grep.o; make V=1
I then modified this to give me the assembly output from the compiler
  gcc -S -s builtin/grep.o -c -MF builtin/.depend/grep.o.d -MMD -MP  -g -O2 -Wall -I.  -DHAVE_PATHS_H -DSHA1_HEADER='<openssl/sha.h>'  -DNO_STRLCPY -DNO_MKSTEMPS  builtin/grep.c

and looked at the result. To interpret the output, I would like to remind you of the following snippets:

  #define grep_lock() pthread_mutex_lock(&grep_mutex)
  #define grep_unlock() pthread_mutex_unlock(&grep_mutex)
...
  static struct work_item *get_work(void)
  {
          struct work_item *ret;
          grep_lock();
          while (todo_start == todo_end && !all_work_added) {
                  pthread_cond_wait(&cond_add, &grep_mutex);
          }
...
  }
...
  static void *run(void *arg)
  {
          int hit = 0;
          struct grep_opt *opt = arg;
          while (1) {
                  struct work_item *w = get_work();
...
          }
...
  }

Getting back to assembly, near the beginning of run() I see (labels and .p2align snipped):

	.loc 1 162 0
	movl	todo_end(%rip), %ebx
	.loc 1 125 0
	movl	$grep_mutex, %edi
	call	pthread_mutex_lock
	.loc 1 126 0
	movl	todo_start(%rip), %eax
	cmpl	%ebx, %eax

I should say that I don't really know much about assembly, in particular not enough to write two correct lines of it. But I can't help noticing that it moved the load of todo_end *out of* the section where grep_mutex is locked. And the comment near the top of the file does say that the whole todo_* family is supposed to be protected by that mutex. What's extra odd is that the .loc seems to indicate that the moved load comes from work_done() instead of get_work(), which is an entirely separate locked section!

Un-inlining the get_work helper using __attribute__((noinline)) makes the assembly

	movl	$grep_mutex, %edi
	call	pthread_mutex_lock
	.loc 1 127 0
	movl	todo_start(%rip), %eax
	cmpl	todo_end(%rip), %eax
	je	.L15

instead; i.e., the load is now after the lock. (Note that line numbers were wiggled by inserting an __attribute__ line.) The beginning of run() turns into exactly the same code if I instead prohibit inlining of work_done().

So AFAICS, we're just unlucky to hit a GCC optimizer bug that voids all guarantees given on locks.

That being said, I'm not entirely convinced that the code in builtin/grep.c works in the face of memory pressure. It guards against concurrent access to read_sha1_file() with the read_sha1_mutex, but any call to xmalloc() outside of that mutex can still potentially invoke the try_to_free_routine. Maybe one of the pack experts can say whether this is safe. (However, I implemented locking around try_to_free_routine as a quick hack and it did not fix the issue discussed in the bug report.)

-- 
Thomas Rast
trast@{inf,student}.ethz.ch
Previous: Bernt HansenNext: Jim Meyering
Message 7 of 15 in “general protection faults with "git grep" version 1.7.7.1”
  1. Markus TrippelsdorfOct 24, 2011
  2. Richard W.M. JonesOct 24, 2011
  3. Markus TrippelsdorfOct 24, 2011
  4. Bernt HansenOct 25, 2011
  5. Jeff KingOct 25, 2011
  6. Bernt HansenOct 25, 2011
  7. Thomas RastOct 25, 2011
  8. Jim MeyeringOct 25, 2011
  9. Markus TrippelsdorfOct 25, 2011
  10. Thomas RastOct 25, 2011
  11. Thomas RastOct 25, 2011
  12. Jim MeyeringOct 25, 2011
  13. Thomas RastOct 25, 2011
  14. Jim MeyeringOct 25, 2011
  15. Jeff KingOct 25, 2011

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.