libsframe: validate FDE/FRE subsection bounds in sframe_decode

Message ID 178207634240.2958.13202347064770017350@digiscrypt.com
State New
Headers
Series libsframe: validate FDE/FRE subsection bounds in sframe_decode |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed

Commit Message

Naveed Khan June 21, 2026, 9:12 p.m. UTC
  sframe_decode only calls sframe_header_sanity_check_p, which verifies
that sfh_fdeoff <= sfh_freoff but never checks the header-supplied
offsets, lengths and counts against the size of the SFrame buffer.  On
the native-endian decode path -- the foreign-endian path is bounds
checked inside flip_sframe -- a crafted SFrame section therefore drives
two out-of-bounds heap reads:

  - sframe_fde_tbl_init reads sfh_num_fdes function descriptor entries
    starting at frame_buf + sfh_fdeoff, and

  - memcpy (dctx->sfd_fres, frame_buf + sfh_freoff, sfh_fre_len) copies
    sfh_fre_len bytes starting at frame_buf + sfh_freoff.

Both read past the end of the buffer.  The defect is reachable from
objdump --sframe and readelf on an object file carrying a crafted
.sframe section; for example a 28-byte section whose header claims
sfh_fre_len = 0x10000 makes objdump --sframe read 64KB past the
section contents.

Validate in sframe_decode that the FDE and FRE sub-sections described
by the header are fully contained in the buffer before they are read.
The checks use subtraction and division so the arithmetic cannot
overflow.

Signed-off-by: Naveed Khan <naveed@digiscrypt.com>
---
  

Patch

diff --git a/libsframe/sframe.c b/libsframe/sframe.c
index cd6bb302..24d07ae3 100644
--- a/libsframe/sframe.c
+++ b/libsframe/sframe.c
@@ -1480,6 +1480,25 @@  sframe_decode (const char *sf_buf, size_t sf_size, int *errp)
       goto decode_fail_free;
     }
   hdrsz = sframe_get_hdr_size (dhp);
+
+  /* Validate that the FDE and FRE sub-sections described by the SFrame
+     header lie entirely within the SFrame buffer.  The earlier call to
+     sframe_header_sanity_check_p has checked that sfh_fdeoff <= sfh_freoff.
+     The FDE sub-section holds sfh_num_fdes entries and precedes the FRE
+     sub-section, which is sfh_fre_len bytes long.  The arithmetic below uses
+     subtraction and division so that it cannot itself overflow.  */
+  size_t fde_size = sizeof (sframe_func_desc_entry_v2);
+  if (sfp->sfp_version == SFRAME_VERSION_3)
+    fde_size = sizeof (sframe_func_desc_idx_v3);
+  if (hdrsz > sf_size
+      || dhp->sfh_freoff > sf_size - hdrsz
+      || dhp->sfh_fre_len > sf_size - hdrsz - dhp->sfh_freoff
+      || dhp->sfh_num_fdes > (dhp->sfh_freoff - dhp->sfh_fdeoff) / fde_size)
+    {
+      sframe_ret_set_errno (errp, SFRAME_ERR_BUF_INVAL);
+      goto decode_fail_free;
+    }
+
   frame_buf += hdrsz;
 
   /* Handle the SFrame Function Descriptor Entry section.  */