Hello Everyone,

I would like to apologize if you received the same email multiple times
from me today.

This is my first time using a mailing list, and I am still learning how it
works. I was a bit confused with the process and accidentally sent
duplicate messages. Previously, I was mostly working with platforms like
GitHub, so this is a new experience for me.

Thank you for your patience and understanding. I appreciate your support as
I learn and get familiar with these tools.

On Mon, Jun 22, 2026 at 8:34 PM Naveed Khan <[email protected]> wrote:

> 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 minimum of frame_nregs and the selected table
> length.  Neither bound dominates: an i386 backend (frame_nregs 9)
> handling an x86_64-ABI sample has frame_nregs < dwarf_to_perf_len, the
> reverse of the overflow above, so MIN() is required rather than the table
> length alone.  Computing the limit once also keeps the loop bound
> invariant.
>
>         * backends/x86_initreg_sample.c: Include system.h for MIN.
>         (x86_sample_perf_regs_mapping): Compute dwarf_to_perf_len for the
>         selected table and bound the mapping loop by
>         MIN (ebl->frame_nregs, dwarf_to_perf_len).
>
> Signed-off-by: Naveed Khan <[email protected]>
> ---
> >From 3d66e7c14278c08b39f799ce2ef211f79b317276 Mon Sep 17 00:00:00 2001
> From: Naveed Khan <[email protected]>
> Date: Mon, 22 Jun 2026 20:29:05 +0530
> Subject: [PATCH v2] x86: Fix out-of-bounds read of register table in
>  sample_perf_regs_mapping
>
> 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 minimum of frame_nregs and the selected table
> length.  Neither bound dominates: an i386 backend (frame_nregs 9)
> handling an x86_64-ABI sample has frame_nregs < dwarf_to_perf_len, the
> reverse of the overflow above, so MIN() is required rather than the table
> length alone.  Computing the limit once also keeps the loop bound
> invariant.
>
>         * backends/x86_initreg_sample.c: Include system.h for MIN.
>         (x86_sample_perf_regs_mapping): Compute dwarf_to_perf_len for the
>         selected table and bound the mapping loop by
>         MIN (ebl->frame_nregs, dwarf_to_perf_len).
>
> Signed-off-by: Naveed Khan <[email protected]>
> ---
>  backends/x86_initreg_sample.c | 16 ++++++++++++++--
>  1 file changed, 14 insertions(+), 2 deletions(-)
>
> diff --git a/backends/x86_initreg_sample.c b/backends/x86_initreg_sample.c
> index 07f0f56..d80e351 100644
> --- a/backends/x86_initreg_sample.c
> +++ b/backends/x86_initreg_sample.c
> @@ -26,6 +26,8 @@
>     the GNU Lesser General Public License along with this program.  If
>     not, see <http://www.gnu.org/licenses/>.  */
>
> +#include "system.h"
> +
>  static inline bool
>  x86_sample_perf_regs_mapping (Ebl *ebl,
>                               uint64_t perf_regs_mask, uint32_t abi,
> @@ -63,6 +65,11 @@ 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;
> +  /* regs_i386 and regs_x86_64 have different lengths; the mapping loop
> +     below must not index dwarf_to_perf beyond the selected table.  */
> +  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;
> @@ -105,8 +112,13 @@ x86_sample_perf_regs_mapping (Ebl *ebl,
>        return false;
>
>    /* 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++)
> +     perf_regs_mask and perf_to_regs[].  Bound by both frame_nregs and
> +     the selected table length: when abi does not match the backend
> +     either one can be the smaller (an x86_64 ebl with an ABI_32 sample
> +     has frame_nregs 17 > 9, an i386 ebl with a 64-bit sample has
> +     frame_nregs 9 < 17), so take the minimum once, up front.  */
> +  size_t max_reg = MIN (ebl->frame_nregs, dwarf_to_perf_len);
> +  for (size_t i = 0; i < max_reg; i++)
>      {
>        k = dwarf_to_perf[i];
>        j = perf_to_regs[k];
> --
> 2.52.0
>

Reply via email to