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

[PATCH 00/17] Remove assumptions about refname lifetimes

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
May 19, 2013, 20:26 UTC
Message-ID
<1368995232-11042-1-git-send-email-mhagger@alum.mit.edu>

Prior to this patch series, the refs API said nothing about the lifetime of the refname parameter passed to each_ref_fn callbacks by the for_each_ref()-style iteration functions. De facto, the refnames usually had long lives because they were pointers into the ref_cache data structures, and those are only invalidated under rare circumstances. And some callers were assuming a long lifetime, for example storing references to the refname string instead of copying it.

But it has long been the case that ref caches could be invalidated, for example when a packed ref is deleted. AFAIK there was never much clarity about what that might mean for callers.

Recently a number of race conditions related to references have been discovered. There is likely to be a two-pronged solution to the races:

* For traditional, filesystem-based references, there will have to be
  more checks that the ref caches are still up-to-date at the time of
  their use (see, for example, [1]).  If not, the ref cache will have
  to be invalidated and reloaded.  Assuming that the invalidation of
  the old cache includes freeing its memory, such an invalidation will
  cause lots of refname strings to be freed even though callers might
  still hold references to them.
* For server-class installations, filesystem-based references might
  not be robust enough for 100% reliable operation, because the
  reading of the complete set of references is not an atomic
  operation.  If another reference storage mechanism is developed,
  there is no reason to expect the refnames strings to have long
  lifetimes.

A prerequisite for either of these approaches is to harmonize what callers assume and what the API guarantees.

The purpose of this patch series is to track down callers who assume that the refnames that they receive via a each_ref_fn callback have lifetimes beyond the duration of the callback invocation and to rewrite them to work without that assumption. The final patch documents explicitly that callers should not retain references to the refnames.

A word about how I audited the code:

To find callers making unwarranted assumptions, I (temporarily) changed do_one_ref() to do a xstrdup() of the refname, pass the copy to the callback function, then free() the copy. This caused ill-behaved callers to access freed memory, which could be detected by running the testsuite under valgrind. There were indeed a number of such errors. All of them are fixed by this patch series, and the test just described now runs without errors.

I plan to do a second audit by hand to see if the test suite and/or valgrind missed anything.

The last two patches are RFCs. I would like some input on the second to last because I am not very familiar with how the object array entry names are used, how many might be created, etc. The last patch is an illustration of how I would like to change the API docs, but it will only be ready when all of the code has been audited and adapted. Please see especially my comments on these two patches for more information.

[1] http://thread.gmane.org/gmane.comp.version-control.git/223299
Michael Haggerty (17):
  describe: make own copy of refname
  fetch: make own copies of refnames
  add_rev_cmdline(): make a copy of the name argument
  builtin_diff_tree(): make it obvious that function wants two entries
  cmd_diff(): use an object_array for holding trees
  cmd_diff(): rename local variable "list" -> "entry"
  cmd_diff(): make it obvious which cases are exclusive of each other
  revision: split some overly-long lines
  gc_boundary(): move the check "alloc <= nr" to caller
  get_revision_internal(): make check less mysterious
  object_array: add function object_array_filter()
  object_array_remove_duplicates(): rewrite to reduce copying
  fsck: don't put a void*-shaped peg in a char*-shaped hole
  find_first_merges(): initialize merges variable using initializer
  find_first_merges(): remove unnecessary code
  object_array_entry: copy name before storing in name field
  refs: document the lifetime of the refname passed to each_ref_fn
 builtin/describe.c |  6 +++--
 builtin/diff.c     | 68 ++++++++++++++++++++++++++----------------------------
 builtin/fetch.c    |  4 ++--
 builtin/fsck.c     |  2 +-
 object.c           | 50 +++++++++++++++++++++++++++++++--------
 object.h           | 23 ++++++++++++++++--
 refs.h             | 22 +++++++++++++-----
 revision.c         | 61 +++++++++++++++++++++++++-----------------------
 revision.h         | 32 ++++++++++++++++---------
 submodule.c        |  6 ++---
 10 files changed, 172 insertions(+), 102 deletions(-)
-- 
1.8.2.3
Next: Michael Haggerty
Message 1 of 42 in “Remove assumptions about refname lifetimes”
  1. 00/17 Remove assumptions about refname lifetimesMichael Haggerty, May 19, 2013
  2. 01/17 describe: make own copy of refnameMichael Haggerty, May 19, 2013
  3. 02/17 fetch: make own copies of refnamesMichael Haggerty, May 19, 2013
  4. 03/17 add_rev_cmdline(): make a copy of the name argumentMichael Haggerty, May 19, 2013
  5. 04/17 builtin_diff_tree(): make it obvious that function wants two entriesMichael Haggerty, May 19, 2013
  6. Junio C HamanoMay 21, 2013
  7. Michael HaggertyMay 23, 2013
  8. 05/17 cmd_diff(): use an object_array for holding treesMichael Haggerty, May 19, 2013
  9. Junio C HamanoMay 21, 2013
  10. Michael HaggertyMay 23, 2013
  11. 06/17 cmd_diff(): rename local variable "list" -> "entry"Michael Haggerty, May 19, 2013
  12. 07/17 cmd_diff(): make it obvious which cases are exclusive of each otherMichael Haggerty, May 19, 2013
  13. 08/17 revision: split some overly-long linesMichael Haggerty, May 19, 2013
  14. Junio C HamanoMay 21, 2013
  15. Michael HaggertyMay 23, 2013
  16. Junio C HamanoMay 23, 2013
  17. 09/17 gc_boundary(): move the check "alloc <= nr" to callerMichael Haggerty, May 19, 2013
  18. Junio C HamanoMay 21, 2013
  19. Michael HaggertyMay 23, 2013
  20. Junio C HamanoMay 23, 2013
  21. 10/17 get_revision_internal(): make check less mysteriousMichael Haggerty, May 19, 2013
  22. Junio C HamanoMay 21, 2013
  23. Michael HaggertyMay 23, 2013
  24. 11/17 object_array: add function object_array_filter()Michael Haggerty, May 19, 2013
  25. 12/17 object_array_remove_duplicates(): rewrite to reduce copyingMichael Haggerty, May 19, 2013
  26. 13/17 fsck: don't put a void*-shaped peg in a char*-shaped holeMichael Haggerty, May 19, 2013
  27. 14/17 find_first_merges(): initialize merges variable using initializerMichael Haggerty, May 19, 2013
  28. 15/17 find_first_merges(): remove unnecessary codeMichael Haggerty, May 19, 2013
  29. 16/17 object_array_entry: copy name before storing in name fieldMichael Haggerty, May 19, 2013
  30. Johan HerlandMay 20, 2013
  31. Michael HaggertyMay 20, 2013
  32. Jeff KingMay 20, 2013
  33. Michael HaggertyMay 20, 2013
  34. 17/17 refs: document the lifetime of the refname passed to each_ref_fnMichael Haggerty, May 19, 2013
  35. Johan HerlandMay 20, 2013
  36. Michael HaggertyMay 20, 2013
  37. Junio C HamanoMay 20, 2013
  38. Jeff KingMay 20, 2013
  39. Johan HerlandMay 20, 2013
  40. Junio C HamanoMay 20, 2013
  41. Johan HerlandMay 20, 2013
  42. Junio C HamanoMay 21, 2013

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.