]> git.ipfire.org Git - thirdparty/git.git/commitdiff
delta, packfile: use size_t for delta header sizes
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Fri, 8 May 2026 08:16:42 +0000 (08:16 +0000)
committerJunio C Hamano <gitster@pobox.com>
Sat, 9 May 2026 02:25:32 +0000 (11:25 +0900)
The delta header decoding functions return unsigned long, which
truncates on Windows for objects larger than 4GB. Introduce size_t
variants get_delta_hdr_size_sz() and get_size_from_delta_sz() that
preserve the full 64-bit size, and use them in packed_object_info()
where the size is needed for streaming decisions.

This was originally authored by LordKiRon <https://github.com/LordKiRon>,
who preferred not to reveal their real name and therefore agreed that I
take over authorship.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
delta.h
packfile.c

diff --git a/delta.h b/delta.h
index 8a56ec07992c75b021fca970d37e22f5e8a8bb48..fad68cfc45f6f4002b089d74a65c09798580d3e7 100644 (file)
--- a/delta.h
+++ b/delta.h
@@ -86,8 +86,11 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
  * This must be called twice on the delta data buffer, first to get the
  * expected source buffer size, and again to get the target buffer size.
  */
-static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
-                                              const unsigned char *top)
+/*
+ * Size_t variant that doesn't truncate - use for >4GB objects on Windows.
+ */
+static inline size_t get_delta_hdr_size_sz(const unsigned char **datap,
+                                          const unsigned char *top)
 {
        const unsigned char *data = *datap;
        size_t cmd, size = 0;
@@ -98,6 +101,13 @@ static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
                i += 7;
        } while (cmd & 0x80 && data < top);
        *datap = data;
+       return size;
+}
+
+static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
+                                              const unsigned char *top)
+{
+       size_t size = get_delta_hdr_size_sz(datap, top);
        return cast_size_t_to_ulong(size);
 }
 
index fdae91dd110682a808620353866e6c45c89bd1be..4208f53046b6309f6e1b27127768f9b044290019 100644 (file)
@@ -1161,9 +1161,12 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        return used;
 }
 
-unsigned long get_size_from_delta(struct packed_git *p,
-                                 struct pack_window **w_curs,
-                                 off_t curpos)
+/*
+ * Size_t variant for >4GB delta results on Windows.
+ */
+static size_t get_size_from_delta_sz(struct packed_git *p,
+                                    struct pack_window **w_curs,
+                                    off_t curpos)
 {
        const unsigned char *data;
        unsigned char delta_head[20], *in;
@@ -1210,10 +1213,18 @@ unsigned long get_size_from_delta(struct packed_git *p,
        data = delta_head;
 
        /* ignore base size */
-       get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+       get_delta_hdr_size_sz(&data, delta_head+sizeof(delta_head));
 
        /* Read the result size */
-       return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
+       return get_delta_hdr_size_sz(&data, delta_head+sizeof(delta_head));
+}
+
+unsigned long get_size_from_delta(struct packed_git *p,
+                                 struct pack_window **w_curs,
+                                 off_t curpos)
+{
+       size_t size = get_size_from_delta_sz(p, w_curs, curpos);
+       return cast_size_t_to_ulong(size);
 }
 
 int unpack_object_header(struct packed_git *p,
@@ -1618,14 +1629,18 @@ static int packed_object_info_with_index_pos(struct packed_git *p, off_t obj_off
                                ret = -1;
                                goto out;
                        }
-                       *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
-                       if (*oi->sizep == 0) {
+                       /*
+                        * Use size_t variant to avoid die() on >4GB deltas.
+                        * oi->sizep is unsigned long, so truncation may occur,
+                        * but streaming code uses its own size_t tracking.
+                        */
+                       size = get_size_from_delta_sz(p, &w_curs, tmp_pos);
+                       if (size == 0) {
                                ret = -1;
                                goto out;
                        }
-               } else {
-                       *oi->sizep = size;
                }
+               *oi->sizep = (unsigned long)size;
        }
 
        if (oi->disk_sizep || (oi->mtimep && p->is_cruft)) {