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

[PATCH 5/5] patch-delta: handle truncated copy parameters

From
Jeff King <peff@peff.net>
Date
Aug 30, 2018, 07:12 UTC
Message-ID
<20180830071252.GE15420@sigill.intra.peff.net>
In-Reply-To
<20180830070548.GA15081@sigill.intra.peff.net>

When we see a delta command instructing us to copy bytes from the base, we have to read the offset and size from the delta stream. We do this without checking whether we're at the end of the stream, meaning we may read past the end of the buffer.

In practice this isn't exploitable in any interesting way because:

  1. Deltas are always in packfiles, so we have at least a
     20-byte trailer that we'll end up reading.
  2. The worst case is that we try to perform a nonsense
     copy from the base object into the result, based on
     whatever was in the pack stream next. In most cases
     this will simply fail due to our bounds-checks against
     the base or the result.
     But even if you carefully constructed a pack stream for
     which it succeeds, it wouldn't perform any delta
     operation that you couldn't have simply included in a
     non-broken form.

But obviously it's poor form to read past the end of the buffer we've been given. Unfortunately there's no easy way to do a single length check, since the number of bytes we need depends on the number of bits set in the initial command byte. So we'll just check each byte as we parse. We can hide the complexity in a macro; it's ugly, but not as ugly as writing out each individual conditional.

Signed-off-by: Jeff King <peff@peff.net>
---
 patch-delta.c                         | 21 ++++++++++++++-------
 t/t5303-pack-corruption-resilience.sh |  2 +-
 2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/patch-delta.c b/patch-delta.c
index 283fb4b759..b5c8594db6 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -40,13 +40,20 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
 		cmd = *data++;
 		if (cmd & 0x80) {
 			unsigned long cp_off = 0, cp_size = 0;
-			if (cmd & 0x01) cp_off = *data++;
-			if (cmd & 0x02) cp_off |= (*data++ << 8);
-			if (cmd & 0x04) cp_off |= (*data++ << 16);
-			if (cmd & 0x08) cp_off |= ((unsigned) *data++ << 24);
-			if (cmd & 0x10) cp_size = *data++;
-			if (cmd & 0x20) cp_size |= (*data++ << 8);
-			if (cmd & 0x40) cp_size |= (*data++ << 16);
+#define PARSE_CP_PARAM(bit, var, shift) do { \
+			if (cmd & (bit)) { \
+				if (data >= top) \
+					goto bad_length; \
+				var |= ((unsigned) *data++ << (shift)); \
+			} } while (0)
+			PARSE_CP_PARAM(0x01, cp_off, 0);
+			PARSE_CP_PARAM(0x02, cp_off, 8);
+			PARSE_CP_PARAM(0x04, cp_off, 16);
+			PARSE_CP_PARAM(0x08, cp_off, 24);
+			PARSE_CP_PARAM(0x10, cp_size, 0);
+			PARSE_CP_PARAM(0x20, cp_size, 8);
+			PARSE_CP_PARAM(0x40, cp_size, 16);
+#undef PARSE_CP_PARAM
 			if (cp_size == 0) cp_size = 0x10000;
 			if (unsigned_add_overflows(cp_off, cp_size) ||
 			    cp_off + cp_size > src_size ||
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
index 41dc947d3f..b68bbeedcc 100755
--- a/t/t5303-pack-corruption-resilience.sh
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -364,7 +364,7 @@ test_expect_success \
 #
 # Note that the literal byte is necessary to get past the uninteresting minimum
 # delta size check.
-test_expect_failure \
+test_expect_success \
     'apply delta with truncated copy parameters' \
     'printf "\5\5\1X\221" > truncated_copy_delta &&
      echo base >base &&
-- 
2.19.0.rc1.539.g3876d0831e
Previous: Jeff KingNext: Jann Horn
Message 29 of 31 in “patch-delta: fix oob read”
  1. 1/3 patch-delta: fix oob readJann Horn, Aug 29, 2018
  2. 2/3 t/helper/test-delta: segfault on OOB accessJann Horn, Aug 29, 2018
  3. Jeff KingAug 29, 2018
  4. Jann HornAug 29, 2018
  5. Jeff KingAug 29, 2018
  6. Jeff KingAug 29, 2018
  7. 3/3 t5303: add tests for corrupted deltasJann Horn, Aug 29, 2018
  8. Jeff KingAug 29, 2018
  9. Jeff KingAug 29, 2018
  10. Jeff KingAug 29, 2018
  11. Jeff KingAug 29, 2018
  12. 0/5 handle corruption in patch-deltaJeff King, Aug 30, 2018
  13. 1/5 test-delta: read input into a heap bufferJeff King, Aug 30, 2018
  14. 2/5 t5303: test some corrupt deltasJeff King, Aug 30, 2018
  15. Junio C HamanoAug 30, 2018
  16. Jeff KingAug 30, 2018
  17. Jeff KingAug 30, 2018
  18. Junio C HamanoAug 30, 2018
  19. Jeff KingAug 30, 2018
  20. Johannes SchindelinAug 31, 2018
  21. Junio C HamanoAug 31, 2018
  22. Jeff KingAug 31, 2018
  23. Junio C HamanoAug 31, 2018
  24. Johannes SchindelinAug 31, 2018
  25. Jeff KingAug 31, 2018
  26. Junio C HamanoAug 31, 2018
  27. 3/5 patch-delta: fix oob readJeff King, Aug 30, 2018
  28. 4/5 patch-delta: consistently report corruptionJeff King, Aug 30, 2018
  29. 5/5 patch-delta: handle truncated copy parametersJeff King, Aug 30, 2018
  30. Jann HornAug 30, 2018
  31. Nicolas PitreAug 30, 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.