@@ -1121,9 +1121,21 @@ impl HostSharedMemory {
11211121 let last_element_offset_rel: usize =
11221122 self . read :: < u64 > ( last_element_offset_abs - 8 ) ? as usize ;
11231123
1124+ // Validate element offset (guest-writable): must be in [8, stack_pointer_rel).
1125+ if last_element_offset_rel >= stack_pointer_rel || last_element_offset_rel < 8 {
1126+ return Err ( new_error ! (
1127+ "Corrupt buffer back-pointer: element offset {} is outside valid range [8, {})." ,
1128+ last_element_offset_rel,
1129+ stack_pointer_rel,
1130+ ) ) ;
1131+ }
1132+
11241133 // make it absolute
11251134 let last_element_offset_abs = last_element_offset_rel + buffer_start_offset;
11261135
1136+ // Max bytes the element can span (excluding the 8-byte back-pointer).
1137+ let max_element_size = stack_pointer_rel - last_element_offset_rel - 8 ;
1138+
11271139 // Get the size of the flatbuffer buffer from memory
11281140 let fb_buffer_size = {
11291141 let size_i32 = self . read :: < u32 > ( last_element_offset_abs) ? + 4 ;
@@ -1133,6 +1145,14 @@ impl HostSharedMemory {
11331145 usize:: try_from ( size_i32)
11341146 } ?;
11351147
1148+ if fb_buffer_size > max_element_size {
1149+ return Err ( new_error ! (
1150+ "Corrupt buffer size prefix: flatbuffer claims {} bytes but the element slot is only {} bytes." ,
1151+ fb_buffer_size,
1152+ max_element_size
1153+ ) ) ;
1154+ }
1155+
11361156 let mut result_buffer = vec ! [ 0 ; fb_buffer_size] ;
11371157
11381158 self . copy_to_slice ( & mut result_buffer, last_element_offset_abs) ?;
@@ -1631,6 +1651,139 @@ mod tests {
16311651 }
16321652 }
16331653
1654+ /// Bounds checking for `try_pop_buffer_into` against corrupt guest data.
1655+ mod try_pop_buffer_bounds {
1656+ use super :: * ;
1657+
1658+ #[ derive( Debug , PartialEq ) ]
1659+ struct RawBytes ( Vec < u8 > ) ;
1660+
1661+ impl TryFrom < & [ u8 ] > for RawBytes {
1662+ type Error = String ;
1663+ fn try_from ( value : & [ u8 ] ) -> std:: result:: Result < Self , Self :: Error > {
1664+ Ok ( RawBytes ( value. to_vec ( ) ) )
1665+ }
1666+ }
1667+
1668+ /// Create a buffer with stack pointer initialized to 8 (empty).
1669+ fn make_buffer ( mem_size : usize ) -> super :: super :: HostSharedMemory {
1670+ let eshm = ExclusiveSharedMemory :: new ( mem_size) . unwrap ( ) ;
1671+ let ( hshm, _) = eshm. build ( ) ;
1672+ hshm. write :: < u64 > ( 0 , 8u64 ) . unwrap ( ) ;
1673+ hshm
1674+ }
1675+
1676+ #[ test]
1677+ fn normal_push_pop_roundtrip ( ) {
1678+ let mem_size = 4096 ;
1679+ let mut hshm = make_buffer ( mem_size) ;
1680+
1681+ // Size-prefixed flatbuffer-like payload: [size: u32 LE][payload]
1682+ let payload = b"hello" ;
1683+ let mut data = Vec :: new ( ) ;
1684+ data. extend_from_slice ( & ( payload. len ( ) as u32 ) . to_le_bytes ( ) ) ;
1685+ data. extend_from_slice ( payload) ;
1686+
1687+ hshm. push_buffer ( 0 , mem_size, & data) . unwrap ( ) ;
1688+ let result: RawBytes = hshm. try_pop_buffer_into ( 0 , mem_size) . unwrap ( ) ;
1689+ assert_eq ! ( result. 0 , data) ;
1690+ }
1691+
1692+ #[ test]
1693+ fn malicious_flatbuffer_size_prefix ( ) {
1694+ let mem_size = 4096 ;
1695+ let mut hshm = make_buffer ( mem_size) ;
1696+
1697+ let payload = b"small" ;
1698+ let mut data = Vec :: new ( ) ;
1699+ data. extend_from_slice ( & ( payload. len ( ) as u32 ) . to_le_bytes ( ) ) ;
1700+ data. extend_from_slice ( payload) ;
1701+ hshm. push_buffer ( 0 , mem_size, & data) . unwrap ( ) ;
1702+
1703+ // Corrupt size prefix at element start (offset 8) to near u32::MAX.
1704+ hshm. write :: < u32 > ( 8 , 0xFFFF_FFFBu32 ) . unwrap ( ) ; // +4 = 0xFFFF_FFFF
1705+
1706+ let result: Result < RawBytes > = hshm. try_pop_buffer_into ( 0 , mem_size) ;
1707+ let err_msg = format ! ( "{}" , result. unwrap_err( ) ) ;
1708+ assert ! (
1709+ err_msg. contains( "Corrupt buffer size prefix: flatbuffer claims 4294967295 bytes but the element slot is only 9 bytes" ) ,
1710+ "Unexpected error message: {}" ,
1711+ err_msg
1712+ ) ;
1713+ }
1714+
1715+ #[ test]
1716+ fn malicious_element_offset_too_small ( ) {
1717+ let mem_size = 4096 ;
1718+ let mut hshm = make_buffer ( mem_size) ;
1719+
1720+ let payload = b"test" ;
1721+ let mut data = Vec :: new ( ) ;
1722+ data. extend_from_slice ( & ( payload. len ( ) as u32 ) . to_le_bytes ( ) ) ;
1723+ data. extend_from_slice ( payload) ;
1724+ hshm. push_buffer ( 0 , mem_size, & data) . unwrap ( ) ;
1725+
1726+ // Corrupt back-pointer (offset 16) to 0 (before valid range).
1727+ hshm. write :: < u64 > ( 16 , 0u64 ) . unwrap ( ) ;
1728+
1729+ let result: Result < RawBytes > = hshm. try_pop_buffer_into ( 0 , mem_size) ;
1730+ let err_msg = format ! ( "{}" , result. unwrap_err( ) ) ;
1731+ assert ! (
1732+ err_msg. contains(
1733+ "Corrupt buffer back-pointer: element offset 0 is outside valid range [8, 24)"
1734+ ) ,
1735+ "Unexpected error message: {}" ,
1736+ err_msg
1737+ ) ;
1738+ }
1739+
1740+ #[ test]
1741+ fn malicious_element_offset_past_stack_pointer ( ) {
1742+ let mem_size = 4096 ;
1743+ let mut hshm = make_buffer ( mem_size) ;
1744+
1745+ let payload = b"test" ;
1746+ let mut data = Vec :: new ( ) ;
1747+ data. extend_from_slice ( & ( payload. len ( ) as u32 ) . to_le_bytes ( ) ) ;
1748+ data. extend_from_slice ( payload) ;
1749+ hshm. push_buffer ( 0 , mem_size, & data) . unwrap ( ) ;
1750+
1751+ // Corrupt back-pointer (offset 16) to 9999 (past stack pointer 24).
1752+ hshm. write :: < u64 > ( 16 , 9999u64 ) . unwrap ( ) ;
1753+
1754+ let result: Result < RawBytes > = hshm. try_pop_buffer_into ( 0 , mem_size) ;
1755+ let err_msg = format ! ( "{}" , result. unwrap_err( ) ) ;
1756+ assert ! (
1757+ err_msg. contains( "Corrupt buffer back-pointer: element offset 9999 is outside valid range [8, 24)" ) ,
1758+ "Unexpected error message: {}" ,
1759+ err_msg
1760+ ) ;
1761+ }
1762+
1763+ #[ test]
1764+ fn malicious_flatbuffer_size_off_by_one ( ) {
1765+ let mem_size = 4096 ;
1766+ let mut hshm = make_buffer ( mem_size) ;
1767+
1768+ let payload = b"abcd" ;
1769+ let mut data = Vec :: new ( ) ;
1770+ data. extend_from_slice ( & ( payload. len ( ) as u32 ) . to_le_bytes ( ) ) ;
1771+ data. extend_from_slice ( payload) ;
1772+ hshm. push_buffer ( 0 , mem_size, & data) . unwrap ( ) ;
1773+
1774+ // Corrupt size prefix: claim 5 bytes (total 9), exceeding the 8-byte slot.
1775+ hshm. write :: < u32 > ( 8 , 5u32 ) . unwrap ( ) ; // fb_buffer_size = 5 + 4 = 9
1776+
1777+ let result: Result < RawBytes > = hshm. try_pop_buffer_into ( 0 , mem_size) ;
1778+ let err_msg = format ! ( "{}" , result. unwrap_err( ) ) ;
1779+ assert ! (
1780+ err_msg. contains( "Corrupt buffer size prefix: flatbuffer claims 9 bytes but the element slot is only 8 bytes" ) ,
1781+ "Unexpected error message: {}" ,
1782+ err_msg
1783+ ) ;
1784+ }
1785+ }
1786+
16341787 #[ cfg( target_os = "linux" ) ]
16351788 mod guard_page_crash_test {
16361789 use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , SharedMemory } ;
0 commit comments