The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=9cd0d6231311bc5609403fb35377a7bb97a3cfaf
commit 9cd0d6231311bc5609403fb35377a7bb97a3cfaf Author: John Baldwin <[email protected]> AuthorDate: 2026-06-22 16:41:04 +0000 Commit: John Baldwin <[email protected]> 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;
