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

Re: [PATCH 0/4] plugging some mmap() leaks

From
Ramsay Jones <ramsay@ramsayjones.plus.com>
Date
Mar 6, 2026, 04:37 UTC
Message-ID
<9137fd66-9ac3-42ff-a892-1b6f20b49972@ramsayjones.plus.com>
In-Reply-To
<20260305230315.GA2354983@coredump.intra.peff.net>
On 05/03/2026 11:03 pm, Jeff King wrote:
Show 10 quoted lines
> On Thu, Mar 05, 2026 at 05:02:14PM -0500, Jeff King wrote:
> 
>> Anyway, I think the solution is probably something like the patch above,
>> though probably it needs to cover the case where new_pack is NULL.
> 
> So here is a more polished version. I decided to try running the whole
> test suite with leak-checking and NO_MMAP, and it turned up one other
> case. This series fixes that, too, and then turns on the flag for all
> leak-checking builds.
> 
Hmm, this gives me flash-backs. ;)

Many moons ago, when the cygwin build routinely set NO_MMAP I had an valgrind build of git fail with a 'double free' caused by a call to git_munmap() for a pointer that had already been git_munmap-ed!

In addition, the failure was not reproducible (or at least I could not find such a test). This was at a time when the testsuite took 4+ hours to run for a regular build, let alone a valgrind build. So, to try and pin down the failure, I created a debug version of the mmap compat functions, which I ran with for several weeks, without failing ... :(

It just so happens that about this time I was also testing running the cygwin build without NO_MMAP set. This was a success, so I dropped the NO_MMAP investigation, never having found the cause of the failure!

I have had the 'mmap' branch, with a version of the debug patch, in my cygwin repo for ever (well, the 'author date' says sep 9th 2012, but I know it was somewhat before then). This version of the patch removed the 'debug' output and was only concerned with the error return behaviour of the 'emulated' syscalls. (it was also somewhat non-performant if you had many mmap's; luckily, that wasn't the case then, and I tended to git-gc very often - which I still do to this day!)

Anyway, just some food for thought. I have nearly deleted that branch many times. I should probably do that now! (Hmm, patch given below just FYI).

Thanks.

ATB, Ramsay Jones

-------- >8 --------
From 40442aa06901720ec55005144438c8c733025cbb Mon Sep 17 00:00:00 2001
From: Ramsay Jones <ramsay@ramsay1.demon.co.uk>
Date: Sun, 9 Sep 2012 20:50:32 +0100
Subject: [PATCH] mmap.c: log mmap() blocks to avoid double-delete bug

When compiling with the NO_MMAP build variable set, the built-in 'git_mmap()' and 'git_munmap()' compatability routines use simple memory allocation and file I/O to emulate the required behaviour. The current implementation is vunerable to the "double-delete" bug (where the pointer returned by malloc() is passed to free() two or more times), should the mapped memory block address be passed to munmap() multiple times.

In order to guard the implementation from such a calling sequence, we keep a list of mmap-block descriptors, which we then consult to determine the validity of the input pointer to munmap(). This then allows 'git_munmap()' to return -1 on error, as required, with errno set to EINVAL.

Using a list in the log of mmap-ed blocks, along with the resulting linear search, means that the performance of the code is directly proportional to the number of concurrently active memory mapped file regions. The number of such regions is not expected to be excessive.

Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
---
 compat/mmap.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/compat/mmap.c b/compat/mmap.c
index 7f662fef7b..137c6dc005 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -1,14 +1,61 @@
 #include "../git-compat-util.h"
 
+struct mmbd {  /* memory mapped block descriptor */
+	struct mmbd *next;  /* next in list */
+	void   *start;      /* pointer to memory mapped block */
+	size_t length;      /* length of memory mapped block */
+};
+
+static struct mmbd *head;  /* head of mmb descriptor list */
+
+
+static void add_desc(struct mmbd *desc, void *start, size_t length)
+{
+	desc->start = start;
+	desc->length = length;
+	desc->next = head;
+	head = desc;
+}
+
+static void free_desc(struct mmbd *desc)
+{
+	if (head == desc)
+		head = head->next;
+	else {
+		struct mmbd *d = head;
+		for (; d; d = d->next) {
+			if (d->next == desc) {
+				d->next = desc->next;
+				break;
+			}
+		}
+	}
+	free(desc);
+}
+
+static struct mmbd *find_desc(void *start)
+{
+	struct mmbd *d = head;
+	for (; d; d = d->next) {
+		if (d->start == start)
+			return d;
+	}
+	return NULL;
+}
+
 void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
 {
 	size_t n = 0;
+	struct mmbd *desc = NULL;
 
 	if (start != NULL || !(flags & MAP_PRIVATE))
 		die("Invalid usage of mmap when built with NO_MMAP");
 
 	start = xmalloc(length);
-	if (start == NULL) {
+	desc = xmalloc(sizeof(*desc));
+	if (!start || !desc) {
+		free(start);
+		free(desc);
 		errno = ENOMEM;
 		return MAP_FAILED;
 	}
@@ -23,18 +70,26 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
 
 		if (count < 0) {
 			free(start);
+			free(desc);
 			errno = EACCES;
 			return MAP_FAILED;
 		}
 
 		n += count;
 	}
+	add_desc(desc, start, length);
 
 	return start;
 }
 
 int git_munmap(void *start, size_t length)
 {
+	struct mmbd *d = find_desc(start);
+	if (!d) {
+		errno = EINVAL;
+		return -1;
+	}
+	free_desc(d);
 	free(start);
 	return 0;
 }
-- 
2.53.0
Previous: Patrick SteinhardtNext: Jeff King
Message 17 of 25 in “memory leak when cloning a repository”
  1. Jacob KellerMar 5, 2026
  2. Jeff KingMar 5, 2026
  3. 0/4 plugging some mmap() leaksJeff King, Mar 5, 2026
  4. 1/4 check_connected(): delay opening new_packJeff King, Mar 5, 2026
  5. Jacob KellerMar 5, 2026
  6. 2/4 check_connected(): fix leak of pack-index mmapJeff King, Mar 5, 2026
  7. Jacob KellerMar 5, 2026
  8. 3/4 pack-revindex: avoid double-loading .rev filesJeff King, Mar 5, 2026
  9. 4/4 Makefile: turn on NO_MMAP when building with LSanJeff King, Mar 5, 2026
  10. Jacob KellerMar 6, 2026
  11. 5/4 meson: turn on NO_MMAP when building with LSanJeff King, Mar 6, 2026
  12. Ramsay JonesMar 6, 2026
  13. Junio C HamanoMar 7, 2026
  14. 5/4 object-file: fix mmap() leak in odb_source_loose_read_object_stream()Jeff King, Mar 7, 2026
  15. Junio C HamanoMar 7, 2026
  16. Patrick SteinhardtMar 10, 2026
  17. Ramsay JonesMar 6, 2026
  18. Jeff KingMar 6, 2026
  19. Ramsay JonesMar 6, 2026
  20. Junio C HamanoMar 6, 2026
  21. Ramsay JonesMar 6, 2026
  22. Junio C HamanoMar 6, 2026
  23. Ramsay JonesMar 6, 2026
  24. Junio C HamanoMar 7, 2026
  25. Jacob KellerMar 5, 2026

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.