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, ¬e, 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;