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

[PATCH 1/3] ewah_read_mmap: bounds-check mmap reads

From
Jeff King <peff@peff.net>
Date
Jun 15, 2018, 03:31 UTC
Message-ID
<20180615033112.GA20390@sigill.intra.peff.net>
In-Reply-To
<20180615032850.GA23241@sigill.intra.peff.net>

The on-disk ewah format tells us how big the ewah data is, and we blindly read that much from the buffer without considering whether the mmap'd data is long enough, which can lead to out-of-bound reads.

Let's make sure we have data available before reading it, both for the ewah header/footer as well as for the bit data itself. In particular:

  - keep our ptr/len pair in sync as we move through the
    buffer, and check it before each read
  - check the size for integer overflow (this should be
    impossible on 64-bit, as the size is given as a 32-bit
    count of 8-byte words, but is possible on a 32-bit
    system)
  - return the number of bytes read as an ssize_t instead of
    an int, again to prevent integer overflow
  - compute the return value using a pointer difference;
    this should yield the same result as the existing code,
    but makes it more obvious that we got our computations
    right

The included test is far from comprehensive, as it just picks a static point at which to truncate the generated bitmap. But in practice this will hit in the middle of an ewah and make sure we're at least exercising this code.

Reported-by: Luat Nguyen <root@l4w.io>
Signed-off-by: Jeff King <peff@peff.net>
---
 ewah/ewah_io.c          | 25 +++++++++++++++++++++----
 ewah/ewok.h             |  2 +-
 t/t5310-pack-bitmaps.sh | 13 +++++++++++++
 3 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/ewah/ewah_io.c b/ewah/ewah_io.c
index bed1994551..33c08c40f8 100644
--- a/ewah/ewah_io.c
+++ b/ewah/ewah_io.c
@@ -122,16 +122,23 @@ int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *sb)
 	return ewah_serialize_to(self, write_strbuf, sb);
 }
 
-int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
+ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 {
 	const uint8_t *ptr = map;
+	size_t data_len;
 	size_t i;
 
+	if (len < sizeof(uint32_t))
+		return error("corrupt ewah bitmap: eof before bit size");
 	self->bit_size = get_be32(ptr);
 	ptr += sizeof(uint32_t);
+	len -= sizeof(uint32_t);
 
+	if (len < sizeof(uint32_t))
+		return error("corrupt ewah bitmap: eof before length");
 	self->buffer_size = self->alloc_size = get_be32(ptr);
 	ptr += sizeof(uint32_t);
+	len -= sizeof(uint32_t);
 
 	REALLOC_ARRAY(self->buffer, self->alloc_size);
 
@@ -141,15 +148,25 @@ int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 	 * the endianness conversion in a separate pass to ensure
 	 * we're loading 8-byte aligned words.
 	 */
-	memcpy(self->buffer, ptr, self->buffer_size * sizeof(eword_t));
-	ptr += self->buffer_size * sizeof(eword_t);
+	data_len = st_mult(self->buffer_size, sizeof(eword_t));
+	if (len < data_len)
+		return error("corrupt ewah bitmap: eof in data "
+			     "(%"PRIuMAX" bytes short)",
+			     (uintmax_t)(data_len - len));
+	memcpy(self->buffer, ptr, data_len);
+	ptr += data_len;
+	len -= data_len;
 
 	for (i = 0; i < self->buffer_size; ++i)
 		self->buffer[i] = ntohll(self->buffer[i]);
 
+	if (len < sizeof(uint32_t))
+		return error("corrupt ewah bitmap: eof before rlw");
 	self->rlw = self->buffer + get_be32(ptr);
+	ptr += sizeof(uint32_t);
+	len -= sizeof(uint32_t);
 
-	return (3 * 4) + (self->buffer_size * 8);
+	return ptr - (const uint8_t *)map;
 }
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd)
diff --git a/ewah/ewok.h b/ewah/ewok.h
index dc43d05b64..357fd93c84 100644
--- a/ewah/ewok.h
+++ b/ewah/ewok.h
@@ -91,7 +91,7 @@ int ewah_serialize_native(struct ewah_bitmap *self, int fd);
 int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *);
 
 int ewah_deserialize(struct ewah_bitmap *self, int fd);
-int ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
+ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
 
 uint32_t ewah_checksum(struct ewah_bitmap *self);
 
diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh
index 423c0a475f..237ee6e5fc 100755
--- a/t/t5310-pack-bitmaps.sh
+++ b/t/t5310-pack-bitmaps.sh
@@ -331,4 +331,17 @@ test_expect_success 'pack reuse respects --incremental' '
 	git show-index <empty.idx >actual &&
 	test_cmp expect actual
 '
