Skip to content

Commit b25daad

Browse files
slin1237eternally-z
authored andcommitted
[model-gateway] fix WASM unbounded request/response body read vuln (sgl-project#14612)
1 parent 7d08f37 commit b25daad

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

sgl-model-gateway/src/middleware.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,8 @@ pub async fn wasm_middleware(
612612
let method = request.method().clone();
613613
let uri = request.uri().clone();
614614
let mut headers = request.headers().clone();
615-
let body_bytes = match axum::body::to_bytes(request.into_body(), usize::MAX).await {
615+
let max_body_size = wasm_manager.get_max_body_size();
616+
let body_bytes = match axum::body::to_bytes(request.into_body(), max_body_size).await {
616617
Ok(bytes) => bytes.to_vec(),
617618
Err(e) => {
618619
error!("Failed to read request body: {}", e);
@@ -708,7 +709,7 @@ pub async fn wasm_middleware(
708709
// Extract response data once before processing modules
709710
let mut status = response.status();
710711
let mut headers = response.headers().clone();
711-
let mut body_bytes = match axum::body::to_bytes(response.into_body(), usize::MAX).await {
712+
let mut body_bytes = match axum::body::to_bytes(response.into_body(), max_body_size).await {
712713
Ok(bytes) => bytes.to_vec(),
713714
Err(e) => {
714715
error!("Failed to read response body: {}", e);

sgl-model-gateway/src/wasm/config.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub struct WasmRuntimeConfig {
1717
pub thread_pool_size: usize,
1818
/// Maximum number of modules to cache per worker
1919
pub module_cache_size: usize,
20+
/// Maximum HTTP body size in bytes for middleware request/response processing
21+
pub max_body_size: usize,
2022
}
2123

2224
impl Default for WasmRuntimeConfig {
@@ -32,6 +34,7 @@ impl Default for WasmRuntimeConfig {
3234
max_stack_size: 1024 * 1024, // 1MB
3335
thread_pool_size: default_thread_pool_size, // based on cpu count
3436
module_cache_size: 10, // Cache up to 10 modules per worker
37+
max_body_size: 10 * 1024 * 1024, // 10MB
3538
}
3639
}
3740
}
@@ -82,6 +85,14 @@ impl WasmRuntimeConfig {
8285
return Err("module_cache_size cannot exceed 1000".to_string());
8386
}
8487

88+
// Validate max_body_size
89+
if self.max_body_size == 0 {
90+
return Err("max_body_size cannot be 0".to_string());
91+
}
92+
if self.max_body_size > 100 * 1024 * 1024 {
93+
return Err("max_body_size cannot exceed 100MB".to_string());
94+
}
95+
8596
Ok(())
8697
}
8798

@@ -92,13 +103,15 @@ impl WasmRuntimeConfig {
92103
max_stack_size: usize,
93104
thread_pool_size: usize,
94105
module_cache_size: usize,
106+
max_body_size: usize,
95107
) -> Result<Self, String> {
96108
let config = Self {
97109
max_memory_pages,
98110
max_execution_time_ms,
99111
max_stack_size,
100112
thread_pool_size,
101113
module_cache_size,
114+
max_body_size,
102115
};
103116
config.validate()?;
104117
Ok(config)
@@ -122,7 +135,7 @@ mod tests {
122135

123136
#[test]
124137
fn test_config_new_with_validation() {
125-
let config = WasmRuntimeConfig::new(1024, 1000, 1024 * 1024, 2, 10);
138+
let config = WasmRuntimeConfig::new(1024, 1000, 1024 * 1024, 2, 10, 10 * 1024 * 1024);
126139
assert!(config.is_ok());
127140
}
128141

@@ -134,6 +147,7 @@ mod tests {
134147
max_stack_size: 1024 * 1024,
135148
thread_pool_size: 2,
136149
module_cache_size: 10,
150+
max_body_size: 10 * 1024 * 1024,
137151
};
138152
let result = config.validate();
139153
assert!(result.is_err());
@@ -148,6 +162,7 @@ mod tests {
148162
max_stack_size: 1024 * 1024,
149163
thread_pool_size: 2,
150164
module_cache_size: 10,
165+
max_body_size: 10 * 1024 * 1024,
151166
};
152167
let result = config.validate();
153168
assert!(result.is_err());
@@ -164,6 +179,7 @@ mod tests {
164179
max_stack_size: 1024 * 1024,
165180
thread_pool_size: 2,
166181
module_cache_size: 10,
182+
max_body_size: 10 * 1024 * 1024,
167183
};
168184
let result = config.validate();
169185
assert!(result.is_err());
@@ -180,6 +196,7 @@ mod tests {
180196
max_stack_size: 1024 * 1024,
181197
thread_pool_size: 2,
182198
module_cache_size: 10,
199+
max_body_size: 10 * 1024 * 1024,
183200
};
184201
let result = config.validate();
185202
assert!(result.is_err());
@@ -196,6 +213,7 @@ mod tests {
196213
max_stack_size: 32 * 1024, // Less than 64KB
197214
thread_pool_size: 2,
198215
module_cache_size: 10,
216+
max_body_size: 10 * 1024 * 1024,
199217
};
200218
let result = config.validate();
201219
assert!(result.is_err());
@@ -212,6 +230,7 @@ mod tests {
212230
max_stack_size: 17 * 1024 * 1024, // Exceeds 16MB
213231
thread_pool_size: 2,
214232
module_cache_size: 10,
233+
max_body_size: 10 * 1024 * 1024,
215234
};
216235
let result = config.validate();
217236
assert!(result.is_err());
@@ -228,6 +247,7 @@ mod tests {
228247
max_stack_size: 1024 * 1024,
229248
thread_pool_size: 0,
230249
module_cache_size: 10,
250+
max_body_size: 10 * 1024 * 1024,
231251
};
232252
let result = config.validate();
233253
assert!(result.is_err());
@@ -242,6 +262,7 @@ mod tests {
242262
max_stack_size: 1024 * 1024,
243263
thread_pool_size: 129, // Exceeds 128
244264
module_cache_size: 10,
265+
max_body_size: 10 * 1024 * 1024,
245266
};
246267
let result = config.validate();
247268
assert!(result.is_err());
@@ -258,6 +279,7 @@ mod tests {
258279
max_stack_size: 1024 * 1024,
259280
thread_pool_size: 2,
260281
module_cache_size: 0,
282+
max_body_size: 10 * 1024 * 1024,
261283
};
262284
let result = config.validate();
263285
assert!(result.is_err());
@@ -274,6 +296,7 @@ mod tests {
274296
max_stack_size: 1024 * 1024,
275297
thread_pool_size: 2,
276298
module_cache_size: 1001, // Exceeds 1000
299+
max_body_size: 10 * 1024 * 1024,
277300
};
278301
let result = config.validate();
279302
assert!(result.is_err());
@@ -290,8 +313,41 @@ mod tests {
290313
max_stack_size: 1024 * 1024,
291314
thread_pool_size: 2,
292315
module_cache_size: 10,
316+
max_body_size: 10 * 1024 * 1024,
293317
};
294318
// 1024 pages * 64KB = 64MB
295319
assert_eq!(config.get_total_memory_bytes(), 64 * 1024 * 1024);
296320
}
321+
322+
#[test]
323+
fn test_validation_max_body_size_zero() {
324+
let config = WasmRuntimeConfig {
325+
max_memory_pages: 1024,
326+
max_execution_time_ms: 1000,
327+
max_stack_size: 1024 * 1024,
328+
thread_pool_size: 2,
329+
module_cache_size: 10,
330+
max_body_size: 0,
331+
};
332+
let result = config.validate();
333+
assert!(result.is_err());
334+
assert!(result.unwrap_err().contains("max_body_size cannot be 0"));
335+
}
336+
337+
#[test]
338+
fn test_validation_max_body_size_too_large() {
339+
let config = WasmRuntimeConfig {
340+
max_memory_pages: 1024,
341+
max_execution_time_ms: 1000,
342+
max_stack_size: 1024 * 1024,
343+
thread_pool_size: 2,
344+
module_cache_size: 10,
345+
max_body_size: 101 * 1024 * 1024, // Exceeds 100MB
346+
};
347+
let result = config.validate();
348+
assert!(result.is_err());
349+
assert!(result
350+
.unwrap_err()
351+
.contains("max_body_size cannot exceed 100MB"));
352+
}
297353
}

sgl-model-gateway/src/wasm/module_manager.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ impl WasmModuleManager {
128128
&self.runtime
129129
}
130130

131+
/// Get the configured maximum body size for HTTP request/response processing
132+
pub fn get_max_body_size(&self) -> usize {
133+
self.runtime.get_config().max_body_size
134+
}
135+
131136
/// Execute WASM module using WebAssembly component model based on attach_point
132137
pub async fn execute_module_interface(
133138
&self,

0 commit comments

Comments
 (0)