x86_sample_perf_regs_mapping selects dwarf_to_perf from one of two
tables, regs_i386[] (9 entries) or regs_x86_64[] (17 entries), depending
on the sample ABI. The final mapping loop was bounded by
ebl->frame_nregs and indexed dwarf_to_perf[i]. For the x86_64 backend
frame_nregs is 17, so when a sample carries PERF_SAMPLE_REGS_ABI_32
(is_abi32 true) the loop reads regs_i386[9..16], eight ints past the end
of the 9-element array. The resulting out-of-bounds values are then used
to index perf_to_regs[].
The abi value originates from a perf sample and is passed unvalidated
through the public dwflst_perf_sample_getframes() API; it is never
checked against the backend ELF class. Commit a532f8d hardened the
perf_to_regs[] write side but left this read overflow in place.
Bound the loop by the length of the selected table so it never indexes
past the array in use.
* backends/x86_initreg_sample.c (x86_sample_perf_regs_mapping):
Compute dwarf_to_perf_len for the selected table and bound the
mapping loop by it.
Signed-off-by: Naveed Khan <[email protected]>
---
diff --git a/backends/x86_initreg_sample.c b/backends/x86_initreg_sample.c
index 07f0f56..eed2943 100644
--- a/backends/x86_initreg_sample.c
+++ b/backends/x86_initreg_sample.c
@@ -63,6 +63,13 @@ x86_sample_perf_regs_mapping (Ebl *ebl,
16/*r8 after flags+segment*/, 17, 18, 19,
20, 21, 22, 23,
8/*ip*/};
const int *dwarf_to_perf = is_abi32 ? regs_i386 : regs_x86_64;
+ /* The two tables have different lengths; the loop below must not index
+ dwarf_to_perf beyond the selected one. This matters when abi does
+ not match the backend (e.g. an x86_64 ebl, frame_nregs == 17,
+ handling an ABI_32 sample where regs_i386 only has 9 entries). */
+ size_t dwarf_to_perf_len = is_abi32 ?
+ sizeof (regs_i386) / sizeof (regs_i386[0]) :
+ sizeof (regs_x86_64) / sizeof (regs_x86_64[0]);
/* Count bits and allocate regs_mapping: */
int j, k, count; uint64_t bit;
@@ -106,7 +113,7 @@ x86_sample_perf_regs_mapping (Ebl *ebl,
/* Locations of perf_regs in the dwarf_regs array, according to
perf_regs_mask and perf_to_regs[]: */
- for (size_t i = 0; i < ebl->frame_nregs; i++)
+ for (size_t i = 0; i < ebl->frame_nregs && i < dwarf_to_perf_len; i++)
{
k = dwarf_to_perf[i];
j = perf_to_regs[k];
--
2.52.0