Skip to content

Commit e428464

Browse files
committed
fix(vmm): reject conflicting host port mappings
1 parent 0856a06 commit e428464

1 file changed

Lines changed: 62 additions & 1 deletion

File tree

dstack/vmm/src/main_service.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,31 @@ pub fn resolve_gpus(gpu_cfg: &rpc::GpuConfig) -> Result<GpuConfig> {
160160
}
161161
}
162162

163+
fn port_mappings_conflict(left: &PortMapping, right: &PortMapping) -> bool {
164+
left.protocol.as_str() == right.protocol.as_str()
165+
&& left.from == right.from
166+
&& (left.address == right.address
167+
|| left.address.is_unspecified()
168+
|| right.address.is_unspecified())
169+
}
170+
171+
fn validate_unique_port_mappings(mappings: &[PortMapping]) -> Result<()> {
172+
for (index, mapping) in mappings.iter().enumerate() {
173+
if mappings[..index]
174+
.iter()
175+
.any(|other| port_mappings_conflict(mapping, other))
176+
{
177+
bail!(
178+
"duplicate host port mapping: {} {}:{}",
179+
mapping.protocol.as_str(),
180+
mapping.address,
181+
mapping.from
182+
);
183+
}
184+
}
185+
Ok(())
186+
}
187+
163188
// Shared function to create manifest from VM configuration
164189
pub fn create_manifest_from_vm_config(
165190
request: VmConfiguration,
@@ -194,6 +219,7 @@ pub fn create_manifest_from_vm_config(
194219
})
195220
})
196221
.collect::<Result<Vec<_>>>()?;
222+
validate_unique_port_mappings(&port_map)?;
197223

198224
let app_id = match &request.app_id {
199225
Some(id) => id.strip_prefix("0x").unwrap_or(id).to_lowercase(),
@@ -398,6 +424,38 @@ fn networks_from_vm_config(
398424
}
399425

400426
impl RpcHandler {
427+
fn validate_port_mapping_conflicts(
428+
&self,
429+
vm_id: Option<&str>,
430+
mappings: &[PortMapping],
431+
) -> Result<()> {
432+
validate_unique_port_mappings(mappings)?;
433+
let state = self.app.lock();
434+
for vm in state.iter_vms() {
435+
if vm_id == Some(vm.config.manifest.id.as_str()) {
436+
continue;
437+
}
438+
for mapping in mappings {
439+
if vm
440+
.config
441+
.manifest
442+
.port_map
443+
.iter()
444+
.any(|existing| port_mappings_conflict(mapping, existing))
445+
{
446+
bail!(
447+
"host port mapping conflicts with VM {}: {} {}:{}",
448+
vm.config.manifest.id,
449+
mapping.protocol.as_str(),
450+
mapping.address,
451+
mapping.from
452+
);
453+
}
454+
}
455+
}
456+
Ok(())
457+
}
458+
401459
fn resolve_gpus(&self, gpu_cfg: &rpc::GpuConfig) -> Result<GpuConfig> {
402460
resolve_gpus_with_config(gpu_cfg, &self.app.config.cvm)
403461
}
@@ -461,6 +519,7 @@ impl RpcHandler {
461519
impl VmmRpc for RpcHandler {
462520
async fn create_vm(self, request: VmConfiguration) -> Result<Id> {
463521
let manifest = create_manifest_from_vm_config(request.clone(), &self.app.config.cvm)?;
522+
self.validate_port_mapping_conflicts(None, &manifest.port_map)?;
464523
let id = manifest.id.clone();
465524
let app_id = manifest.app_id.clone();
466525
let vm_work_dir = self.app.work_dir(&id);
@@ -590,7 +649,7 @@ impl VmmRpc for RpcHandler {
590649
manifest.no_tee = no_tee;
591650
}
592651
if request.update_ports {
593-
manifest.port_map = request
652+
let port_map = request
594653
.ports
595654
.iter()
596655
.map(|p| {
@@ -602,6 +661,8 @@ impl VmmRpc for RpcHandler {
602661
})
603662
})
604663
.collect::<Result<Vec<_>>>()?;
664+
self.validate_port_mapping_conflicts(Some(&request.id), &port_map)?;
665+
manifest.port_map = port_map;
605666
}
606667
if request.update_kms_urls {
607668
manifest.kms_urls = request.kms_urls.clone();

0 commit comments

Comments
 (0)