Skip to content

Commit 7202a6f

Browse files
peffgitster
authored andcommitted
encode_in_pack_object_header: respect output buffer length
The encode_in_pack_object_header() writes a variable-length header to an output buffer, but it doesn't actually know long the buffer is. At first glance, this looks like it might be possible to overflow. In practice, this is probably impossible. The smallest buffer we use is 10 bytes, which would hold the header for an object up to 2^67 bytes. Obviously we're not likely to see such an object, but we might worry that an object could lie about its size (causing us to overflow before we realize it does not actually have that many bytes). But the argument is passed as a uintmax_t. Even on systems that have __int128 available, uintmax_t is typically restricted to 64-bit by the ABI. So it's unlikely that a system exists where this could be exploited. Still, it's easy enough to use a normal out/len pair and make sure we don't write too far. That protects the hypothetical 128-bit system, makes it harder for callers to accidentally specify a too-small buffer, and makes the resulting code easier to audit. Note that the one caller in fast-import tried to catch such a case, but did so _after_ the call (at which point we'd have already overflowed!). This check can now go away. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9871824 commit 7202a6f

File tree

5 files changed

+16
-10
lines changed

5 files changed

+16
-10
lines changed

builtin/pack-objects.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
286286
* The object header is a byte of 'type' followed by zero or
287287
* more bytes of length.
288288
*/
289-
hdrlen = encode_in_pack_object_header(type, size, header);
289+
hdrlen = encode_in_pack_object_header(header, sizeof(header),
290+
type, size);
290291

291292
if (type == OBJ_OFS_DELTA) {
292293
/*
@@ -358,7 +359,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
358359
if (entry->delta)
359360
type = (allow_ofs_delta && entry->delta->idx.offset) ?
360361
OBJ_OFS_DELTA : OBJ_REF_DELTA;
361-
hdrlen = encode_in_pack_object_header(type, entry->size, header);
362+
hdrlen = encode_in_pack_object_header(header, sizeof(header),
363+
type, entry->size);
362364

363365
offset = entry->in_pack_offset;
364366
revidx = find_pack_revindex(p, offset);

bulk-checkin.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ static int stream_to_pack(struct bulk_checkin_state *state,
105105

106106
git_deflate_init(&s, pack_compression_level);
107107

108-
hdrlen = encode_in_pack_object_header(type, size, obuf);
108+
hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size);
109109
s.next_out = obuf + hdrlen;
110110
s.avail_out = sizeof(obuf) - hdrlen;
111111

fast-import.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,8 @@ static int store_object(
11731173
delta_count_by_type[type]++;
11741174
e->depth = last->depth + 1;
11751175

1176-
hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
1176+
hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
1177+
OBJ_OFS_DELTA, deltalen);
11771178
sha1write(pack_file, hdr, hdrlen);
11781179
pack_size += hdrlen;
11791180

@@ -1184,7 +1185,8 @@ static int store_object(
11841185
pack_size += sizeof(hdr) - pos;
11851186
} else {
11861187
e->depth = 0;
1187-
hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
1188+
hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
1189+
type, dat->len);
11881190
sha1write(pack_file, hdr, hdrlen);
11891191
pack_size += hdrlen;
11901192
}
@@ -1246,9 +1248,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
12461248

12471249
git_deflate_init(&s, pack_compression_level);
12481250

1249-
hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
1250-
if (out_sz <= hdrlen)
1251-
die("impossibly large object header");
1251+
hdrlen = encode_in_pack_object_header(out_buf, out_sz, OBJ_BLOB, len);
12521252

12531253
s.next_out = out_buf + hdrlen;
12541254
s.avail_out = out_sz - hdrlen;

pack-write.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ char *index_pack_lockfile(int ip_out)
304304
* - each byte afterwards: low seven bits are size continuation,
305305
* with the high bit being "size continues"
306306
*/
307-
int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned char *hdr)
307+
int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
308+
enum object_type type, uintmax_t size)
308309
{
309310
int n = 1;
310311
unsigned char c;
@@ -315,6 +316,8 @@ int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned
315316
c = (type << 4) | (size & 15);
316317
size >>= 4;
317318
while (size) {
319+
if (n == hdr_len)
320+
die("object size is too enormous to format");
318321
*hdr++ = c | 0x80;
319322
c = size & 0x7f;
320323
size >>= 7;

pack.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uin
8484
extern off_t write_pack_header(struct sha1file *f, uint32_t);
8585
extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
8686
extern char *index_pack_lockfile(int fd);
87-
extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
87+
extern int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
88+
enum object_type, uintmax_t);
8889

8990
#define PH_ERROR_EOF (-1)
9091
#define PH_ERROR_PACK_SIGNATURE (-2)

0 commit comments

Comments
 (0)