review: NDP state corruption fix, port double-free fix, observer limits
CRITICAL/MEDIUM BUGS FIXED: 1. ethernet.rs send_ndp_solicit: state corruption via recursive call The function destructured self.ndp_state by value (target, tries, silent_until) at the start and wrote back at the end. But between destructuring and writing back, self.drop_waiting_packets_v6(target) could recursively call self.send_ndp_solicit(Instant::ZERO) for a different target. The recursive call updated self.ndp_state with the new target's state. When control returned to the original frame, the original write-back clobbered the recursive call's state, silently losing neighbor discovery for the new target. Fix: use scoped pattern matches to read target/tries/silent_until from the live state right before they are needed, so the recursive call's writes are preserved. This matches the pattern used by send_arp (which uses ref mut and is correct). 2. scheme/socket.rs on_close: port double-free close_file() was called before the refcount check, so the port was released on every close. For a dup'd socket, the second close tried to release the port again — double-free. Fix: compute the new refcount first, only call close_file and remove from socket_set when the count reaches 0 (last reference). 3. scheme/tcp.rs new_socket: port + socket leak on connect failure If get_port() succeeded but connect() later failed, the port was claimed and the socket was added to socket_set, but new_socket returned Err. The caller (open_inner) did not insert the file handle, so on_close was never called. Both the port and the socket slot leaked. Fix: when connect() fails, release the auto-allocated port before returning. Explicit user-provided ports are still released by on_close when the last file is dropped (preserves the existing on_close-based release). 4. observer.rs capture: per-packet size limit not enforced Vec::with_capacity only pre-allocates; extend_from_slice copies the full packet. The intended per-packet limit (MAX_CAPTURE_BYTES / max_packets = 256 bytes) was being bypassed, allowing a single 1500-byte packet to consume the full capture buffer. Fix: truncate to per_packet_limit before extending. 5. observer.rs capture: short packets bypassed filter Packets < 20 bytes returned true (capture anyway) regardless of the user's filter. A filter like 'tcp port 80' would capture all short packets, including non-TCP. Fix: return false (no match) for short packets. Filter semantics are now consistent: if a packet can't be matched, it's not captured. All 29 existing tests still pass.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user