git: 9cd0d6231311 - main - cd9660: Add various length checks when parsing RRIP extensions

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Mon, 22 Jun 2026 16:47:27 UTC
The branch main has been updated by jhb:

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

commit 9cd0d6231311bc5609403fb35377a7bb97a3cfaf
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2026-06-22 16:41:04 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2026-06-22 16:41:04 +0000

    cd9660: Add various length checks when parsing RRIP extensions
    
    Pass the length of a RockRidge attribute to the handler functions and
    validate that length in each handler.  If a parsing error is detected,
    abort the entire parsing pass.
    
    Reviewed by:    des
    Differential Revision:  https://reviews.freebsd.org/D57136
---
 sys/fs/cd9660/cd9660_rrip.c | 186 ++++++++++++++++++++++++++++++++------------
 sys/fs/cd9660/iso_rrip.h    |   1 +
 2 files changed, 136 insertions(+), 51 deletions(-)

diff --git a/sys/fs/cd9660/cd9660_rrip.c b/sys/fs/cd9660/cd9660_rrip.c
index db87f6d1223e..e3aab4fa6115 100644
--- a/sys/fs/cd9660/cd9660_rrip.c
+++ b/sys/fs/cd9660/cd9660_rrip.c
@@ -48,7 +48,7 @@
 #include <fs/cd9660/cd9660_rrip.h>
 #include <fs/cd9660/iso_rrip.h>
 
