DragonFlyBSD Kernel Audit
DF-2236 / fix.diff
← back to finding ↓ download raw
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 {