git: c14665b4aee7 - stable/13 - diff: Fix integer overflow.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Thu, 01 Aug 2024 16:46:58 UTC
The branch stable/13 has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=c14665b4aee7e1594467bac4a9d9cc5c66173975

commit c14665b4aee7e1594467bac4a9d9cc5c66173975
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2024-07-29 14:02:29 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2024-08-01 16:46:19 +0000

    diff: Fix integer overflow.
    
    The legacy Stone algorithm uses `int` to represent line numbers, array
    indices, and array lengths.  If given inputs approaching `INT_MAX` lines,
    it would overflow and attempt to allocate ridiculously large amounts of
    memory.  To avoid this without penalizing non-pathological inputs,
    switch a few variables to `size_t` and add checks while and immediately
    after reading both inputs.
    
    MFC after:      3 days
    PR:             280371
    Sponsored by:   Klara, Inc.
    Reviewed by:    allanjude
    Differential Revision:  https://reviews.freebsd.org/D46169
    
    (cherry picked from commit 9317242469f1ca682626d9806f8caf65d143c09a)
---
 usr.bin/diff/diffreg.c | 45 ++++++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 21 deletions(-)

diff --git a/usr.bin/diff/diffreg.c b/usr.bin/diff/diffreg.c
index a73020c44a80..fb01fa24a281 100644
--- a/usr.bin/diff/diffreg.c
+++ b/usr.bin/diff/diffreg.c
@@ -217,9 +217,9 @@ static int  *klist;		/* will be overlaid on file[0] after class */
 static int  *member;		/* will be overlaid on file[1] */
 static int   clen;
 static int   inifdef;		/* whether or not we are in a #ifdef block */
-static int   len[2];
-static int   pref, suff;	/* length of prefix and suffix */
-static int   slen[2];
+static size_t	 len[2];		/* lengths of files in lines */
+static size_t	 pref, suff;		/* lengths of prefix and suffix */
+static size_t	 slen[2];		/* lengths of files minus pref / suff */
 static int   anychange;
 static int   hw, padding;	/* half width and padding */
 static int   edoffset;
@@ -391,6 +391,10 @@ diffreg(char *file1, char *file2, int flags, int capsicum)
 		status |= 1;
 		goto closem;
 	}
+	if (len[0] > INT_MAX - 2)
+		errc(1, EFBIG, "%s", file1);
+	if (len[1] > INT_MAX - 2)
+		errc(1, EFBIG, "%s", file2);
 
 	prune();
 	sort(sfile[0], slen[0]);
@@ -529,18 +533,17 @@ prepare(int i, FILE *fd, size_t filesize, int flags)
 		sz = 100;
 
 	p = xcalloc(sz + 3, sizeof(*p));
-	while ((r = readhash(fd, flags, &h)) != RH_EOF)
-		switch (r) {
-		case RH_EOF: /* otherwise clang complains */
-		case RH_BINARY:
+	while ((r = readhash(fd, flags, &h)) != RH_EOF) {
+		if (r == RH_BINARY)
 			return (false);
-		case RH_OK:
-			if (j == sz) {
-				sz = sz * 3 / 2;
-				p = xreallocarray(p, sz + 3, sizeof(*p));
-			}
-			p[++j].value = h;
+		if (j == SIZE_MAX)
+			break;
+		if (j == sz) {
+			sz = sz * 3 / 2;
+			p = xreallocarray(p, sz + 3, sizeof(*p));
 		}
+		p[++j].value = h;
+	}
 
 	len[i] = j;
 	file[i] = p;
@@ -551,7 +554,7 @@ prepare(int i, FILE *fd, size_t filesize, int flags)
 static void
 prune(void)
 {
-	int i, j;
+	size_t i, j;
 
 	for (pref = 0; pref < len[0] && pref < len[1] &&
 	    file[0][pref + 1].value == file[1][pref + 1].value;
@@ -708,7 +711,7 @@ static void
 unravel(int p)
 {
 	struct cand *q;
-	int i;
+	size_t i;
 
 	for (i = 0; i <= len[0]; i++)
 		J[i] = i <= pref ? i :
@@ -735,7 +738,7 @@ check(FILE *f1, FILE *f2, int flags)
 	ixold[0] = ixnew[0] = 0;
 	jackpot = 0;
 	ctold = ctnew = 0;
-	for (i = 1; i <= len[0]; i++) {
+	for (i = 1; i <= (int)len[0]; i++) {
 		if (J[i] == 0) {
 			ixold[i] = ctold += skipline(f1);
 			continue;
@@ -835,7 +838,7 @@ check(FILE *f1, FILE *f2, int flags)
 		ixnew[j] = ctnew;
 		j++;
 	}
-	for (; j <= len[1]; j++) {
+	for (; j <= (int)len[1]; j++) {
 		ixnew[j] = ctnew += skipline(f2);
 	}
 	/*
@@ -1488,9 +1491,9 @@ dump_context_vec(FILE *f1, FILE *f2, int flags)
 
 	b = d = 0;		/* gcc */
 	lowa = MAX(1, cvp->a - diff_context);
-	upb = MIN(len[0], context_vec_ptr->b + diff_context);
+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
 	lowc = MAX(1, cvp->c - diff_context);
-	upd = MIN(len[1], context_vec_ptr->d + diff_context);
+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
 
 	printf("***************");
 	if ((flags & D_PROTOTYPE)) {
@@ -1591,9 +1594,9 @@ dump_unified_vec(FILE *f1, FILE *f2, int flags)
 
 	b = d = 0;		/* gcc */
 	lowa = MAX(1, cvp->a - diff_context);
-	upb = MIN(len[0], context_vec_ptr->b + diff_context);
+	upb = MIN((int)len[0], context_vec_ptr->b + diff_context);
 	lowc = MAX(1, cvp->c - diff_context);
-	upd = MIN(len[1], context_vec_ptr->d + diff_context);
+	upd = MIN((int)len[1], context_vec_ptr->d + diff_context);
 
 	printf("@@ -");
 	uni_range(lowa, upb);