mirror of
https://github.com/git/git
synced 2024-10-01 06:05:20 +00:00
fa72f90e7a
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>
88 lines
2.2 KiB
C
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;
|
|
}
|