diff --git a/netstack/src/link/ethernet.rs b/netstack/src/link/ethernet.rs index 21a90be99b..b52effbceb 100644 --- a/netstack/src/link/ethernet.rs +++ b/netstack/src/link/ethernet.rs @@ -469,23 +469,29 @@ impl EthernetLink { return; }; - let (target, mut tries, mut silent_until) = match self.ndp_state { + // Extract target. Do NOT destructure tries/silent_until by value + // — the recursive call into drop_waiting_packets_v6 can modify + // self.ndp_state and a later write-back would clobber that + // update. We read tries/silent_until from the live state right + // before we write it back. + let target = match self.ndp_state { NdpState::Discovered => return, - NdpState::Discovering { - target, - tries, - silent_until, - } => (target, tries, silent_until), + NdpState::Discovering { target, .. } => target, }; - if silent_until > now { - return; - } - - if tries >= NDP_MAX_TRIES { - self.drop_waiting_packets_v6(target); - self.ndp_state = NdpState::Discovered; - return; + { + let (tries, silent_until) = match self.ndp_state { + NdpState::Discovering { tries, silent_until, .. } => (tries, silent_until), + NdpState::Discovered => return, + }; + if silent_until > now { + return; + } + if tries >= NDP_MAX_TRIES { + self.drop_waiting_packets_v6(target); + self.ndp_state = NdpState::Discovered; + return; + } } let snmc = solicited_node_multicast(&target); @@ -502,6 +508,13 @@ impl EthernetLink { icmp.emit(&src_addr, &dst_addr, &mut icmp_pkt, &ChecksumCapabilities::ignored()); let icmp_len = icmp.buffer_len(); + // Read the current state (which may have been updated by a + // recursive call into drop_waiting_packets_v6) and update + // incrementally. This avoids overwriting a recursive update. + let (mut tries, mut silent_until) = match self.ndp_state { + NdpState::Discovering { tries, silent_until, .. } => (tries, silent_until), + NdpState::Discovered => return, + }; tries += 1; silent_until = now + Self::NDP_SILENCE_TIME; self.ndp_state = NdpState::Discovering { diff --git a/netstack/src/observer.rs b/netstack/src/observer.rs index 78d12a59e3..bb730a27f9 100644 --- a/netstack/src/observer.rs +++ b/netstack/src/observer.rs @@ -42,7 +42,7 @@ impl CaptureFilter { return true; } if packet.len() < 20 { - return true; // too short to filter, capture anyway + return false; // too short to determine protocol/port } let version = packet[0] >> 4; let (proto, src_port_offset, dst_port_offset) = match version { @@ -147,8 +147,12 @@ impl Observer { if buf.len() >= self.max_packets { buf.remove(0); } - let mut copy = Vec::with_capacity(packet.len().min(MAX_CAPTURE_BYTES / self.max_packets)); - copy.extend_from_slice(packet); + // Truncate to per-packet size limit so a single large packet + // can't exhaust the total capture buffer. + let per_packet_limit = MAX_CAPTURE_BYTES / self.max_packets.max(1); + let truncated_len = packet.len().min(per_packet_limit); + let mut copy = Vec::with_capacity(truncated_len); + copy.extend_from_slice(&packet[..truncated_len]); buf.push(copy); self.total_captured.fetch_add(1, Ordering::Relaxed); } diff --git a/netstack/src/scheme/socket.rs b/netstack/src/scheme/socket.rs index e73cb28777..8028710c4d 100644 --- a/netstack/src/scheme/socket.rs +++ b/netstack/src/scheme/socket.rs @@ -675,32 +675,33 @@ where let socket_handle = scheme_file.socket_handle(); let mut socket_set = self.socket_set.borrow_mut(); - let socket = socket_set.get::(socket_handle); - let _ = socket.close_file(&scheme_file, &mut self.scheme_data); - - let remove = match self.ref_counts.entry(socket_handle) { + // Compute refcount change WITHOUT calling close_file yet. + // The port release (inside close_file) must happen exactly + // once when the LAST file referencing this socket is + // closed — otherwise a dup'd socket's port would be + // double-freed. + let new_count = match self.ref_counts.entry(socket_handle) { Entry::Vacant(_) => { warn!("Closing a socket_handle with no ref"); - true + 0 } Entry::Occupied(mut e) => { - if *e.get() == 0 { - warn!("Closing a socket_handle with no ref"); + let count = *e.get(); + if count <= 1 { e.remove(); - true + 0 } else { *e.get_mut() -= 1; - if *e.get() == 0 { - e.remove(); - true - } else { - false - } + count - 1 } } }; - if remove { + // Only close the socket (which releases the port) when + // the last reference is gone. + if new_count == 0 { + let socket = socket_set.get::(socket_handle); + let _ = socket.close_file(&scheme_file, &mut self.scheme_data); socket_set.remove(socket_handle); } } diff --git a/netstack/src/scheme/tcp.rs b/netstack/src/scheme/tcp.rs index 4c2c34aa78..a84962154a 100644 --- a/netstack/src/scheme/tcp.rs +++ b/netstack/src/scheme/tcp.rs @@ -131,14 +131,26 @@ impl<'a> SchemeSocket for TcpSocket<'a> { port: local_endpoint.port, }; + let allocated_port = local_endpoint.port; + let allocated_ephemeral = local_endpoint_addr.is_none(); trace!("Connecting tcp {} {}", local_endpoint, remote_endpoint); - tcp_socket + if let Err(e) = tcp_socket .connect( context.iface.borrow_mut().context(), IpEndpoint::new(remote_endpoint.addr.unwrap(), remote_endpoint.port), local_endpoint, ) - .map_err(|_| SyscallError::new(syscall::EIO))?; + .map_err(|_| SyscallError::new(syscall::EIO)) + { + // Connect failed. Release the auto-allocated port (if + // any) so the next attempt can use it. Explicit user- + // provided ports are released by on_close when the + // last file is dropped. + if allocated_ephemeral { + port_set.release_port(allocated_port); + } + return Err(e); + } None } else { trace!("Listening tcp {}", local_endpoint);