Skip to content

Commit 545bbdd

Browse files
committed
feat: Enhance memory limit validation and resource management in WASM components
Signed-off-by: Jiaxiao Zhou <duibao55328@gmail.com>
1 parent ab235ec commit 545bbdd

File tree

4 files changed

+83
-24
lines changed

4 files changed

+83
-24
lines changed

crates/policy/src/types.rs

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,13 @@ impl CpuLimit {
262262
}
263263

264264
impl MemoryLimit {
265+
// Memory limit bounds: minimum 64KB, maximum 64GB (reasonable for WASM components)
266+
const MIN_MEMORY_BYTES: u64 = 64 * 1024; // 64KB
267+
const MAX_MEMORY_BYTES: u64 = 64u64 * 1024 * 1024 * 1024; // 64GB
268+
265269
/// Validate and convert memory limit to bytes
266270
pub fn to_bytes(&self) -> PolicyResult<u64> {
267-
match self {
271+
let bytes = match self {
268272
MemoryLimit::String(s) => {
269273
if s.is_empty() {
270274
bail!("Memory limit string cannot be empty");
@@ -287,16 +291,41 @@ impl MemoryLimit {
287291
.parse()
288292
.map_err(|_| anyhow::anyhow!("Invalid memory value: {}", s))?;
289293

294+
if value == 0 {
295+
bail!("Memory limit cannot be zero: {}", s);
296+
}
297+
290298
value
291299
.checked_mul(multiplier)
292-
.ok_or_else(|| anyhow::anyhow!("Memory value too large: {}", s))
300+
.ok_or_else(|| anyhow::anyhow!("Memory value too large: {}", s))?
293301
}
294302
MemoryLimit::Number(n) => {
303+
if *n == 0 {
304+
bail!("Memory limit cannot be zero");
305+
}
295306
// Assume legacy numeric values are in MB
296307
n.checked_mul(1024 * 1024)
297-
.ok_or_else(|| anyhow::anyhow!("Memory value too large: {}", n))
308+
.ok_or_else(|| anyhow::anyhow!("Memory value too large: {}", n))?
298309
}
310+
};
311+
312+
// Validate bounds
313+
if bytes < Self::MIN_MEMORY_BYTES {
314+
bail!(
315+
"Memory limit {} bytes is below minimum {} KB",
316+
bytes,
317+
Self::MIN_MEMORY_BYTES / 1024
318+
);
299319
}
320+
if bytes > Self::MAX_MEMORY_BYTES {
321+
bail!(
322+
"Memory limit {} bytes exceeds maximum {} GB",
323+
bytes,
324+
Self::MAX_MEMORY_BYTES / (1024 * 1024 * 1024)
325+
);
326+
}
327+
328+
Ok(bytes)
300329
}
301330
}
302331

@@ -735,13 +764,16 @@ mod tests {
735764
let memory_gi = MemoryLimit::String("2Gi".to_string());
736765
assert_eq!(memory_gi.to_bytes().unwrap(), 2 * 1024 * 1024 * 1024);
737766

738-
// Test Ti format
739-
let memory_ti = MemoryLimit::String("1Ti".to_string());
740-
assert_eq!(memory_ti.to_bytes().unwrap(), 1024u64 * 1024 * 1024 * 1024);
767+
// Test Gi format (larger value)
768+
let memory_gi_large = MemoryLimit::String("32Gi".to_string());
769+
assert_eq!(
770+
memory_gi_large.to_bytes().unwrap(),
771+
32u64 * 1024 * 1024 * 1024
772+
);
741773

742-
// Test plain bytes
743-
let memory_bytes = MemoryLimit::String("1024".to_string());
744-
assert_eq!(memory_bytes.to_bytes().unwrap(), 1024);
774+
// Test plain bytes (above minimum)
775+
let memory_bytes = MemoryLimit::String("131072".to_string()); // 128KB
776+
assert_eq!(memory_bytes.to_bytes().unwrap(), 131072);
745777

746778
// Test numeric format (legacy, assumes MB)
747779
let memory_numeric = MemoryLimit::Number(512);
@@ -756,6 +788,21 @@ mod tests {
756788

757789
let invalid_number = MemoryLimit::String("invalidMi".to_string());
758790
assert!(invalid_number.to_bytes().is_err());
791+
792+
// Test bounds validation - too small
793+
let too_small = MemoryLimit::String("32Ki".to_string()); // 32KB < 64KB minimum
794+
assert!(too_small.to_bytes().is_err());
795+
796+
// Test bounds validation - zero
797+
let zero_bytes = MemoryLimit::String("0".to_string());
798+
assert!(zero_bytes.to_bytes().is_err());
799+
800+
let zero_numeric = MemoryLimit::Number(0);
801+
assert!(zero_numeric.to_bytes().is_err());
802+
803+
// Test bounds validation - too large (128GB > 64GB maximum)
804+
let too_large = MemoryLimit::String("128Gi".to_string());
805+
assert!(too_large.to_bytes().is_err());
759806
}
760807

761808
#[test]

crates/wassette/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,11 @@ impl LifecycleManager {
490490
if resource_limiter.is_some() {
491491
store.limiter(|state: &mut WassetteWasiState<WasiState>| {
492492
// Extract the resource limiter from the inner state
493-
// We know it exists because we checked above
494-
state.inner.resource_limiter.as_mut().unwrap()
493+
state
494+
.inner
495+
.resource_limiter
496+
.as_mut()
497+
.expect("Resource limiter should be present - checked above")
495498
});
496499
}
497500

crates/wassette/src/policy_internal.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,18 @@ impl crate::LifecycleManager {
297297
.and_then(|v| v.as_str())
298298
.ok_or_else(|| anyhow!("Missing 'memory' field for resource permission"))?;
299299

300-
// Store as a custom rule with the specific structure expected by the policy system
301-
let resource_details = serde_json::json!({
302-
"resources": {
303-
"limits": {
304-
"memory": memory
305-
}
306-
}
307-
});
300+
// Create structured resource limits instead of hardcoded JSON
301+
let resource_limits = policy::ResourceLimits {
302+
limits: Some(policy::ResourceLimitValues::new(
303+
None,
304+
Some(policy::MemoryLimit::String(memory.to_string())),
305+
)),
306+
..Default::default()
307+
};
308+
309+
// Convert to JSON for storage in PermissionRule::Custom
310+
let resource_details = serde_json::to_value(resource_limits)
311+
.map_err(|e| anyhow!("Failed to serialize resource limits: {}", e))?;
308312
PermissionRule::Custom("resource".to_string(), resource_details)
309313
}
310314
other => {

crates/wassette/src/wasistate.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,11 +183,16 @@ pub fn create_wasi_state_template_from_policy(
183183
let preopened_dirs = extract_storage_permissions(policy, plugin_dir)?;
184184
let allowed_hosts = extract_allowed_hosts(policy);
185185
let memory_limit = extract_memory_limit(policy)?;
186-
let store_limits = memory_limit.map(|limit| {
187-
wasmtime::StoreLimitsBuilder::new()
188-
.memory_size(limit.try_into().unwrap_or(usize::MAX))
189-
.build()
190-
});
186+
let store_limits = memory_limit
187+
.map(|limit| -> anyhow::Result<wasmtime::StoreLimits> {
188+
let limit_usize = limit.try_into().map_err(|_| {
189+
anyhow::anyhow!("Memory limit {} too large for target architecture", limit)
190+
})?;
191+
Ok(wasmtime::StoreLimitsBuilder::new()
192+
.memory_size(limit_usize)
193+
.build())
194+
})
195+
.transpose()?;
191196

192197
Ok(WasiStateTemplate {
193198
network_perms,

0 commit comments

Comments
 (0)