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

Re: Bug#569505: git-core: 'git add' corrupts repository if the working directory is modified as it runs

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Feb 12, 2010, 00:27 UTC
Message-ID
<20100212002741.GB9883@progeny.tock>
In-Reply-To
<20100211234753.22574.48799.reportbug@gibbs.hungrycats.org>
Hi gitsters,

Zygo Blaxell reported through http://bugs.debian.org/569505 that ‘git update-index’ has some issues when the files it is adding change under its feet:

My thoughts:
 - Low-hanging fruit: it should be possible for update-index to check
   the stat information to see if the file has changed between when it
   first opens it and when it finishes.
 - Zygo reported suppress that ‘git gc’ didn’t notice the problem.
   Should ‘git gc’ imply a ‘git fsck --no-full’?
 - Recovering from this kind of mistake in early history is indeed
   hard.  Any tricks for doing this?  Maybe fast-export | fast-import
   can do something with this, or maybe replace + filter-branch once
   it learns to be a little smarter.
 - How do checkout-index and cat-file blob react to a blob whose
   contents do not reflect its object name?  Are they behaving
   appropriately?  I would want cat-file blob to be able to retrieve
   such a broken blob’s contents, checkout-index not so much.

I imagine there are other things to learn, too. The report and reproduction recipe follow.

Thoughts? Jonathan

Package: git-core
Version: 1:1.6.6.1-1
Severity: important

'git add' will happily corrupt a git repo if it is run while files in the working directory are being modified. A blob is added to the index with contents that do not match its SHA1 hash. If the index is then committed, the corrupt blob cannot be checked out (or is checked out with incorrect contents, depending on which tool you use to try to get the file out of git) in the future.

Surprisingly, it's possible to clone, fetch, push, pull, and sometimes even gc the corrupted repo several times before anyone notices the corruption. If the affected commit is included in a merge with history from other git users, the only way to fix it is to rebase (or come up with a blob whose contents match the affected SHA1 hash somehow).

It is usually possible to retrieve data committed before the corruption by simply checking out an earlier tree in the affected branch's history.

The following shell code demonstrates this problem. It runs a thread which continuously modifies a file, and another thread that does 'git commit -am' and 'git fsck' in a continuous loop until corruption is detected. This might take up to 20 seconds on a slow machine.

	#!/bin/sh
	set -e
	# Create an empty git repo in /tmp/git-test
	rm -fr /tmp/git-test
	mkdir /tmp/git-test
	cd /tmp/git-test
	git init
	# Create a file named foo and add it to the repo
	touch foo
	git add foo
	# Thread 1:  continuously modify foo:
	while echo -n .; do
		dd if=/dev/urandom of=foo count=1024 bs=1k conv=notrunc >/dev/null 2>&1
	done &
	# Thread 2:  loop until the repo is corrupted
	while git fsck; do
		# Note the implied 'git add' in 'commit -a'
		# It will do the same with explicit 'git add'
		git commit -a -m'Test'
	done
	# Kill thread 1, we don't need it any more
	kill $!
	# Success!  Well, sort of.
	echo Repository is corrupted.  Have a nice day.

I discovered this bug accidentally when I was using inotifywait (from the inotify-tools package) to automatically commit snapshots of a working directory triggered by write events.

I tested this with a number of kernel versions from 2.6.27 to 2.6.31. All of them reproduced this problem. I checked this because strace shows 'git add' doing a mmap(..., MAP_PRIVATE, ...) of its input file, so I was wondering if there might have been a recent change in mmap() behavior in either git or the kernel.

git 1.5.6.5 has this problem too, but some of the error messages are different, and the problem sometimes manifests itself as silent corruption of other objects (e.g. if someone checks out a corrupt tree and then does 'git add -u' or 'git commit -a', they will include the corrupt data in their commit).

