git: 9front

Download patch

ref: f7bf0ab313d2c04d29262101cc251cd80a33558b
parent: 6e30d23ceb6a4f999a9c92784a95d27627e9df10
author: Ori Bernstein <ori@eigenstate.org>
date: Sun Jan 11 19:50:34 EST 2026

jpg: add range checks where needed

this fixes a number of crashes coming from AFL, mostly around
missing verification for ranges in jpeg headers, duplicated
jpeg structures, and similar bugs.

--- a/sys/src/cmd/jpg/readjpg.c
+++ b/sys/src/cmd/jpg/readjpg.c
@@ -24,11 +24,11 @@
 	APPn	=0xE0,	/* Reserved for application segments */
 	JPGn	=0xF0,	/* Reserved for JPEG extensions */
 	COM	=0xFE,	/* Comment */
-
-	CLAMPOFF	= 300,
-	NCLAMP		= CLAMPOFF+700
 };
 
+/* clamp x to 0-255 */
+#define CLAMP(x) ((x) < 0 ? 0 : ((x) > 255 ? 255 : (x)))
+
 typedef struct Framecomp Framecomp;
 typedef struct Header Header;
 typedef struct Huffman Huffman;
@@ -96,8 +96,6 @@
 	int		Vmax;
 };
 
-static	uchar	clamp[NCLAMP];
-
 static	Rawimage	*readslave(Header*, int);
 static	int			readsegment(Header*, int*);
 static	void			quanttables(Header*, uchar*, int);
@@ -134,24 +132,7 @@
 	53, 60, 61, 54, 47, 55, 62, 63 /* 56-63 */
 };
 
-static
-void
-jpginit(void)
-{
-	int k;
-	static int inited;
 
-	if(inited)
-		return;
-	inited = 1;
-	for(k=0; k<CLAMPOFF; k++)
-		clamp[k] = 0;
-	for(; k<CLAMPOFF+256; k++)
-		clamp[k] = k-CLAMPOFF;
-	for(; k<NCLAMP; k++)
-		clamp[k] = 255;
-}
-
 static
 void*
 jpgmalloc(Header *h, int n, int clear)
@@ -245,7 +226,6 @@
 		werrstr("ReadJPG: unknown color space");
 		return nil;
 	}
-	jpginit();
 	h = malloc(sizeof(Header));
 	array = malloc(2*sizeof(Rawimage*));
 	if(h==nil || array==nil){
@@ -320,9 +300,15 @@
 
 		case SOF:
 		case SOF2:
+			if(header->mode != 0)
+				jpgerror(header, "ReadJPG: duplicate SOF header");
 			header->Y = int2(b, 1);
 			header->X = int2(b, 3);
 			header->Nf = b[5];
+			if(header->X == 0 || header->Y == 0)
+				jpgerror(header, "ReadJPG: image must have 1 or 3 components");
+			if(header->Nf != 1 && header->Nf != 3)
+				jpgerror(header, "ReadJPG: image must have 1 or 3 components");
 			for(i=0; i<header->Nf; i++){
 				header->comp[i].C = b[6+3*i+0];
 				nibbles(b[6+3*i+1], &H, &V);
@@ -334,6 +320,8 @@
 				header->comp[i].H = H;
 				header->comp[i].V = V;
 				header->comp[i].Tq = b[6+3*i+2];
+				if(header->comp[i].Tq > 3)
+					jpgerror(header, "ReadJPG: invalid quantization table index");
 			}
 			header->mode = m;
 			header->sf = b;
@@ -511,8 +499,11 @@
 
 	/* initialize HUFFVAL */
 	t->val = jpgmalloc(h, nsize*sizeof(int), 1);
-	for(i=0; i<nsize; i++)
+	for(i=0; i<nsize; i++){
 		t->val[i] = b[17+i];
+		if(Tc == 0 && t->val[i] > 15 || (t->val[i] & 0xF) > 14)
+			jpgerror(h, "ReadJPG: corrupt Huffman table");
+	}
 
 	/* flow chart C-3 */
 	t->code = jpgmalloc(h, (nsize+1)*sizeof(int), 1);
@@ -676,6 +667,8 @@
 		/* so if both have 3 we know scan is Y Cb Cr and there's no need to */
 		/* reorder */
 		nibbles(ss[2+2*comp], &Td[comp], &Ta[comp]);
+		if(Td[comp] > 3 || Ta[comp] > 3)
+			jpgerror(h, "ReadJPG: invalid Huffman table index");
 		H[comp] = h->comp[comp].H;
 		V[comp] = h->comp[comp].V;
 		nblock = H[comp]*V[comp];
@@ -849,7 +842,7 @@
 	ss = h->ss;
 	Ns = ss[0];
 	Nf = h->Nf;
-	if(Ns!=3 && Ns!=1)
+	if(Ns!=3 && Ns!=1 || Nf != 3 && Nf != 1)
 		jpgerror(h, "ReadJPG: image must have 1 or 3 components");
 
 	image = jpgmalloc(h, sizeof(Rawimage), 1);
@@ -881,6 +874,8 @@
 		if(h->comp[comp].V > h->Vmax)
 			h->Vmax = h->comp[comp].V;
 	}
