hw/net/virtio-net.c: Don't assume IP length field is aligned

In virtio-net.c we assume that the IP length field in the packet is
aligned, and we copy its address into a uint16_t* in the
VirtioNetRscUnit struct which we then dereference later.  This isn't
a safe assumption; it will also result in compilation failures if we
mark the ip_header struct as QEMU_PACKED because the compiler will
not let you take the address of an unaligned struct field.

Make the ip_plen field in VirtioNetRscUnit a void*, and make all the
places where we read or write through that pointer instead use some
new accessor functions read_unit_ip_len() and write_unit_ip_len()
which account for the pointer being potentially unaligned and also do
the network-byte-order conversion we were previously using htons() to
perform.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20241114141619.806652-2-peter.maydell@linaro.org>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
This commit is contained in:
Peter Maydell 2024-11-14 14:16:18 +00:00 committed by Philippe Mathieu-Daudé
parent 626b39006d
commit 5814c08467
2 changed files with 20 additions and 5 deletions

View File

@ -2049,6 +2049,21 @@ static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf,
return virtio_net_receive_rcu(nc, buf, size, false); return virtio_net_receive_rcu(nc, buf, size, false);
} }
/*
* Accessors to read and write the IP packet data length field. This
* is a potentially unaligned network-byte-order 16 bit unsigned integer
* pointed to by unit->ip_len.
*/
static uint16_t read_unit_ip_len(VirtioNetRscUnit *unit)
{
return lduw_be_p(unit->ip_plen);
}
static void write_unit_ip_len(VirtioNetRscUnit *unit, uint16_t l)
{
stw_be_p(unit->ip_plen, l);
}
static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
const uint8_t *buf, const uint8_t *buf,
VirtioNetRscUnit *unit) VirtioNetRscUnit *unit)
@ -2063,7 +2078,7 @@ static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain,
unit->ip_plen = &ip->ip_len; unit->ip_plen = &ip->ip_len;
unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen);
unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10;
unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen; unit->payload = read_unit_ip_len(unit) - ip_hdrlen - unit->tcp_hdrlen;
} }
static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
@ -2082,7 +2097,7 @@ static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain,
/* There is a difference between payload length in ipv4 and v6, /* There is a difference between payload length in ipv4 and v6,
ip header is excluded in ipv6 */ ip header is excluded in ipv6 */
unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen; unit->payload = read_unit_ip_len(unit) - unit->tcp_hdrlen;
} }
static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain, static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain,
@ -2231,7 +2246,7 @@ static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain,
VirtioNetRscUnit *o_unit; VirtioNetRscUnit *o_unit;
o_unit = &seg->unit; o_unit = &seg->unit;
o_ip_len = htons(*o_unit->ip_plen); o_ip_len = read_unit_ip_len(o_unit);
nseq = htonl(n_unit->tcp->th_seq); nseq = htonl(n_unit->tcp->th_seq);
oseq = htonl(o_unit->tcp->th_seq); oseq = htonl(o_unit->tcp->th_seq);
@ -2267,7 +2282,7 @@ coalesce:
o_unit->payload += n_unit->payload; /* update new data len */ o_unit->payload += n_unit->payload; /* update new data len */
/* update field in ip header */ /* update field in ip header */
*o_unit->ip_plen = htons(o_ip_len + n_unit->payload); write_unit_ip_len(o_unit, o_ip_len + n_unit->payload);
/* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced
for windows guest, while this may change the behavior for linux for windows guest, while this may change the behavior for linux

View File

@ -102,7 +102,7 @@ typedef struct VirtioNetRscStat {
/* Rsc unit general info used to checking if can coalescing */ /* Rsc unit general info used to checking if can coalescing */
typedef struct VirtioNetRscUnit { typedef struct VirtioNetRscUnit {
void *ip; /* ip header */ void *ip; /* ip header */
uint16_t *ip_plen; /* data len pointer in ip header field */ void *ip_plen; /* pointer to unaligned uint16_t data len in ip header */
struct tcp_header *tcp; /* tcp header */ struct tcp_header *tcp; /* tcp header */
uint16_t tcp_hdrlen; /* tcp header len */ uint16_t tcp_hdrlen; /* tcp header len */
uint16_t payload; /* pure payload without virtio/eth/ip/tcp */ uint16_t payload; /* pure payload without virtio/eth/ip/tcp */