Skip to content

Commit dd66409

Browse files
rpedgecokees
authored andcommitted
binfmt_elf: Don't write past end of notes for regset gap
In fill_thread_core_info() the ptrace accessible registers are collected to be written out as notes in a core file. The note array is allocated from a size calculated by iterating the user regset view, and counting the regsets that have a non-zero core_note_type. However, this only allows for there to be non-zero core_note_type at the end of the regset view. If there are any gaps in the middle, fill_thread_core_info() will overflow the note allocation, as it iterates over the size of the view and the allocation would be smaller than that. There doesn't appear to be any arch that has gaps such that they exceed the notes allocation, but the code is brittle and tries to support something it doesn't. It could be fixed by increasing the allocation size, but instead just have the note collecting code utilize the array better. This way the allocation can stay smaller. Even in the case of no arch's that have gaps in their regset views, this introduces a change in the resulting indicies of t->notes. It does not introduce any changes to the core file itself, because any blank notes are skipped in write_note_info(). In case, the allocation logic between fill_note_info() and fill_thread_core_info() ever diverges from the usage logic, warn and skip writing any notes that would overflow the array. This fix is derrived from an earlier one[0] by Yu-cheng Yu. [0] https://lore.kernel.org/lkml/20180717162502.32274-1-yu-cheng.yu@intel.com/ Co-developed-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/r/20220317192013.13655-4-rick.p.edgecombe@intel.com
1 parent 19e8b70 commit dd66409

1 file changed

Lines changed: 14 additions & 10 deletions

File tree

fs/binfmt_elf.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,9 +1766,9 @@ static void do_thread_regset_writeback(struct task_struct *task,
17661766

17671767
static int fill_thread_core_info(struct elf_thread_core_info *t,
17681768
const struct user_regset_view *view,
1769-
long signr, size_t *total)
1769+
long signr, struct elf_note_info *info)
17701770
{
1771-
unsigned int i;
1771+
unsigned int note_iter, view_iter;
17721772

17731773
/*
17741774
* NT_PRSTATUS is the one special case, because the regset data
@@ -1782,17 +1782,17 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
17821782

17831783
fill_note(&t->notes[0], "CORE", NT_PRSTATUS,
17841784
PRSTATUS_SIZE, &t->prstatus);
1785-
*total += notesize(&t->notes[0]);
1785+
info->size += notesize(&t->notes[0]);
17861786

17871787
do_thread_regset_writeback(t->task, &view->regsets[0]);
17881788

17891789
/*
17901790
* Each other regset might generate a note too. For each regset
1791-
* that has no core_note_type or is inactive, we leave t->notes[i]
1792-
* all zero and we'll know to skip writing it later.
1791+
* that has no core_note_type or is inactive, skip it.
17931792
*/
1794-
for (i = 1; i < view->n; ++i) {
1795-
const struct user_regset *regset = &view->regsets[i];
1793+
note_iter = 1;
1794+
for (view_iter = 1; view_iter < view->n; ++view_iter) {
1795+
const struct user_regset *regset = &view->regsets[view_iter];
17961796
int note_type = regset->core_note_type;
17971797
bool is_fpreg = note_type == NT_PRFPREG;
17981798
void *data;
@@ -1808,13 +1808,17 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
18081808
if (ret < 0)
18091809
continue;
18101810

1811+
if (WARN_ON_ONCE(note_iter >= info->thread_notes))
1812+
break;
1813+
18111814
if (is_fpreg)
18121815
SET_PR_FPVALID(&t->prstatus);
18131816

1814-
fill_note(&t->notes[i], is_fpreg ? "CORE" : "LINUX",
1817+
fill_note(&t->notes[note_iter], is_fpreg ? "CORE" : "LINUX",
18151818
note_type, ret, data);
18161819

1817-
*total += notesize(&t->notes[i]);
1820+
info->size += notesize(&t->notes[note_iter]);
1821+
note_iter++;
18181822
}
18191823

18201824
return 1;
@@ -1894,7 +1898,7 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
18941898
* Now fill in each thread's information.
18951899
*/
18961900
for (t = info->thread; t != NULL; t = t->next)
1897-
if (!fill_thread_core_info(t, view, cprm->siginfo->si_signo, &info->size))
1901+
if (!fill_thread_core_info(t, view, cprm->siginfo->si_signo, info))
18981902
return 0;
18991903

19001904
/*

0 commit comments

Comments
 (0)