+	if(h->Hmax == 0 || h->Vmax == 0)
+		jpgerror(h, "ReadJPG: invalid sampling factors");
 	h->nacross = ((h->X+(8*h->Hmax-1))/(8*h->Hmax));
 	h->ndown = ((h->Y+(8*h->Vmax-1))/(8*h->Vmax));
 	nmcu = h->nacross*h->ndown;
@@ -916,6 +911,8 @@
 
 	for(i=0; i<Ns; i++){
 		nibbles(ss[2+2*i], &Td[i], &z);	/* z is ignored */
+		if(Td[i] > 3)
+			jpgerror(h, "ReadJPG: invalid Huffman table index");
 		DC[i] = 0;
 		for(j=0; j<h->Nf; j++)
 			if(h->comp[j].C == ss[1+2*i]){
@@ -944,6 +941,8 @@
 		for(y=0; y<nver; y++){
 			for(x=0; x<nhor; x++){
 				bn = (x/H + h->nacross*(y/V))*H*V + H*(y%V) + x%H;
+				if(bn < 0 || bn >= h->naccoeff[comp])
+					jpgerror(h, "ReadJPG: block number out of range");
 				if(Ah == 0){
 					t = decode(h, dcht);
 					diff = receive(h, t);
@@ -1011,6 +1010,8 @@
 		jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan");
 	Ss = ss[1+2];
 	Se = ss[2+2];
+	if(Ss >= 64 || Se >= 64)
+		jpgerror(h, "ReadJPG: invalid coefficient range in progressive AC scan");
 	H = h->comp[comp].H;
 	V = h->comp[comp].V;
 
@@ -1026,6 +1027,8 @@
 	h->sr = 0;
 	h->peek = -1;
 	nibbles(ss[1+1], &z, &Ta);	/* z is thrown away */
+	if(Ta > 3)
+		jpgerror(h, "ReadJPG: invalid Huffman table index");
 
 	ri = h->ri;
 
@@ -1045,6 +1048,8 @@
 			/* arrange blockno to be in same sequence as original scan calculation. */
 			tmcu = x/H + (nacross/H)*(y/V);
 			blockno = tmcu*H*V + H*(y%V) + x%H;
+			if(blockno < 0 || blockno >= h->naccoeff[comp])
+				jpgerror(h, "ReadJPG: block number out of range");
 			acc = h->accoeff[comp][blockno];
 			k = Ss;
 			do {
@@ -1108,6 +1113,8 @@
 		jpgerror(h, "ReadJPG: illegal Ns>1 in progressive AC scan");
 	Ss = ss[1+2];
 	Se = ss[2+2];
+	if(Ss >= 64 || Se >= 64)
+		jpgerror(h, "ReadJPG: invalid coefficient range in progressive AC scan");
 	H = h->comp[comp].H;
 	V = h->comp[comp].V;
 
@@ -1123,6 +1130,8 @@
 	h->sr = 0;
 	h->peek = -1;
 	nibbles(ss[1+1], &z, &Ta);	/* z is thrown away */
+	if(Ta > 3)
+		jpgerror(h, "ReadJPG: invalid Huffman table index");
 	ri = h->ri;
 
 	eobrun = 0;
@@ -1140,6 +1149,8 @@
 			/* arrange blockno to be in same sequence as original scan calculation. */
 			tmcu = x/H + (nacross/H)*(y/V);
 			blockno = tmcu*H*V + H*(y%V) + x%H;
+			if(blockno < 0 || blockno >= h->naccoeff[comp])
+				jpgerror(h, "ReadJPG: block number out of range");
 			acc = ac[blockno];
 			if(eobrun > 0){
 				if(nzeros > 0)
@@ -1172,7 +1183,7 @@
 						}
 						break;
 					}
-					for(i=0; i<16; k++){
+					for(i=0; i<16 && k<=Se; k++){
 						increment(h, acc, k, qt[k]<<Al);
 						if(acc[k] == 0)
 							i++;
@@ -1221,6 +1232,8 @@
 		jpgerror(h, "ReadJPG: bad component index in scan header");
 
 	if(Ss == 0){
+		if(Al > 13)
+			jpgerror(h, "ReadJPG: invalid successive approximation for DC");
 		progressivedc(h, comp, Ah, Al);
 		return;
 	}
@@ -1260,7 +1273,7 @@
 	k = 0;
 	for(y=0; y<dy; y++){
 		for(x=0; x<dx; x++){
-			r = clamp[(data[k+x]+128)+CLAMPOFF];
+			r = CLAMP(data[k+x] + 128);
 			pic[pici+x] = r;
 		}
 		pici += h->X;
@@ -1302,9 +1315,9 @@
 		bp = bpic+pici;
 		if(colorspace == CYCbCr)
 			for(x=0; x<dx; x++){
-				*rp++ = clamp[*p0++ + 128 + CLAMPOFF];
-				*gp++ = clamp[*p1++ + 128 + CLAMPOFF];
-				*bp++ = clamp[*p2++ + 128 + CLAMPOFF];
+				*rp++ = CLAMP(*p0++ + 128);
+				*gp++ = CLAMP(*p1++ + 128);
+				*bp++ = CLAMP(*p2++ + 128);
 			}
 		else
 			for(x=0; x<dx; x++){
@@ -1314,9 +1327,9 @@
 				r = Y+c1*Cr;
 				g = Y-c2*Cb-c3*Cr;
 				b = Y+c4*Cb;
-				*rp++ = clamp[(r>>11)+CLAMPOFF];
-				*gp++ = clamp[(g>>11)+CLAMPOFF];
-				*bp++ = clamp[(b>>11)+CLAMPOFF];
+				*rp++ = CLAMP(r >> 11);
+				*gp++ = CLAMP(g >> 11);
+				*bp++ = CLAMP(b >> 11);
 			}
 		pici += h->X;
 		k += 8;
@@ -1366,9 +1379,9 @@
 		x2 = 0;
 		for(x=0; x<dx; x++){
 			if(colorspace == CYCbCr){
-				rpic[pici+x] = clamp[data0[b0][y0+x0++*H0/Hmax] + 128 + CLAMPOFF];
-				gpic[pici+x] = clamp[data1[b1][y1+x1++*H1/Hmax] + 128 + CLAMPOFF];
-				bpic[pici+x] = clamp[data2[b2][y2+x2++*H2/Hmax] + 128 + CLAMPOFF];
+				rpic[pici+x] = CLAMP(data0[b0][y0+x0++*H0/Hmax] + 128);
+				gpic[pici+x] = CLAMP(data1[b1][y1+x1++*H1/Hmax] + 128);
+				bpic[pici+x] = CLAMP(data2[b2][y2+x2++*H2/Hmax] + 128);
 			}else{
 				Y = (data0[b0][y0+x0++*H0/Hmax]+128)<<11;
 				Cb = data1[b1][y1+x1++*H1/Hmax];
@@ -1376,9 +1389,9 @@
 				r = Y+c1*Cr;
 				g = Y-c2*Cb-c3*Cr;
 				b = Y+c4*Cb;
-				rpic[pici+x] = clamp[(r>>11)+CLAMPOFF];
-				gpic[pici+x] = clamp[(g>>11)+CLAMPOFF];
-				bpic[pici+x] = clamp[(b>>11)+CLAMPOFF];
+				rpic[pici+x] = CLAMP(r >> 11);
+				gpic[pici+x] = CLAMP(g >> 11);
+				bpic[pici+x] = CLAMP(b >> 11);
 			}
 			if(x0*H0/Hmax >= 8){
 				x0 = 0;
@@ -1407,6 +1420,8 @@
 	int code, v, cnt, sr, i;
 	int *maxcode;
 
+	if(t->val == nil)
+		jpgerror(h, "ReadJPG: undefined Huffman table");
 	maxcode = t->maxcode;
 	if(h->cnt < 8)
 		nextbyte(h, 0);
--