Skip to content

Commit ff72bc6

Browse files
benhilliserfrimod
andauthored
netvsp: handle invalid rndis packets (#1590) (#1646)
Netvsp driver does not accept an RNDIS header from Linux NetVSC, if that RNDIS header crosses a 4KB page boundary. handle_rndis_packet_message will return WorkerError::RndisMessageTooSmall. The error bubbles up out of process_ring_buffer causing netvsp to stop processing packets. RNDIS headers crossing the page boundary has been a longstanding issue with the Linux NetVSC driver, but it has become more frequent recently with the Linux v6.14 kernel due to a recent upstream commit in the Linux networking subsystem that may have slightly affected the way SKBs are constructed. Making the OpenHCL Netvsp driver more resilient (and more like the Windows Netvsp driver) by adding logic to log the error and complete the packet with a Failure status. Cherry-pick of #1590 Co-authored-by: erfrimod <31358361+erfrimod@users.noreply.github.com>
1 parent 72f8b94 commit ff72bc6

File tree

2 files changed

+166
-12
lines changed

2 files changed

+166
-12
lines changed

vm/devices/net/netvsp/src/lib.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ struct QueueStats {
454454
struct PendingTxCompletion {
455455
transaction_id: u64,
456456
tx_id: Option<TxId>,
457+
status: protocol::Status,
457458
}
458459

459460
#[derive(Clone, Copy)]
@@ -934,11 +935,14 @@ impl ActiveState {
934935
// This shouldn't be referenced, but set it in case it is in the future.
935936
active.pending_tx_packets[id.0 as usize].transaction_id = transaction_id;
936937
}
938+
// Save/Restore does not preserve the status of pending tx completions,
939+
// completing any pending completions with 'success' to avoid making changes to saved_state.
937940
active
938941
.pending_tx_completions
939942
.push_back(PendingTxCompletion {
940943
transaction_id,
941944
tx_id,
945+
status: protocol::Status::SUCCESS,
942946
});
943947
}
944948
Ok(active)
@@ -4956,7 +4960,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
49564960
assert!(tx_packet.pending_packet_count > 0);
49574961
tx_packet.pending_packet_count -= 1;
49584962
if tx_packet.pending_packet_count == 0 {
4959-
self.complete_tx_packet(state, id)?;
4963+
self.complete_tx_packet(state, id, protocol::Status::SUCCESS)?;
49604964
}
49614965
}
49624966

@@ -4974,6 +4978,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
49744978
Some(PendingTxCompletion {
49754979
transaction_id: inflight.transaction_id,
49764980
tx_id: Some(TxId(id as u32)),
4981+
status: protocol::Status::SUCCESS,
49774982
})
49784983
} else {
49794984
None
@@ -5070,8 +5075,19 @@ impl<T: 'static + RingMem> NetChannel<T> {
50705075
PacketData::RndisPacket(_) => {
50715076
assert!(data.tx_segments.is_empty());
50725077
let id = state.free_tx_packets.pop().unwrap();
5073-
let num_packets =
5074-
self.handle_rndis(buffers, id, state, &packet, &mut data.tx_segments)?;
5078+
let result: Result<usize, WorkerError> =
5079+
self.handle_rndis(buffers, id, state, &packet, &mut data.tx_segments);
5080+
let num_packets = match result {
5081+
Ok(num_packets) => num_packets,
5082+
Err(err) => {
5083+
tracelimit::error_ratelimited!(
5084+
err = &err as &dyn std::error::Error,
5085+
"failed to handle RNDIS packet"
5086+
);
5087+
self.complete_tx_packet(state, id, protocol::Status::FAILURE)?;
5088+
continue;
5089+
}
5090+
};
50755091
total_packets += num_packets as u64;
50765092
state.pending_tx_packets[id.0 as usize].pending_packet_count += num_packets;
50775093

@@ -5082,7 +5098,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
50825098
state.stats.tx_stalled.increment();
50835099
}
50845100
} else {
5085-
self.complete_tx_packet(state, id)?;
5101+
self.complete_tx_packet(state, id, protocol::Status::SUCCESS)?;
50865102
}
50875103
}
50885104
PacketData::RndisPacketComplete(_completion) => {
@@ -5222,7 +5238,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
52225238
}
52235239

52245240
if state.pending_tx_packets[id.0 as usize].pending_packet_count == 0 {
5225-
self.complete_tx_packet(state, id)?;
5241+
self.complete_tx_packet(state, id, protocol::Status::SUCCESS)?;
52265242
}
52275243

