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
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>
---
@@ -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. */