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