mirror of
https://github.com/safing/portmaster.git
synced 2026-05-20 20:40:36 +00:00
fix(kext): potentially fix BSOD by heap-allocating WFP transport send params
FwpsInjectTransportSendAsync1 dereferences FWPS_TRANSPORT_SEND_PARAMS1 (and remote_address within it) asynchronously after the callsite returns. The params were stack-allocated, so WFP may have accessed freed stack memory at DISPATCH_LEVEL, potentially causing PAGE_FAULT_IN_NONPAGED_AREA or DRIVER_IRQL_NOT_LESS_OR_EQUAL BSODs. Candidate fix: embed send_params in TransportPacketList, box the whole struct before calling the inject API, and populate send_params (remote_address points into boxed remote_ip) only after boxing so all pointers are into stable non-paged heap memory. Introduce free_transport_packet as the WFP completion callback that drops Box<TransportPacketList> once injection is complete. https://github.com/safing/portmaster-shadow/issues/38
This commit is contained in:
@@ -32,6 +32,11 @@ pub struct TransportPacketList {
|
||||
inbound: bool,
|
||||
interface_index: u32,
|
||||
sub_interface_index: u32,
|
||||
// send_params and remote_ip must outlive inject_packet_list_transport
|
||||
// because FwpsInjectTransportSendAsync1 may read them after the function returns.
|
||||
// Storing send_params here ensures it lives on the heap inside Box<TransportPacketList>
|
||||
// until the WFP completion callback (free_transport_packet) drops it.
|
||||
send_params: FWPS_TRANSPORT_SEND_PARAMS1,
|
||||
}
|
||||
|
||||
pub struct InjectInfo {
|
||||
@@ -100,7 +105,7 @@ impl Injector {
|
||||
sub_interface_index: u32,
|
||||
) -> TransportPacketList {
|
||||
let mut control_data = None;
|
||||
if let Some(cd) = callout_data.get_control_data() {
|
||||
if let Some(cd) = callout_data.get_control_data() {
|
||||
control_data = Some(cd);
|
||||
}
|
||||
let mut remote_ip: [u8; 16] = [0; 16];
|
||||
@@ -122,6 +127,8 @@ impl Injector {
|
||||
inbound,
|
||||
interface_index,
|
||||
sub_interface_index,
|
||||
// Populated with valid pointers in inject_packet_list_transport after boxing.
|
||||
send_params: unsafe { MaybeUninit::zeroed().assume_init() },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,34 +140,37 @@ impl Injector {
|
||||
if self.transport_inject_handle == INVALID_HANDLE_VALUE {
|
||||
return Err("failed to inject packet: invalid handle value".to_string());
|
||||
}
|
||||
// Box the entire packet_list so that remote_ip and send_params
|
||||
// are heap-allocated. Their addresses remain stable until free_transport_packet
|
||||
// drops the Box after WFP calls the completion callback.
|
||||
let mut boxed = Box::new(packet_list);
|
||||
let raw_nbl = boxed.net_buffer_list.nbl;
|
||||
|
||||
unsafe {
|
||||
// Populate send_params with pointers into the boxed struct.
|
||||
// These addresses are stable because the Box will not move until freed.
|
||||
let mut control_data_length = 0;
|
||||
let control_data = match &packet_list.control_data {
|
||||
let control_data: *mut c_void = match &boxed.control_data {
|
||||
Some(cd) => {
|
||||
control_data_length = cd.len();
|
||||
cd.as_ptr().cast()
|
||||
cd.as_ptr() as *mut c_void
|
||||
}
|
||||
None => core::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let mut send_params = FWPS_TRANSPORT_SEND_PARAMS1 {
|
||||
remote_address: &packet_list.remote_ip as _,
|
||||
remote_scope_id: packet_list.remote_scope_id,
|
||||
control_data: control_data as _,
|
||||
boxed.send_params = FWPS_TRANSPORT_SEND_PARAMS1 {
|
||||
remote_address: boxed.remote_ip.as_ptr(),
|
||||
remote_scope_id: boxed.remote_scope_id,
|
||||
control_data,
|
||||
control_data_length: control_data_length as u32,
|
||||
header_include_header: core::ptr::null_mut(),
|
||||
header_include_header_length: 0,
|
||||
};
|
||||
let address_family = if packet_list.ipv6 { AF_INET6 } else { AF_INET };
|
||||
|
||||
let net_buffer_list = packet_list.net_buffer_list;
|
||||
// Escape the stack. Packet buffer should be valid until the packet is injected.
|
||||
let boxed_nbl = Box::new(net_buffer_list);
|
||||
let raw_nbl = boxed_nbl.nbl;
|
||||
let raw_ptr = Box::into_raw(boxed_nbl);
|
||||
let address_family = if boxed.ipv6 { AF_INET6 } else { AF_INET };
|
||||
let raw_ptr = Box::into_raw(boxed);
|
||||
|
||||
// Inject
|
||||
let status = if packet_list.inbound {
|
||||
// Inject. Context is *mut TransportPacketList; freed by free_transport_packet.
|
||||
let status = if (*raw_ptr).inbound {
|
||||
FwpsInjectTransportReceiveAsync0(
|
||||
self.transport_inject_handle,
|
||||
core::ptr::null_mut(),
|
||||
@@ -168,23 +178,23 @@ impl Injector {
|
||||
0,
|
||||
address_family,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
packet_list.interface_index,
|
||||
packet_list.sub_interface_index,
|
||||
(*raw_ptr).interface_index,
|
||||
(*raw_ptr).sub_interface_index,
|
||||
raw_nbl,
|
||||
free_packet,
|
||||
free_transport_packet,
|
||||
raw_ptr as _,
|
||||
)
|
||||
} else {
|
||||
FwpsInjectTransportSendAsync1(
|
||||
self.transport_inject_handle,
|
||||
core::ptr::null_mut(),
|
||||
packet_list.endpoint_handle,
|
||||
(*raw_ptr).endpoint_handle,
|
||||
0,
|
||||
&mut send_params,
|
||||
&mut (*raw_ptr).send_params,
|
||||
address_family,
|
||||
UNSPECIFIED_COMPARTMENT_ID,
|
||||
raw_nbl,
|
||||
free_packet,
|
||||
free_transport_packet,
|
||||
raw_ptr as _,
|
||||
)
|
||||
};
|
||||
@@ -344,3 +354,21 @@ unsafe extern "C" fn free_packet(
|
||||
}
|
||||
_ = Box::from_raw(context as *mut NetBufferList);
|
||||
}
|
||||
|
||||
/// Completion callback for transport inject paths (both inbound and outbound).
|
||||
/// The context is a `Box<TransportPacketList>` cast to `*mut c_void`.
|
||||
/// Dropping it also correctly drops the inner `NetBufferList`.
|
||||
unsafe extern "C" fn free_transport_packet(
|
||||
context: *mut c_void,
|
||||
net_buffer_list: *mut NET_BUFFER_LIST,
|
||||
_dispatch_level: bool,
|
||||
) {
|
||||
if let Some(nbl) = net_buffer_list.as_ref() {
|
||||
if let Err(err) = check_ntstatus(nbl.Status) {
|
||||
crate::err!("inject status: {}", err);
|
||||
} else {
|
||||
crate::dbg!("inject status: Ok");
|
||||
}
|
||||
}
|
||||
_ = Box::from_raw(context as *mut TransportPacketList);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user