52285244
Ok(packets_sent)
@@ -5276,12 +5292,14 @@ impl<T: 'static + RingMem> NetChannel<T> {
52765292
Ok(num_packets)
52775293
}
52785294

5279-
fn try_send_tx_packet(&mut self, transaction_id: u64) -> Result<bool, WorkerError> {
5295+
fn try_send_tx_packet(
5296+
&mut self,
5297+
transaction_id: u64,
5298+
status: protocol::Status,
5299+
) -> Result<bool, WorkerError> {
52805300
let message = self.message(
52815301
protocol::MESSAGE1_TYPE_SEND_RNDIS_PACKET_COMPLETE,
5282-
protocol::Message1SendRndisPacketComplete {
5283-
status: protocol::Status::SUCCESS,
5284-
},
5302+
protocol::Message1SendRndisPacketComplete { status },
52855303
);
52865304
let result = self.queue.split().1.try_write(&queue::OutgoingPacket {
52875305
transaction_id,
@@ -5302,7 +5320,7 @@ impl<T: 'static + RingMem> NetChannel<T> {
53025320
fn send_pending_packets(&mut self, state: &mut ActiveState) -> Result<bool, WorkerError> {
53035321
let mut did_some_work = false;
53045322
while let Some(pending) = state.pending_tx_completions.front() {
5305-
if !self.try_send_tx_packet(pending.transaction_id)? {
5323+
if !self.try_send_tx_packet(pending.transaction_id, pending.status)? {
53065324
return Ok(did_some_work);
53075325
}
53085326
did_some_work = true;
@@ -5317,17 +5335,25 @@ impl<T: 'static + RingMem> NetChannel<T> {
53175335
Ok(did_some_work)
53185336
}
53195337

5320-
fn complete_tx_packet(&mut self, state: &mut ActiveState, id: TxId) -> Result<(), WorkerError> {
5338+
fn complete_tx_packet(
5339+
&mut self,
5340+
state: &mut ActiveState,
5341+
id: TxId,
5342+
status: protocol::Status,
5343+
) -> Result<(), WorkerError> {
53215344
let tx_packet = &mut state.pending_tx_packets[id.0 as usize];
53225345
assert_eq!(tx_packet.pending_packet_count, 0);
5323-
if self.pending_send_size == 0 && self.try_send_tx_packet(tx_packet.transaction_id)? {
5346+
if self.pending_send_size == 0
5347+
&& self.try_send_tx_packet(tx_packet.transaction_id, status)?
5348+
{
53245349
tracing::trace!(id = id.0, "sent tx completion");
53255350
state.free_tx_packets.push(id);
53265351
} else {
53275352
tracing::trace!(id = id.0, "pended tx completion");
53285353
state.pending_tx_completions.push_back(PendingTxCompletion {
53295354
transaction_id: tx_packet.transaction_id,
53305355
tx_id: Some(id),
5356+
status,
53315357
});
53325358
}
53335359
Ok(())

vm/devices/net/netvsp/src/test.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,6 +2296,134 @@ async fn initialize_rndis_with_prev_vf_switch_data_path(driver: DefaultDriver) {
22962296
assert_eq!(endpoint_state.lock().use_vf.take().unwrap(), false);
22972297
}
22982298

2299+
#[async_test]
2300+
async fn rndis_handle_packet_errors(driver: DefaultDriver) {
2301+
let endpoint_state = TestNicEndpointState::new();
2302+
let endpoint = TestNicEndpoint::new(Some(endpoint_state.clone()));
2303+
let builder = Nic::builder();
2304+
let nic = builder.build(
2305+
&VmTaskDriverSource::new(SingleDriverBackend::new(driver.clone())),
2306+
Guid::new_random(),
2307+
Box::new(endpoint),
2308+
[1, 2, 3, 4, 5, 6].into(),
2309+
0,
2310+
);
2311+
2312+
let mut nic = TestNicDevice::new_with_nic(&driver, nic).await;
2313+
nic.start_vmbus_channel();
2314+
let mut channel = nic.connect_vmbus_channel().await;
2315+
channel
2316+
.initialize(0, protocol::NdisConfigCapabilities::new())
2317+
.await;
2318+
channel
2319+
.send_rndis_control_message(
2320+
rndisprot::MESSAGE_TYPE_INITIALIZE_MSG,
2321+
rndisprot::InitializeRequest {
2322+
request_id: 123,
2323+
major_version: rndisprot::MAJOR_VERSION,
2324+
minor_version: rndisprot::MINOR_VERSION,
2325+
max_transfer_size: 0,
2326+
},
2327+
&[],
2328+
)
2329+
.await;
2330+
2331+
let initialize_complete: rndisprot::InitializeComplete = channel
2332+
.read_rndis_control_message(rndisprot::MESSAGE_TYPE_INITIALIZE_CMPLT)
2333+
.await
2334+
.unwrap();
2335+
assert_eq!(initialize_complete.request_id, 123);
2336+
assert_eq!(initialize_complete.status, rndisprot::STATUS_SUCCESS);
2337+
assert_eq!(initialize_complete.major_version, rndisprot::MAJOR_VERSION);
2338+
assert_eq!(initialize_complete.minor_version, rndisprot::MINOR_VERSION);
2339+
2340+
// Not expecting an association packet because virtual function is not present
2341+
assert!(
2342+
channel
2343+
.read_with(|_| panic!("No packet expected"))
2344+
.await
2345+
.is_err()
2346+
);
2347+
2348+
assert_eq!(endpoint_state.lock().stop_endpoint_counter, 1);
2349+
2350+
// Send a packet with an invalid data offset.
2351+
// Expecting the channel to handle WorkerError::RndisMessageTooSmall
2352+
channel
2353+
.send_rndis_control_message_no_completion(
2354+
rndisprot::MESSAGE_TYPE_PACKET_MSG,
2355+
rndisprot::Packet {
2356+
data_offset: 7777,
2357+
data_length: 0,
2358+
oob_data_offset: 0,
2359+
oob_data_length: 0,
2360+
num_oob_data_elements: 0,
2361+
per_packet_info_offset: 0,
2362+
per_packet_info_length: 0,
2363+
vc_handle: 0,
2364+
reserved: 0,
2365+
},
2366+
&[],
2367+
)
2368+
.await;
2369+
2370+
// Expect a completion message with failure status.
2371+
channel
2372+
.read_with(|packet| match packet {
2373+
IncomingPacket::Completion(completion) => {
2374+
let mut reader = completion.reader();
2375+
let header: protocol::MessageHeader = reader.read_plain().unwrap();
2376+
assert_eq!(
2377+
header.message_type,
2378+
protocol::MESSAGE1_TYPE_SEND_RNDIS_PACKET_COMPLETE
2379+
);
2380+
let completion_data: protocol::Message1SendRndisPacketComplete =
2381+
reader.read_plain().unwrap();
2382+
assert_eq!(completion_data.status, protocol::Status::FAILURE);
2383+
}
2384+
_ => panic!("Unexpected packet"),
2385+
})
2386+
.await
2387+
.expect("completion message");
2388+
2389+
// Verify the channel is still processing packets by sending another (also invalid).
2390+
channel
2391+
.send_rndis_control_message_no_completion(
2392+
rndisprot::MESSAGE_TYPE_PACKET_MSG,
2393+
rndisprot::Packet {
2394+
data_offset: 8888,
2395+
data_length: 0,
2396+
oob_data_offset: 0,
2397+
oob_data_length: 0,
2398+
num_oob_data_elements: 0,
2399+
per_packet_info_offset: 0,
2400+
per_packet_info_length: 0,
2401+
vc_handle: 0,
2402+
reserved: 0,
2403+
},
2404+
&[],
2405+
)
2406+
.await;
2407+
2408+
channel
2409+
.read_with(|packet| match packet {
2410+
IncomingPacket::Completion(completion) => {
2411+
let mut reader = completion.reader();
2412+
let header: protocol::MessageHeader = reader.read_plain().unwrap();
2413+
assert_eq!(
2414+
header.message_type,
2415+
protocol::MESSAGE1_TYPE_SEND_RNDIS_PACKET_COMPLETE
2416+
);
2417+
let completion_data: protocol::Message1SendRndisPacketComplete =
2418+
reader.read_plain().unwrap();
2419+
assert_eq!(completion_data.status, protocol::Status::FAILURE);
2420+
}
2421+
_ => panic!("Unexpected packet"),
2422+
})
2423+
.await
2424+
.expect("completion message");
2425+
}
2426+
22992427
#[async_test]
23002428
async fn stop_start_with_vf(driver: DefaultDriver) {
23012429
let endpoint_state = TestNicEndpointState::new();

0 commit comments

Comments
 (0)