git: 9front

Download patch

ref: f7882376011a2b22b1478f0c2163092888b70df8
parent: 62abdae9c3475f4fe5971c3fc172e7ad374c42a3
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Mar 28 03:36:50 EDT 2026

git: double check for overflows and sign extension in pack parsing

--- a/sys/src/cmd/git/pack.c
+++ b/sys/src/cmd/git/pack.c
@@ -401,8 +401,8 @@
 	return d.len;
 }
 
-static vlong
-readvint(char *p, char **pp)
+static int
+readvint(char *p, char *ep, char **pp)
 {
 	int s, c;
 	vlong n;
@@ -410,12 +410,13 @@
 	s = 0;
 	n = 0;
 	do {
+		if(p == ep)
+			return -1;
 		c = *p++;
-		n |= (c & 0x7f) << s;
+		n |= (vlong)(c & 0x7f) << s;
 		s += 7;
 	} while (c & 0x80 && s < 63);
 	*pp = p;
-
 	return n;
 }
 
@@ -423,20 +424,23 @@
 applydelta(Object *dst, Object *base, char *d, int nd)
 {
 	char *r, *b, *ed, *er;
-	int n, nr, c;
-	vlong o, l;
+	vlong o, l,  n, nr, c;
 
 	ed = d + nd;
 	b = base->data;
-	n = readvint(d, &d);
-	if(n != base->size){
+	n = readvint(d, ed, &d);
+	if(n == -1 || n >= 1LL << 31 || n != base->size){
 		werrstr("mismatched source size");
 		return -1;
 	}
 
-	nr = readvint(d, &d);
+	nr = readvint(d, ed, &d);
+	if(nr == -1 || n >= 1LL << 31){
+		werrstr("invalid pack: %r");
+		return -1;
+	}
 	r = emalloc(nr + 64);
-	n = snprint(r, 64, "%T %d", base->type, nr) + 1;
+	n = snprint(r, 64, "%T %lld", base->type, nr) + 1;
 	dst->all = r;
 	dst->type = base->type;
 	dst->data = r + n;
@@ -451,18 +455,18 @@
 			o = 0;
 			l = 0;
 			/* Offset in base */
-			if(d != ed && (c & 0x01)) o |= (*d++ <<  0) & 0x000000ff;
-			if(d != ed && (c & 0x02)) o |= (*d++ <<  8) & 0x0000ff00;
-			if(d != ed && (c & 0x04)) o |= (*d++ << 16) & 0x00ff0000;
-			if(d != ed && (c & 0x08)) o |= (*d++ << 24) & 0xff000000;
+			if(d != ed && (c & 0x01)) o |= (*d++ & 0xff) <<  0;
+			if(d != ed && (c & 0x02)) o |= (*d++ & 0xff) <<  8;
+			if(d != ed && (c & 0x04)) o |= (*d++ & 0xff) << 16;
+			if(d != ed && (c & 0x08)) o |= (*d++ & 0xff) << 24;
 
 			/* Length to copy */
-			if(d != ed && (c & 0x10)) l |= (*d++ <<  0) & 0x0000ff;
-			if(d != ed && (c & 0x20)) l |= (*d++ <<  8) & 0x00ff00;
-			if(d != ed && (c & 0x40)) l |= (*d++ << 16) & 0xff0000;
+			if(d != ed && (c & 0x10)) l |= (*d++ & 0xff) <<  0;
+			if(d != ed && (c & 0x20)) l |= (*d++ & 0xff) <<  8;
+			if(d != ed && (c & 0x40)) l |= (*d++ & 0xff) << 16;
 			if(l == 0) l = 0x10000;
 
-			if(o + l > base->size){
+			if(o < 0 || l < 0 || o + l > base->size){
 				werrstr("garbled delta: out of bounds copy");
 				return -1;
 			}
@@ -632,7 +636,7 @@
 		{"tag", GTag},
 		{nil},
 	};
-	char *d, *s, *e;
+	char *d, *s, *e, *ed;
 	vlong sz, n;
 	int l;
 
@@ -641,6 +645,7 @@
 		return -1;
 
 	s = d;
+	ed = d + n;
 	o->type = GNone;
 	for(p = types; p->tag; p++){
 		l = strlen(p->tag);
@@ -647,7 +652,7 @@
 		if(strncmp(s, p->tag, l) == 0){
 			s += l;
 			o->type = p->type;
-			while(!isspace(*s))
+			while(s != ed && !isspace(*s))
 				s++;
 			break;
 		}
@@ -975,7 +980,7 @@
 			t->mode |= DMDIR;
 		t->name = p;
 		p = memchr(p, 0, ep - p);
-		if(*p++ != 0 ||  ep - p < sizeof(t->h.h))
+		if(p == nil || *p++ != 0 ||  ep - p < sizeof(t->h.h))
 			sysfatal("malformed tree %H, remaining %d (%s)", o->hash, (int)(ep - p), p);
 		memcpy(t->h.h, p, sizeof(t->h.h));
 		p += sizeof(t->h.h);
--