-typedef int	rrt_func_t(void *, ISO_RRIP_ANALYZE *ana);
+typedef int	rrt_func_t(void *, size_t, ISO_RRIP_ANALYZE *ana);
 
 typedef struct {
 	char type[2];
@@ -57,32 +57,48 @@ typedef struct {
 	int result;
 } RRIP_TABLE;
 
-static int	cd9660_rrip_altname(ISO_RRIP_ALTNAME *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_attr(ISO_RRIP_ATTR *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_cont(ISO_RRIP_CONT *p, ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_altname(ISO_RRIP_ALTNAME *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_attr(ISO_RRIP_ATTR *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_cont(ISO_RRIP_CONT *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
 static void	cd9660_rrip_defattr(struct iso_directory_record *isodir,
 		    ISO_RRIP_ANALYZE *ana);
 static void	cd9660_rrip_defname(struct iso_directory_record *isodir,
 		    ISO_RRIP_ANALYZE *ana);
 static void	cd9660_rrip_deftstamp(struct iso_directory_record *isodir,
 		    ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_device(ISO_RRIP_DEVICE *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_extref(ISO_RRIP_EXTREF *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_idflag(ISO_RRIP_IDFLAG *p, ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_device(ISO_RRIP_DEVICE *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_extref(ISO_RRIP_EXTREF *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_idflag(ISO_RRIP_IDFLAG *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
 static int	cd9660_rrip_loop(struct iso_directory_record *isodir,
 		    ISO_RRIP_ANALYZE *ana, RRIP_TABLE *table);
-static int	cd9660_rrip_pclink(ISO_RRIP_CLINK *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_reldir(ISO_RRIP_RELDIR *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_slink(ISO_RRIP_SLINK *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_stop(ISO_SUSP_HEADER *p, ISO_RRIP_ANALYZE *ana);
-static int	cd9660_rrip_tstamp(ISO_RRIP_TSTAMP *p, ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_pclink(ISO_RRIP_CLINK *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_reldir(ISO_RRIP_RELDIR *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_slink(ISO_RRIP_SLINK *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_stop(ISO_SUSP_HEADER *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
+static int	cd9660_rrip_tstamp(ISO_RRIP_TSTAMP *p, size_t len,
+		    ISO_RRIP_ANALYZE *ana);
 
 /*
  * POSIX file attribute
  */
 static int
-cd9660_rrip_attr(ISO_RRIP_ATTR *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_attr(ISO_RRIP_ATTR *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	ana->inop->inode.iso_mode = isonum_733(p->mode);
 	ana->inop->inode.iso_uid = isonum_733(p->uid);
 	ana->inop->inode.iso_gid = isonum_733(p->gid);
@@ -103,16 +119,23 @@ cd9660_rrip_defattr(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana)
  * Symbolic Links
  */
 static int
-cd9660_rrip_slink(ISO_RRIP_SLINK *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_slink(ISO_RRIP_SLINK *p, size_t plen, ISO_RRIP_ANALYZE *ana)
 {
 	ISO_RRIP_SLINK_COMPONENT *pcomp;
 	ISO_RRIP_SLINK_COMPONENT *pcompe;
+	ISO_RRIP_SLINK_COMPONENT *pcompn;
 	int len, wlen, cont;
 	char *outbuf, *inbuf;
 	char hostbuf[MAXHOSTNAMELEN];
 
+	/* This requires at least one byte of a component. */
+	if (plen < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component;
-	pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length));
+	pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + plen);
 	len = *ana->outlen;
 	outbuf = ana->outbuf;
 	cont = ana->cont;
@@ -120,10 +143,16 @@ cd9660_rrip_slink(ISO_RRIP_SLINK *p, ISO_RRIP_ANALYZE *ana)
 	/*
 	 * Gathering a Symbolic name from each component with path
 	 */
-	for (;
-	     pcomp < pcompe;
-	     pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp + ISO_RRIP_SLSIZ
-						  + isonum_711(pcomp->clen))) {
+	for (; pcomp < pcompe; pcomp = pcompn) {
+		pcompn = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp +
+		    ISO_RRIP_SLSIZ + isonum_711(pcomp->clen));
+
+		/* If this component overflows the buffer, abort parsing. */
+		if (pcompn > pcompe) {
+			ana->error = true;
+			return (0);
+		}
+
 		if (!cont) {
 			if (len < ana->maxlen) {
 				len++;
@@ -175,21 +204,15 @@ cd9660_rrip_slink(ISO_RRIP_SLINK *p, ISO_RRIP_ANALYZE *ana)
 			/* Inserting component */
 			wlen = isonum_711(pcomp->clen);
 			inbuf = pcomp->name;
-			if (inbuf + wlen > (char *)pcompe)
-				wlen = ana->maxlen + 1;
 			break;
 		default:
 			printf("RRIP with incorrect flags?");
-			wlen = ana->maxlen + 1;
-			break;
+			ana->error = true;
+			return (0);
 		}
 
 		if (len + wlen > ana->maxlen) {
-			/* indicate error to caller */
-			ana->cont = 1;
-			ana->fields = 0;
-			ana->outbuf -= *ana->outlen;
-			*ana->outlen = 0;
+			ana->error = true;
 			return (0);
 		}
 
@@ -212,13 +235,18 @@ cd9660_rrip_slink(ISO_RRIP_SLINK *p, ISO_RRIP_ANALYZE *ana)
  * Alternate name
  */
 static int
-cd9660_rrip_altname(ISO_RRIP_ALTNAME *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_altname(ISO_RRIP_ALTNAME *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
 	char *inbuf;
 	int wlen;
 	int cont;
 	char hostbuf[MAXHOSTNAMELEN];
 
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	inbuf = "..";
 	wlen = 0;
 	cont = 0;
@@ -246,23 +274,18 @@ cd9660_rrip_altname(ISO_RRIP_ALTNAME *p, ISO_RRIP_ANALYZE *ana)
 		/* FALLTHROUGH */
 	case 0:
 		/* Inserting component */
-		wlen = isonum_711(p->h.length) - 5;
-		inbuf = (char *)p + 5;
-		if (wlen < 0)
-			wlen = ana->maxlen + 1;
+		wlen = len - sizeof(*p);
+		inbuf = (char *)(p + 1);
 		break;
 
 	default:
 		printf("RRIP with incorrect NM flags?\n");
-		wlen = ana->maxlen + 1;
-		break;
+		ana->error = true;
+		return (0);
 	}
 
 	if ((*ana->outlen += wlen) > ana->maxlen) {
-		/* treat as no name field */
-		ana->fields &= ~ISO_SUSP_ALTNAME;
-		ana->outbuf -= *ana->outlen - wlen;
-		*ana->outlen = 0;
+		ana->error = true;
 		return (0);
 	}
 
@@ -299,8 +322,13 @@ cd9660_rrip_defname(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana)
  * Parent or Child Link
  */
 static int
-cd9660_rrip_pclink(ISO_RRIP_CLINK *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_pclink(ISO_RRIP_CLINK *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	*ana->inump = isonum_733(p->dir_loc) << ana->imp->im_bshift;
 	ana->fields &= ~(ISO_SUSP_CLINK | ISO_SUSP_PLINK);
 	return (*p->h.type == 'C' ? ISO_SUSP_CLINK : ISO_SUSP_PLINK);
@@ -310,7 +338,7 @@ cd9660_rrip_pclink(ISO_RRIP_CLINK *p, ISO_RRIP_ANALYZE *ana)
  * Relocated directory
  */
 static int
-cd9660_rrip_reldir(ISO_RRIP_RELDIR *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_reldir(ISO_RRIP_RELDIR *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
 	/* special hack to make caller aware of RE field */
 	*ana->outlen = 0;
@@ -319,9 +347,29 @@ cd9660_rrip_reldir(ISO_RRIP_RELDIR *p, ISO_RRIP_ANALYZE *ana)
 }
 
 static int
-cd9660_rrip_tstamp(ISO_RRIP_TSTAMP *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_tstamp(ISO_RRIP_TSTAMP *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
 	u_char *ptime;
+	u_int count;
+
+	if (len < sizeof(*p))
+		goto bad;
+
+	count = 0;
+	if (*p->flags & ISO_SUSP_TSTAMP_CREAT)
+		count++;
+	if (*p->flags & ISO_SUSP_TSTAMP_MODIFY)
+		count++;
+	if (*p->flags & ISO_SUSP_TSTAMP_ACCESS)
+		count++;
+	if (*p->flags & ISO_SUSP_TSTAMP_ATTR)
+		count++;
+	if (*p->flags & ISO_SUSP_TSTAMP_FORM17)
+		count *= 17;
+	else
+		count *= 7;
+	if (offsetof(ISO_RRIP_TSTAMP, time) + count > len)
+		goto bad;
 
 	ptime = p->time;
 
@@ -374,6 +422,10 @@ cd9660_rrip_tstamp(ISO_RRIP_TSTAMP *p, ISO_RRIP_ANALYZE *ana)
 	}
 	ana->fields &= ~ISO_SUSP_TSTAMP;
 	return (ISO_SUSP_TSTAMP);
+
+bad:
+	ana->error = true;
+	return (0);
 }
 
 static void
@@ -387,10 +439,15 @@ cd9660_rrip_deftstamp(struct iso_directory_record *isodir,
  * POSIX device modes
  */
 static int
-cd9660_rrip_device(ISO_RRIP_DEVICE *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_device(ISO_RRIP_DEVICE *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
 	u_int high, low;
 
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	high = isonum_733(p->dev_t_high);
 	low  = isonum_733(p->dev_t_low);
 
@@ -406,12 +463,17 @@ cd9660_rrip_device(ISO_RRIP_DEVICE *p, ISO_RRIP_ANALYZE *ana)
  * Flag indicating
  */
 static int
-cd9660_rrip_idflag(ISO_RRIP_IDFLAG *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_idflag(ISO_RRIP_IDFLAG *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	ana->fields &= isonum_711(p->flags) | ~0xff; /* don't touch high bits */
 	/* special handling of RE field */
 	if (ana->fields & ISO_SUSP_RELDIR)
-		return (cd9660_rrip_reldir(/* XXX */ (ISO_RRIP_RELDIR *)p, ana));
+		return (cd9660_rrip_reldir(/* XXX */ (ISO_RRIP_RELDIR *)p, len, ana));
 
 	return (ISO_SUSP_IDFLAG);
 }
@@ -420,8 +482,13 @@ cd9660_rrip_idflag(ISO_RRIP_IDFLAG *p, ISO_RRIP_ANALYZE *ana)
  * Continuation pointer
  */
 static int
-cd9660_rrip_cont(ISO_RRIP_CONT *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_cont(ISO_RRIP_CONT *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
+	if (len < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
+
 	ana->iso_ce_blk = isonum_733(p->location);
 	ana->iso_ce_off = isonum_733(p->offset);
 	ana->iso_ce_len = isonum_733(p->length);
@@ -432,7 +499,7 @@ cd9660_rrip_cont(ISO_RRIP_CONT *p, ISO_RRIP_ANALYZE *ana)
  * System Use end
  */
 static int
-cd9660_rrip_stop(ISO_SUSP_HEADER *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_stop(ISO_SUSP_HEADER *p, size_t len, ISO_RRIP_ANALYZE *ana)
 {
 	return (ISO_SUSP_STOP);
 }
@@ -441,11 +508,18 @@ cd9660_rrip_stop(ISO_SUSP_HEADER *p, ISO_RRIP_ANALYZE *ana)
  * Extension reference
  */
 static int
-cd9660_rrip_extref(ISO_RRIP_EXTREF *p, ISO_RRIP_ANALYZE *ana)
+cd9660_rrip_extref(ISO_RRIP_EXTREF *p, size_t plen, ISO_RRIP_ANALYZE *ana)
 {
-	size_t len = isonum_711(p->len_id);
-	char *data = (char *)(p + 1);
+	size_t len;
+	char *data;
+
+	if (plen < sizeof(*p)) {
+		ana->error = true;
+		return (0);
+	}
 
+	len = isonum_711(p->len_id);
+	data = (char *)(p + 1);
 	if ((len == 10 && memcmp(data, "RRIP_1991A", len) == 0) ||
 	    (len == 10 && memcmp(data, "IEEE_P1282", len) == 0) ||
 	    (len ==  9 && memcmp(data, "IEEE_1282", len) == 0)) {
@@ -490,6 +564,7 @@ cd9660_rrip_loop(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana,
 	pend = (ISO_SUSP_HEADER *)((char *)isodir + isonum_711(isodir->length));
 
 	result = 0;
+	ana->error = false;
 	while (1) {
 		ana->iso_ce_len = 0;
 		/*
@@ -515,10 +590,18 @@ cd9660_rrip_loop(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana,
 				for (ptable = table; ptable->func; ptable++) {
 					if (phead->type[0] == ptable->type[0] &&
 					    phead->type[1] == ptable->type[1]) {
-						result |= ptable->func(phead, ana);
+						result |= ptable->func(phead,
+						    isonum_711(phead->length),
+						    ana);
 						break;
 					}
 				}
+				if (ana->error) {
+					*ana->outlen = 0;
+					result = 0;
+					goto error;
+				}
+
 				if (!ana->fields)
 					break;
 			}
@@ -550,6 +633,7 @@ cd9660_rrip_loop(struct iso_directory_record *isodir, ISO_RRIP_ANALYZE *ana,
 		} else
 			break;
 	}
+error:
 	if (bp)
 		brelse(bp);
 	/*
diff --git a/sys/fs/cd9660/iso_rrip.h b/sys/fs/cd9660/iso_rrip.h
index 5a75beb08d93..934a9f7bd2da 100644
--- a/sys/fs/cd9660/iso_rrip.h
+++ b/sys/fs/cd9660/iso_rrip.h
@@ -68,6 +68,7 @@ typedef struct {
 	u_short		*outlen;	/* length of above */
 	u_short		maxlen;		/* maximum length of above */
 	int		cont;		/* continuation of above */
+	bool		error;		/* parsing error occurred */
 } ISO_RRIP_ANALYZE;
 
 struct iso_directory_record;