Skip to content

Commit

Permalink
patch-delta: handle truncated copy parameters
Browse files Browse the repository at this point in the history
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>
Reviewed-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
peff authored and gitster committed Aug 30, 2018
1 parent fa72f90 commit 9514b0b
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 8 deletions.
21 changes: 14 additions & 7 deletions patch-delta.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 ||
Expand Down
2 changes: 1 addition & 1 deletion t/t5303-pack-corruption-resilience.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down

0 comments on commit 9514b0b

Please sign in to comment.