check patch_delta bounds more carefully
authorNicolas Pitre <nico@cam.org>
Fri, 7 Apr 2006 19:26:10 +0000 (15:26 -0400)
committerJunio C Hamano <junkio@cox.net>
Fri, 7 Apr 2006 23:31:20 +0000 (16:31 -0700)
Let's avoid going south with invalid delta data.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
delta.h
patch-delta.c
sha1_file.c

diff --git a/delta.h b/delta.h
index a15350d..9464f3e 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -16,7 +16,8 @@ extern void *patch_delta(void *src_buf, unsigned long src_size,
  * This must be called twice on the delta data buffer, first to get the
  * expected reference buffer size, and again to get the result buffer size.
  */
-static inline unsigned long get_delta_hdr_size(const unsigned char **datap)
+static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
+                                              const unsigned char *top)
 {
        const unsigned char *data = *datap;
        unsigned char cmd;
@@ -26,7 +27,7 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap)
                cmd = *data++;
                size |= (cmd & ~0x80) << i;
                i += 7;
-       } while (cmd & 0x80);
+       } while (cmd & 0x80 && data < top);
        *datap = data;
        return size;
 }
index c0e1311..d95f0d9 100644 (file)
@@ -28,12 +28,12 @@ void *patch_delta(void *src_buf, unsigned long src_size,
        top = delta_buf + delta_size;
 
        /* make sure the orig file size matches what we expect */
-       size = get_delta_hdr_size(&data);
+       size = get_delta_hdr_size(&data, top);
        if (size != src_size)
                return NULL;
 
        /* now the result size */
-       size = get_delta_hdr_size(&data);
+       size = get_delta_hdr_size(&data, top);
        dst_buf = malloc(size + 1);
        if (!dst_buf)
                return NULL;
@@ -52,21 +52,37 @@ void *patch_delta(void *src_buf, unsigned long src_size,
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
                        if (cmd & 0x40) cp_size |= (*data++ << 16);
                        if (cp_size == 0) cp_size = 0x10000;
+                       if (cp_off + cp_size < cp_size ||
+                           cp_off + cp_size > src_size ||
+                           cp_size > size)
+                               goto bad;
                        memcpy(out, src_buf + cp_off, cp_size);
                        out += cp_size;
-               } else {
+                       size -= cp_size;
+               } else if (cmd) {
+                       if (cmd > size)
+                               goto bad;
                        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).
+                        */
+                       goto bad;
                }
        }
 
        /* sanity check */
-       if (data != top || out - dst_buf != size) {
+       if (data != top || size != 0) {
+               bad:
                free(dst_buf);
                return NULL;
        }
 
-       *dst_size = size;
+       *dst_size = out - dst_buf;
        return dst_buf;
 }
index aa09b46..d8ef565 100644 (file)
@@ -804,10 +804,12 @@ static int packed_delta_info(unsigned char *base_sha1,
                 * the result size.
                 */
                data = delta_head;
-               get_delta_hdr_size(&data); /* ignore base size */
+
+               /* ignore base size */
+               get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 
                /* Read the result size */
-               result_size = get_delta_hdr_size(&data);
+               result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
                *sizep = result_size;
        }
        return 0;