@@ -39,7 +39,7 @@ use crate::devices::{DeviceError, report_net_event_fail};
3939use crate :: dumbo:: pdu:: arp:: ETH_IPV4_FRAME_LEN ;
4040use crate :: dumbo:: pdu:: ethernet:: { EthernetFrame , PAYLOAD_OFFSET } ;
4141use crate :: impl_device_type;
42- use crate :: logger:: { IncMetric , METRICS , error} ;
42+ use crate :: logger:: { IncMetric , METRICS , error, warn } ;
4343use crate :: mmds:: data_store:: Mmds ;
4444use crate :: mmds:: ns:: MmdsNetworkStack ;
4545use crate :: rate_limiter:: { BucketUpdate , RateLimiter , TokenType } ;
@@ -1032,24 +1032,12 @@ impl VirtioDevice for Net {
10321032 }
10331033
10341034 fn write_config ( & mut self , offset : u64 , data : & [ u8 ] ) {
1035- let config_space_bytes = self . config_space . as_mut_slice ( ) ;
1036- let start = usize:: try_from ( offset) . ok ( ) ;
1037- let end = start. and_then ( |s| s. checked_add ( data. len ( ) ) ) ;
1038- // Only the guest_mac field (bytes 0..size_of::<MacAddr>()) is writable by the guest.
1039- // All other fields (status, max_virtqueue_pairs, mtu) are read-only device fields.
1040- let Some ( dst) = start
1041- . zip ( end)
1042- . filter ( |& ( _, end) | end <= std:: mem:: size_of :: < MacAddr > ( ) )
1043- . and_then ( |( start, end) | config_space_bytes. get_mut ( start..end) )
1044- else {
1045- error ! ( "Failed to write config space" ) ;
1046- self . metrics . cfg_fails . inc ( ) ;
1047- return ;
1048- } ;
1049-
1050- dst. copy_from_slice ( data) ;
1051- self . guest_mac = Some ( self . config_space . guest_mac ) ;
1052- self . metrics . mac_address_updates . inc ( ) ;
1035+ self . metrics . cfg_fails . inc ( ) ;
1036+ warn ! (
1037+ "virtio-net: guest driver attempted to write device config (offset={:#x}, len={:#x})" ,
1038+ offset,
1039+ data. len( )
1040+ ) ;
10531041 }
10541042
10551043 fn activate (
@@ -1295,42 +1283,52 @@ pub mod tests {
12951283 }
12961284
12971285 #[ test]
1298- fn test_virtio_device_rewrite_config ( ) {
1286+ fn test_virtio_device_config_space_is_read_only ( ) {
12991287 let mut net = default_net ( ) ;
1300- set_mac ( & mut net, MacAddr :: from_str ( "11:22:33:44:55:66" ) . unwrap ( ) ) ;
1301-
1302- let new_config: [ u8 ; MAC_ADDR_LEN as usize ] = [ 0x66 , 0x55 , 0x44 , 0x33 , 0x22 , 0x11 ] ;
1303- net. write_config ( 0 , & new_config) ;
1304- let mut new_config_read = [ 0u8 ; MAC_ADDR_LEN as usize ] ;
1305- net. read_config ( 0 , & mut new_config_read) ;
1306- assert_eq ! ( new_config, new_config_read) ;
1307-
1308- // Check that the guest MAC was updated.
1309- let expected_guest_mac = MacAddr :: from_bytes_unchecked ( & new_config) ;
1310- assert_eq ! ( expected_guest_mac, net. guest_mac. unwrap( ) ) ;
1311- assert_eq ! ( net. metrics. mac_address_updates. count( ) , 1 ) ;
1312-
1313- // Partial write (this is how the kernel sets a new mac address) - byte by byte.
1314- let new_config = [ 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 ] ;
1315- for i in 0 ..new_config. len ( ) {
1316- net. write_config ( i as u64 , & new_config[ i..=i] ) ;
1288+ let initial_mac = MacAddr :: from_str ( "11:22:33:44:55:66" ) . unwrap ( ) ;
1289+ set_mac ( & mut net, initial_mac) ;
1290+
1291+ let initial_bytes: [ u8 ; MAC_ADDR_LEN as usize ] = [ 0x11 , 0x22 , 0x33 , 0x44 , 0x55 , 0x66 ] ;
1292+
1293+ // Sanity check: the configured MAC is what the guest reads back.
1294+ let mut config_read = [ 0u8 ; MAC_ADDR_LEN as usize ] ;
1295+ net. read_config ( 0 , & mut config_read) ;
1296+ assert_eq ! ( config_read, initial_bytes) ;
1297+ assert_eq ! ( net. guest_mac. unwrap( ) , initial_mac) ;
1298+
1299+ // A 6-byte write to offset 0 must be rejected and leave both the
1300+ // config space bytes and the device's stored guest_mac unchanged.
1301+ let attempted_bytes: [ u8 ; MAC_ADDR_LEN as usize ] = [ 0x66 , 0x55 , 0x44 , 0x33 , 0x22 , 0x11 ] ;
1302+ let cfg_fails_before = net. metrics . cfg_fails . count ( ) ;
1303+ net. write_config ( 0 , & attempted_bytes) ;
1304+ net. read_config ( 0 , & mut config_read) ;
1305+ assert_eq ! ( config_read, initial_bytes) ;
1306+ assert_eq ! ( net. guest_mac. unwrap( ) , initial_mac) ;
1307+ assert_eq ! ( net. metrics. cfg_fails. count( ) , cfg_fails_before + 1 ) ;
1308+
1309+ // Single-byte writes covering the MAC region must also be rejected
1310+ // without changing state.
1311+ let cfg_fails_before = net. metrics . cfg_fails . count ( ) ;
1312+ for i in 0 ..attempted_bytes. len ( ) {
1313+ net. write_config ( i as u64 , & attempted_bytes[ i..=i] ) ;
13171314 }
1318- net. read_config ( 0 , & mut new_config_read) ;
1319- assert_eq ! ( new_config, new_config_read) ;
1320-
1321- // Invalid write.
1322- net. write_config ( 5 , & new_config) ;
1323- // Verify old config was untouched.
1324- new_config_read = [ 0u8 ; MAC_ADDR_LEN as usize ] ;
1325- net. read_config ( 0 , & mut new_config_read) ;
1326- assert_eq ! ( new_config, new_config_read) ;
1327-
1328- // Large offset that may cause an overflow.
1329- net. write_config ( u64:: MAX , & new_config) ;
1330- // Verify old config was untouched.
1331- new_config_read = [ 0u8 ; MAC_ADDR_LEN as usize ] ;
1332- net. read_config ( 0 , & mut new_config_read) ;
1333- assert_eq ! ( new_config, new_config_read) ;
1315+ net. read_config ( 0 , & mut config_read) ;
1316+ assert_eq ! ( config_read, initial_bytes) ;
1317+ assert_eq ! ( net. guest_mac. unwrap( ) , initial_mac) ;
1318+ assert_eq ! (
1319+ net. metrics. cfg_fails. count( ) ,
1320+ cfg_fails_before + attempted_bytes. len( ) as u64
1321+ ) ;
1322+
1323+ // Out-of-range and overflowing offsets must be rejected without
1324+ // changing state.
1325+ let cfg_fails_before = net. metrics . cfg_fails . count ( ) ;
1326+ net. write_config ( 5 , & attempted_bytes) ;
1327+ net. write_config ( u64:: MAX , & attempted_bytes) ;
1328+ net. read_config ( 0 , & mut config_read) ;
1329+ assert_eq ! ( config_read, initial_bytes) ;
1330+ assert_eq ! ( net. guest_mac. unwrap( ) , initial_mac) ;
1331+ assert_eq ! ( net. metrics. cfg_fails. count( ) , cfg_fails_before + 2 ) ;
13341332 }
13351333
13361334 #[ test]
0 commit comments