DragonFlyBSD Kernel Audit
DF-0070 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/kern/kern_checkpoint.c b/sys/kern/kern_checkpoint.c
--- a/sys/kern/kern_checkpoint.c
+++ b/sys/kern/kern_checkpoint.c
@@ -73,7 +73,7 @@
 
 static int elf_loadphdrs(struct file *fp,  Elf_Phdr *phdr, int numsegs);
 static int elf_getnotes(struct lwp *lp, struct file *fp, size_t notesz);
-static int elf_demarshalnotes(void *src, prpsinfo_t *psinfo,
+static int elf_demarshalnotes(void *src, size_t srcsz, prpsinfo_t *psinfo,
 		 prstatus_t *status, prfpregset_t *fpregset, int nthreads);
 static int elf_loadnotes(struct lwp *, prpsinfo_t *, prstatus_t *,
 		 prfpregset_t *);
@@ -197,7 +197,7 @@
 	PRINTF(("reading notes section\n"));
 	if ((error = read_check(fp, note, notesz)) != 0)
 		goto done;
-	error = elf_demarshalnotes(note, psinfo, status, fpregset, nthreads);
+	error = elf_demarshalnotes(note, notesz, psinfo, status, fpregset, nthreads);
 	if (error)
 		goto done;
 	/* fetch register state from notes */
@@ -311,10 +311,11 @@
 }
 
 static int
-elf_getnote(void *src, size_t *off, const char *name, unsigned int type,
-	    void **desc, size_t descsz)
+elf_getnote(void *src, size_t *off, size_t srcsz, const char *name,
+	    unsigned int type, void **desc, size_t descsz)
 {
 	Elf_Note note;
+	size_t namesz_pad, descsz_pad;
 	int error;
 
 	TRACE_ENTER;
@@ -322,6 +323,16 @@
 		error = EFAULT;
 		goto done;
 	}
+	/*
+	 * Validate every access against the source buffer size before
+	 * touching the data.  Without these checks a crafted n_namesz /
+	 * n_descsz drives *off past the buffer and the bcopy() below reads
+	 * kernel heap out of bounds (DF-0070).
+	 */
+	if (*off > srcsz || srcsz - *off < sizeof(note)) {
+		error = EINVAL;
+		goto done;
+	}
 	bcopy((char *)src + *off, &note, sizeof note);
 
 	PRINTF(("at offset: %zd expected note of type: %d - got: %d\n",
@@ -332,19 +343,31 @@
 		error = EINVAL;
 		goto done;
 	}
+	/* Guard against an attacker-inflated n_namesz and the OOB strncmp. */
+	namesz_pad = roundup2(note.n_namesz, sizeof(Elf_Size));
+	if (note.n_namesz > 32 || namesz_pad > srcsz - *off) {
+		error = EINVAL;
+		goto done;
+	}
 	if (strncmp(name, (char *) src + *off, note.n_namesz) != 0) {
 		error = EINVAL;
 		goto done;
 	}
-	*off += roundup2(note.n_namesz, sizeof(Elf_Size));
+	*off += namesz_pad;
 	if (note.n_descsz != descsz) {
 		TRACE_ERR;
 		error = EINVAL;
 		goto done;
 	}
+	/* Guard the descriptor bcopy against an OOB read. */
+	descsz_pad = roundup2(note.n_descsz, sizeof(Elf_Size));
+	if (descsz_pad > srcsz - *off) {
+		error = EINVAL;
+		goto done;
+	}
 	if (desc)
 	        bcopy((char *)src + *off, *desc, note.n_descsz);
-	*off += roundup2(note.n_descsz, sizeof(Elf_Size));
+	*off += descsz_pad;
 	error = 0;
  done:
 	TRACE_EXIT;
@@ -352,23 +375,23 @@
 }
 
 static int
-elf_demarshalnotes(void *src, prpsinfo_t *psinfo, prstatus_t *status,
-		   prfpregset_t *fpregset, int nthreads)
+elf_demarshalnotes(void *src, size_t srcsz, prpsinfo_t *psinfo,
+		   prstatus_t *status, prfpregset_t *fpregset, int nthreads)
 {
 	int i;
 	int error;
 	size_t off = 0;
 
 	TRACE_ENTER;
-	error = elf_getnote(src, &off, "CORE", NT_PRPSINFO,
+	error = elf_getnote(src, &off, srcsz, "CORE", NT_PRPSINFO,
 			   (void **)&psinfo, sizeof(prpsinfo_t));
 	if (error)
 		goto done;
-	error = elf_getnote(src, &off, "CORE", NT_PRSTATUS,
+	error = elf_getnote(src, &off, srcsz, "CORE", NT_PRSTATUS,
 			   (void **)&status, sizeof(prstatus_t));
 	if (error)
 		goto done;
-	error = elf_getnote(src, &off, "CORE", NT_FPREGSET,
+	error = elf_getnote(src, &off, srcsz, "CORE", NT_FPREGSET,
 			   (void **)&fpregset, sizeof(prfpregset_t));
 	if (error)
 		goto done;
@@ -379,11 +402,11 @@
 	 */
 	for (i = 0 ; i < nthreads - 1; i++) {
 		status++; fpregset++;
-		error = elf_getnote(src, &off, "CORE", NT_PRSTATUS,
+		error = elf_getnote(src, &off, srcsz, "CORE", NT_PRSTATUS,
 				   (void **)&status, sizeof (prstatus_t));
 		if (error)
 			goto done;
-		error = elf_getnote(src, &off, "CORE", NT_FPREGSET,
+		error = elf_getnote(src, &off, srcsz, "CORE", NT_FPREGSET,
 				   (void **)&fpregset, sizeof(prfpregset_t));
 		if (error)
 			goto done;