+
+test_expect_success 'truncated bitmap fails gracefully' '
+	git repack -ad &&
+	git rev-list --use-bitmap-index --count --all >expect &&
+	bitmap=$(ls .git/objects/pack/*.bitmap) &&
+	test_when_finished "rm -f $bitmap" &&
+	head -c 512 <$bitmap >$bitmap.tmp &&
+	mv $bitmap.tmp $bitmap &&
+	git rev-list --use-bitmap-index --count --all >actual 2>stderr &&
+	test_cmp expect actual &&
+	test_i18ngrep corrupt stderr
+'
+
 test_done
-- 
2.18.0.rc2.534.g53d976aeb8
Previous: Jeff KingNext: SZEDER Gábor
Message 3 of 58 in “security: potential out-of-bound read at ewah_io.c |ewah_read_mmap|”
  1. Luat NguyenJun 14, 2018
  2. Jeff KingJun 15, 2018
  3. 1/3 ewah_read_mmap: bounds-check mmap readsJeff King, Jun 15, 2018
  4. SZEDER GáborJun 15, 2018
  5. Junio C HamanoJun 15, 2018
  6. SZEDER GáborJun 15, 2018
  7. Jeff KingJun 15, 2018
  8. Junio C HamanoJun 15, 2018
  9. Junio C HamanoJun 15, 2018
  10. Jeff KingJun 15, 2018
  11. Junio C HamanoJun 15, 2018
  12. SZEDER GáborJun 16, 2018
  13. Jeff KingJun 16, 2018
  14. 2/3 ewah: drop ewah_deserialize functionJeff King, Jun 15, 2018
  15. 3/3 ewah: drop ewah_serialize_native functionJeff King, Jun 15, 2018
  16. Ramsay JonesJun 15, 2018
  17. Ramsay JonesJun 15, 2018
  18. 0/8 Delete unused methods in EWAH bitmapDerrick Stolee, Jun 15, 2018
  19. 2/8 ewah/bitmap.c: delete unused 'bitmap_each_bit()'Derrick Stolee, Jun 15, 2018
  20. Ramsay JonesJun 15, 2018
  21. 4/8 ewah_bitmap: delete unused 'ewah_and_not()'Derrick Stolee, Jun 15, 2018
  22. 6/8 ewah_bitmap: delete unused 'ewah_or()'Derrick Stolee, Jun 15, 2018
  23. 8/8 ewah_io: delete unused 'ewah_serialize_native()'Derrick Stolee, Jun 15, 2018
  24. Ramsay JonesJun 15, 2018
  25. Derrick StoleeJun 15, 2018
  26. 1/8 ewah/bitmap.c: delete unused 'bitmap_clear()'Derrick Stolee, Jun 15, 2018
  27. Ramsay JonesJun 15, 2018
  28. Derrick StoleeJun 15, 2018
  29. 7/8 ewah_io: delete unused 'ewah_serialize()'Derrick Stolee, Jun 15, 2018
  30. 3/8 ewah_bitmap: delete unused 'ewah_and()'Derrick Stolee, Jun 15, 2018
  31. 5/8 ewah_bitmap: delete unused 'ewah_not()'Derrick Stolee, Jun 15, 2018
  32. Derrick StoleeJun 15, 2018
  33. 0/7 Delete unused methods in EWAH bitmapDerrick Stolee, Jun 15, 2018
  34. 1/7 ewah/bitmap.c: delete unused 'bitmap_clear()'Derrick Stolee, Jun 15, 2018
  35. 2/7 ewah/bitmap.c: delete unused 'bitmap_each_bit()'Derrick Stolee, Jun 15, 2018
  36. 3/7 ewah_bitmap: delete unused 'ewah_and()'Derrick Stolee, Jun 15, 2018
  37. 4/7 ewah_bitmap: delete unused 'ewah_and_not()'Derrick Stolee, Jun 15, 2018
  38. 7/7 ewah_io: delete unused 'ewah_serialize()'Derrick Stolee, Jun 15, 2018
  39. 5/7 ewah_bitmap: delete unused 'ewah_not()'Derrick Stolee, Jun 15, 2018
  40. 6/7 ewah_bitmap: delete unused 'ewah_or()'Derrick Stolee, Jun 15, 2018
  41. Junio C HamanoJun 15, 2018
  42. Derrick StoleeJun 15, 2018
  43. Junio C HamanoJun 15, 2018
  44. Jeff KingJun 15, 2018
  45. Derrick StoleeJun 15, 2018
  46. Jeff KingJun 15, 2018
  47. Junio C HamanoJun 15, 2018
  48. Jeff KingJun 15, 2018
  49. 4/3 ewah: adjust callers of ewah_read_mmap()Jeff King, Jun 15, 2018
  50. Derrick StoleeJun 15, 2018
  51. Junio C HamanoJun 15, 2018
  52. Jeff KingJun 15, 2018
  53. Derrick StoleeJun 15, 2018
  54. Jeff KingJun 15, 2018
  55. Junio C HamanoJun 15, 2018
  56. Junio C HamanoJun 15, 2018
  57. Dyer, EdwinJun 19, 2018
  58. Jeff KingJun 19, 2018

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.