1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-04-27 23:45:12 +02:00
git/patch-delta.c
Jann Horn fa72f90e7a patch-delta: consistently report corruption
When applying a delta, if we see an opcode that cannot be
fulfilled (e.g., asking to write more bytes than the
destination has left), we break out of our parsing loop but
don't signal an explicit error. We rely on the sanity check
after the loop to see if we have leftover delta bytes or
didn't fill our result buffer.

This can silently ignore corruption when the delta buffer
ends with a bogus command and the destination buffer is
already full. Instead, let's jump into the error handler
directly when we see this case.

Note that the tests also cover the "bad opcode" case, which
already handles this correctly.

Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-08-30 10:30:22 -07:00

88 lines
2.2 KiB
C

/*
* patch-delta.c:
* recreate a buffer from a source and the delta produced by diff-delta.c
*
* (C) 2005 Nicolas Pitre <nico@fluxnic.net>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "git-compat-util.h"
#include "delta.h"
void *patch_delta(const void *src_buf, unsigned long src_size,
const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size)
{
const unsigned char *data, *top;
unsigned char *dst_buf, *out, cmd;
unsigned long size;
if (delta_size < DELTA_SIZE_MIN)
return NULL;
data = delta_buf;
top = (const unsigned char *) delta_buf + delta_size;
/* make sure the orig file size matches what we expect */
size = get_delta_hdr_size(&data, top);
if (size != src_size)
return NULL;
/* now the result size */
size = get_delta_hdr_size(&data, top);
dst_buf = xmallocz(size);
out = dst_buf;
while (data < top) {
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);
if (cp_size == 0) cp_size = 0x10000;
if (unsigned_add_overflows(cp_off, cp_size) ||
cp_off + cp_size > src_size ||
cp_size > size)
goto bad_length;
memcpy(out, (char *) src_buf + cp_off, cp_size);
out += cp_size;
size -= cp_size;
} else if (cmd) {
if (cmd > size || cmd > top - data)
goto bad_length;
memcpy(out, data, cmd);
out += cmd;
data += cmd;
size -= cmd;
} else {
/*
* cmd == 0 is reserved for future encoding
* extensions. In the mean time we must fail when
* encountering them (might be data corruption).
*/
error("unexpected delta opcode 0");
goto bad;
}
}
/* sanity check */
if (data != top || size != 0) {
bad_length:
error("delta replay has gone wild");
bad:
free(dst_buf);
return NULL;
}
*dst_size = out - dst_buf;
return dst_buf;
}