DF-2236 / fix.diff
diff --git a/sys/sys/iconv.h b/sys/sys/iconv.h --- a/sys/sys/iconv.h +++ b/sys/sys/iconv.h @@ -122,6 +122,7 @@ struct iconv_cspair { int cp_id; /* unique id of charset pair */ int cp_refcount; /* number of references from other pairs */ + int cp_datalen; /* size of cp_data allocation in bytes */ const char * cp_from; const char * cp_to; void * cp_data; diff --git a/sys/libiconv/iconv.c b/sys/libiconv/iconv.c --- a/sys/libiconv/iconv.c +++ b/sys/libiconv/iconv.c @@ -209,6 +209,7 @@ } else csp->cp_from = iconv_unicode_string; csp->cp_data = data; + csp->cp_datalen = 0; /* set by caller if data is provided */ TAILQ_INSERT_TAIL(&iconv_cslist, csp, cp_link); *cspp = csp; @@ -425,6 +426,7 @@ } if (din.ia_datalen) { csp->cp_data = kmalloc(din.ia_datalen, M_ICONVDATA, M_WAITOK); + csp->cp_datalen = din.ia_datalen; error = copyin(din.ia_data, csp->cp_data, din.ia_datalen); if (error) goto bad; diff --git a/sys/libiconv/iconv_xlat16.c b/sys/libiconv/iconv_xlat16.c --- a/sys/libiconv/iconv_xlat16.c +++ b/sys/libiconv/iconv_xlat16.c @@ -65,10 +65,27 @@ int i; dp = (struct iconv_xlat16 *)kobj_create((struct kobj_class*)dcp, M_ICONV, M_WAITOK); + /* + * Validate cp_data: it must be non-NULL and at least large enough to + * hold the 0x200-entry index (sizeof(dp->d_table)). Each non-NULL + * index entry implies a 0x200-byte data block that must also fit. + * Without these checks a maliciously undersized table registered via + * kern.iconv.add causes a NULL-pointer deref or OOB heap read here. + */ + if (csp->cp_data == NULL || csp->cp_datalen < sizeof(dp->d_table)) { + kobj_delete((struct kobj*)dp, M_ICONV); + return EINVAL; + } headp = (uint32_t *)((caddr_t)csp->cp_data + sizeof(dp->d_table)); idxp = (uint32_t **)csp->cp_data; for (i = 0 ; i < 0x200 ; i++) { if (*idxp) { + /* ensure the implied 0x200-byte data block is in-bounds */ + if ((caddr_t)headp + 0x200 * sizeof(uint32_t) > + (caddr_t)csp->cp_data + csp->cp_datalen) { + kobj_delete((struct kobj*)dp, M_ICONV); + return EINVAL; + } dp->d_table[i] = headp; headp += 0x80; } else { |