Next: Zygo Blaxell
Message 1 of 84 in “Re: Bug#569505: git-core: 'git add' corrupts repository if the working directory is modified as it runs”
  1. Jonathan NiederFeb 12, 2010
  2. Zygo BlaxellFeb 12, 2010
  3. Jonathan NiederFeb 13, 2010
  4. Ilari LiusvaaraFeb 13, 2010
  5. Thomas RastFeb 13, 2010
  6. Ilari LiusvaaraFeb 13, 2010
  7. Dmitry PotapovFeb 13, 2010
  8. Zygo BlaxellFeb 13, 2010
  9. don't use mmap() to hash filesDmitry Potapov, Feb 14, 2010
  10. Junio C HamanoFeb 14, 2010
  11. Dmitry PotapovFeb 14, 2010
  12. Junio C HamanoFeb 14, 2010
  13. Thomas RastFeb 14, 2010
  14. Junio C HamanoFeb 14, 2010
  15. Johannes SchindelinFeb 14, 2010
  16. Junio C HamanoFeb 14, 2010
  17. Dmitry PotapovFeb 14, 2010
  18. Jakub NarebskiFeb 14, 2010
  19. Paolo BonziniFeb 14, 2010
  20. Johannes SchindelinFeb 14, 2010
  21. Dmitry PotapovFeb 14, 2010
  22. Johannes SchindelinFeb 14, 2010
  23. Johannes SchindelinFeb 14, 2010
  24. Dmitry PotapovFeb 14, 2010
  25. Zygo BlaxellFeb 14, 2010
  26. Nicolas PitreFeb 15, 2010
  27. Dmitry PotapovFeb 15, 2010
  28. Paolo BonziniFeb 15, 2010
  29. Dmitry PotapovFeb 15, 2010
  30. Dmitry PotapovFeb 14, 2010
  31. Avery PennarunFeb 14, 2010
  32. Nicolas PitreFeb 15, 2010
  33. Avery PennarunFeb 15, 2010
  34. Nicolas PitreFeb 15, 2010
  35. Avery PennarunFeb 15, 2010
  36. Nicolas PitreFeb 15, 2010
  37. don't use mmap() to hash filesDmitry Potapov, Feb 14, 2010
  38. Teach "git add" and friends to be paranoidJunio C Hamano, Feb 18, 2010
  39. Junio C HamanoFeb 18, 2010
  40. Zygo BlaxellFeb 18, 2010
  41. Junio C HamanoFeb 19, 2010
  42. Jeff KingFeb 18, 2010
  43. Nicolas PitreFeb 18, 2010
  44. Junio C HamanoFeb 18, 2010
  45. Wincent ColaiutaFeb 18, 2010
  46. Zygo BlaxellFeb 18, 2010
  47. Jonathan NiederFeb 18, 2010
  48. Junio C HamanoFeb 18, 2010
  49. Paolo BonziniFeb 22, 2010
  50. Dmitry PotapovFeb 22, 2010
  51. Thomas RastFeb 18, 2010
  52. Junio C HamanoFeb 18, 2010
  53. Nicolas PitreFeb 18, 2010
  54. 16 gig, 350,000 file repositoryBill Lear, Feb 18, 2010
  55. Nicolas PitreFeb 18, 2010
  56. Erik Faye-LundFeb 19, 2010
  57. Bill LearFeb 22, 2010
  58. Nicolas PitreFeb 22, 2010
  59. Peter HarrisFeb 18, 2010
  60. Junio C HamanoFeb 18, 2010
  61. Nicolas PitreFeb 18, 2010
  62. Jonathan NiederFeb 19, 2010
  63. Zygo BlaxellFeb 19, 2010
  64. Junio C HamanoFeb 19, 2010
  65. Zygo BlaxellFeb 19, 2010
  66. Dmitry PotapovFeb 19, 2010
  67. Junio C HamanoFeb 19, 2010
  68. Junio C HamanoFeb 20, 2010
  69. Dmitry PotapovFeb 21, 2010
  70. Junio C HamanoFeb 21, 2010
  71. Dmitry PotapovFeb 22, 2010
  72. Junio C HamanoFeb 22, 2010
  73. Dmitry PotapovFeb 22, 2010
  74. Nicolas PitreFeb 22, 2010
  75. Dmitry PotapovFeb 22, 2010
  76. Zygo BlaxellFeb 22, 2010
  77. Nicolas PitreFeb 22, 2010
  78. Junio C HamanoFeb 22, 2010
  79. Nicolas PitreFeb 22, 2010
  80. Dmitry PotapovFeb 22, 2010
  81. Nicolas PitreFeb 22, 2010
  82. mmap with MAP_PRIVATE is useless (was Re: Bug#569505: git-core: 'git add' corrupts repository if the working directory is modified as it runs)Paolo Bonzini, Feb 14, 2010
  83. Junio C HamanoFeb 14, 2010
  84. Paolo BonziniFeb 14, 2010

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.