@@ -96,6 +96,15 @@ const MAX_FS_READ: u64 = 16 * 1024 * 1024;
9696/// Cap for `net_send`/`net_sendto` decoded payload (1 MiB).
9797const MAX_NET_SEND : usize = 1024 * 1024 ;
9898
99+ /// Cap for `fs_write`/`fs_write_bytes` payload to prevent guest-triggered OOM (16 MiB).
100+ const MAX_FS_WRITE : usize = 16 * 1024 * 1024 ;
101+
102+ /// Cap for `fs_truncate` length to prevent disk exhaustion (1 GiB).
103+ const MAX_TRUNCATE_LEN : u64 = 1024 * 1024 * 1024 ;
104+
105+ /// Cap for incoming dispatch payload size (64 MiB).
106+ const MAX_DISPATCH_PAYLOAD : usize = 64 * 1024 * 1024 ;
107+
99108/// Cap for `__hl_sleep` duration to prevent unbounded host-thread blocking (60 s).
100109const MAX_SLEEP_NS : u64 = 60_000_000_000 ;
101110
@@ -674,6 +683,12 @@ impl ToolRegistry {
674683 /// payload and result to stderr — useful when diagnosing
675684 /// guest/host protocol mismatches.
676685 pub fn dispatch ( & self , payload : & [ u8 ] ) -> Vec < u8 > {
686+ if payload. len ( ) > MAX_DISPATCH_PAYLOAD {
687+ return serde_json:: to_vec ( & serde_json:: json!( {
688+ "error" : format!( "payload too large: {} bytes (max {})" , payload. len( ) , MAX_DISPATCH_PAYLOAD )
689+ } ) )
690+ . unwrap_or_default ( ) ;
691+ }
677692 let debug = std:: env:: var ( "HL_DISPATCH_DEBUG" )
678693 . ok ( )
679694 . map ( |v| v == "1" )
@@ -1122,6 +1137,7 @@ fn dns_read_name(data: &[u8], pos: &mut usize) -> Option<String> {
11221137 let mut p = * pos;
11231138 let mut jumped = false ;
11241139 let mut jump_save = 0 ;
1140+ let mut hops = 0u8 ;
11251141 loop {
11261142 if p >= data. len ( ) {
11271143 return None ;
@@ -1132,10 +1148,13 @@ fn dns_read_name(data: &[u8], pos: &mut usize) -> Option<String> {
11321148 break ;
11331149 }
11341150 if ( len & 0xC0 ) == 0xC0 {
1135- // Pointer
11361151 if p + 1 >= data. len ( ) {
11371152 return None ;
11381153 }
1154+ hops += 1 ;
1155+ if hops > 128 {
1156+ return None ;
1157+ }
11391158 let offset = ( ( len & 0x3F ) << 8 ) | data[ p + 1 ] as usize ;
11401159 if !jumped {
11411160 jump_save = p + 2 ;
@@ -1658,6 +1677,13 @@ impl FsRouter {
16581677 let text = args[ "text" ]
16591678 . as_str ( )
16601679 . ok_or_else ( || anyhow ! ( "fs_write: missing 'text'" ) ) ?;
1680+ if text. len ( ) > MAX_FS_WRITE {
1681+ return Err ( anyhow ! (
1682+ "fs_write: payload too large ({} bytes, max {})" ,
1683+ text. len( ) ,
1684+ MAX_FS_WRITE
1685+ ) ) ;
1686+ }
16611687 let append = args[ "append" ] . as_bool ( ) . unwrap_or ( false ) ;
16621688 let ( fs, rel) = r. route ( path) ?;
16631689 let target = fs. resolve ( rel) ?;
@@ -1764,9 +1790,22 @@ impl FsRouter {
17641790 let data_b64 = args[ "data" ]
17651791 . as_str ( )
17661792 . ok_or_else ( || anyhow ! ( "fs_write_bytes: missing 'data'" ) ) ?;
1793+ if data_b64. len ( ) > MAX_FS_WRITE * 4 / 3 + 4 {
1794+ return Err ( anyhow ! (
1795+ "fs_write_bytes: payload too large (max {} decoded bytes)" ,
1796+ MAX_FS_WRITE
1797+ ) ) ;
1798+ }
17671799 let data = base64:: engine:: general_purpose:: STANDARD
17681800 . decode ( data_b64)
17691801 . map_err ( |e| anyhow ! ( "fs_write_bytes: bad base64: {}" , e) ) ?;
1802+ if data. len ( ) > MAX_FS_WRITE {
1803+ return Err ( anyhow ! (
1804+ "fs_write_bytes: decoded payload too large ({} bytes, max {})" ,
1805+ data. len( ) ,
1806+ MAX_FS_WRITE
1807+ ) ) ;
1808+ }
17701809 let offset = args[ "offset" ] . as_u64 ( ) ;
17711810 let append = args[ "append" ] . as_bool ( ) . unwrap_or ( false ) ;
17721811 let ( fs, rel) = r. route ( path) ?;
@@ -1797,6 +1836,13 @@ impl FsRouter {
17971836 let length = args[ "length" ]
17981837 . as_u64 ( )
17991838 . ok_or_else ( || anyhow ! ( "fs_truncate: missing 'length'" ) ) ?;
1839+ if length > MAX_TRUNCATE_LEN {
1840+ return Err ( anyhow ! (
1841+ "fs_truncate: length too large ({} bytes, max {})" ,
1842+ length,
1843+ MAX_TRUNCATE_LEN
1844+ ) ) ;
1845+ }
18001846 let ( fs, rel) = r. route ( path) ?;
18011847 let target = fs. resolve ( rel) ?;
18021848 let f = std:: fs:: OpenOptions :: new ( )
0 commit comments