-----BEGIN PGP SIGNATURE-----

Version: GnuPG v1
 
 iQEcBAABAgAGBQJgWGTMAAoJEO8Ells5jWIRLUQIAJ6xxOKldqyo9n9N2vP+6O1J
 lLSC0NT2d0ACptl/8o+KKlf9z2n7o/SKykLUXwCjgLeT2xTKhJ+p8tPOXJ4gns3D
 U3GGc20gUkuGwNl0g2Rr2Yz4EJXlxx5Sl9LJz9quyx0Yp0SV8fYgQpj3mLgRye6T
 hbVSDnQiSyo1iSx+Q/Qc6+Gr4a+sV4pExhrVP1n61JFCB95DUMNqjBsHv+urowHh
 WCN9JbHuiZRHeIGIZujJxzMrh8jlP4pPFIrXvsdwHm/xJlVTgMXFvfIjfi3IrYWU
 NbI0PluSfPbeAB8V+aPb9386K4UFpEEHJipIm+4HSVpVsAuoRVnEVoIWGubXfWo=
 =9Tij
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/jasowang/tags/net-pull-request' into staging

# gpg: Signature made Mon 22 Mar 2021 09:35:08 GMT
# gpg:                using RSA key EF04965B398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [marginal]
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F  3562 EF04 965B 398D 6211

* remotes/jasowang/tags/net-pull-request:
  net/eth: Add an assert() and invert if() statement to simplify code
  net/eth: Read ip6_ext_hdr_routing buffer before accessing it
  net/eth: Check iovec has enough data earlier
  net/eth: Check size earlier in _eth_get_rss_ex_dst_addr()
  net/eth: Better describe _eth_get_rss_ex_dst_addr's offset argument
  net/eth: Simplify _eth_get_rss_ex_dst_addr()
  net/eth: Use correct in6_address offset in _eth_get_rss_ex_dst_addr()
  net/colo-compare.c: Optimize removal of secondary packet
  net/colo-compare.c: Fix memory leak for non-tcp packet
  hw/net: virtio-net: Initialize nc->do_not_pad to true
  net: Pad short frames to minimum size before sending from SLiRP/TAP
  net: Add a 'do_not_pad" to NetClientState
  net: eth: Add a helper to pad a short Ethernet frame

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-03-22 11:24:55 +00:00
commit b184750926
11 changed files with 149 additions and 24 deletions

View file

@ -2027,6 +2027,7 @@ e1000e
M: Dmitry Fleytman <dmitry.fleytman@gmail.com>
S: Maintained
F: hw/net/e1000e*
F: tests/qtest/fuzz-e1000e-test.c
eepro100
M: Stefan Weil <sw@weilnetz.de>

View file

@ -3314,6 +3314,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp)
object_get_typename(OBJECT(dev)), dev->id, n);
}
for (i = 0; i < n->max_queues; i++) {
n->nic->ncs[i].do_not_pad = true;
}
peer_test_vnet_hdr(n);
if (peer_has_vnet_hdr(n)) {
for (i = 0; i < n->max_queues; i++) {

View file

@ -31,6 +31,7 @@
#define ETH_ALEN 6
#define ETH_HLEN 14
#define ETH_ZLEN 60 /* Min. octets in frame without FCS */
struct eth_header {
uint8_t h_dest[ETH_ALEN]; /* destination eth addr */
@ -422,4 +423,20 @@ bool
eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
size_t ip6hdr_off, eth_ip6_hdr_info *info);
/**
* eth_pad_short_frame - pad a short frame to the minimum Ethernet frame length
*
* If the Ethernet frame size is shorter than 60 bytes, it will be padded to
* 60 bytes at the address @padded_pkt.
*
* @padded_pkt: buffer address to hold the padded frame
* @padded_buflen: pointer holding length of @padded_pkt. If the frame is
* padded, the length will be updated to the padded one.
* @pkt: address to hold the original Ethernet frame
* @pkt_size: size of the original Ethernet frame
* @return true if the frame is padded, otherwise false
*/
bool eth_pad_short_frame(uint8_t *padded_pkt, size_t *padded_buflen,
const void *pkt, size_t pkt_size);
#endif

View file

@ -103,6 +103,7 @@ struct NetClientState {
int vring_enable;
int vnet_hdr_len;
bool is_netdev;
bool do_not_pad; /* do not pad to the minimum ethernet frame length */
QTAILQ_HEAD(, NetFilterState) filters;
};

View file

@ -690,7 +690,8 @@ static void colo_compare_packet(CompareState *s, Connection *conn,
if (result) {
colo_release_primary_pkt(s, pkt);
g_queue_remove(&conn->secondary_list, result->data);
packet_destroy(result->data, NULL);
g_queue_delete_link(&conn->secondary_list, result);
} else {
/*
* If one packet arrive late, the secondary_list or

View file

@ -401,31 +401,29 @@ eth_is_ip6_extension_header_type(uint8_t hdr_type)
static bool
_eth_get_rss_ex_dst_addr(const struct iovec *pkt, int pkt_frags,
size_t rthdr_offset,
size_t ext_hdr_offset,
struct ip6_ext_hdr *ext_hdr,
struct in6_address *dst_addr)
{
struct ip6_ext_hdr_routing *rthdr = (struct ip6_ext_hdr_routing *) ext_hdr;
struct ip6_ext_hdr_routing rt_hdr;
size_t input_size = iov_size(pkt, pkt_frags);
size_t bytes_read;
if ((rthdr->rtype == 2) &&
(rthdr->len == sizeof(struct in6_address) / 8) &&
(rthdr->segleft == 1)) {
size_t input_size = iov_size(pkt, pkt_frags);
size_t bytes_read;
if (input_size < rthdr_offset + sizeof(*ext_hdr)) {
return false;
}
bytes_read = iov_to_buf(pkt, pkt_frags,
rthdr_offset + sizeof(*ext_hdr),
dst_addr, sizeof(*dst_addr));
return bytes_read == sizeof(*dst_addr);
if (input_size < ext_hdr_offset + sizeof(rt_hdr) + sizeof(*dst_addr)) {
return false;
}
return false;
bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset,
&rt_hdr, sizeof(rt_hdr));
assert(bytes_read == sizeof(rt_hdr));
if ((rt_hdr.rtype != 2) || (rt_hdr.segleft != 1)) {
return false;
}
bytes_read = iov_to_buf(pkt, pkt_frags, ext_hdr_offset + sizeof(rt_hdr),
dst_addr, sizeof(*dst_addr));
assert(bytes_read == sizeof(*dst_addr));
return true;
}
static bool
@ -528,10 +526,12 @@ bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
}
if (curr_ext_hdr_type == IP6_ROUTING) {
info->rss_ex_dst_valid =
_eth_get_rss_ex_dst_addr(pkt, pkt_frags,
ip6hdr_off + info->full_hdr_len,
&ext_hdr, &info->rss_ex_dst);
if (ext_hdr.ip6r_len == sizeof(struct in6_address) / 8) {
info->rss_ex_dst_valid =
_eth_get_rss_ex_dst_addr(pkt, pkt_frags,
ip6hdr_off + info->full_hdr_len,
&ext_hdr, &info->rss_ex_dst);
}
} else if (curr_ext_hdr_type == IP6_DESTINATON) {
info->rss_ex_src_valid =
_eth_get_rss_ex_src_addr(pkt, pkt_frags,
@ -548,3 +548,20 @@ bool eth_parse_ipv6_hdr(const struct iovec *pkt, int pkt_frags,
info->l4proto = ext_hdr.ip6r_nxt;
return true;
}
bool eth_pad_short_frame(uint8_t *padded_pkt, size_t *padded_buflen,
const void *pkt, size_t pkt_size)
{
assert(padded_buflen && *padded_buflen >= ETH_ZLEN);
if (pkt_size >= ETH_ZLEN) {
return false;
}
/* pad to minimum Ethernet frame length */
memcpy(padded_pkt, pkt, pkt_size);
memset(&padded_pkt[pkt_size], 0, ETH_ZLEN - pkt_size);
*padded_buflen = ETH_ZLEN;
return true;
}

View file

@ -31,6 +31,7 @@
#include <pwd.h>
#include <sys/wait.h>
#endif
#include "net/eth.h"
#include "net/net.h"
#include "clients.h"
#include "hub.h"
@ -115,6 +116,15 @@ static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len,
void *opaque)
{
SlirpState *s = opaque;
uint8_t min_pkt[ETH_ZLEN];
size_t min_pktsz = sizeof(min_pkt);
if (!s->nc.peer->do_not_pad) {
if (eth_pad_short_frame(min_pkt, &min_pktsz, pkt, pkt_len)) {
pkt = min_pkt;
pkt_len = min_pktsz;
}
}
return qemu_send_packet(&s->nc, pkt, pkt_len);
}

View file

@ -31,6 +31,7 @@
#include "qemu-common.h"
#include "clients.h" /* net_init_tap */
#include "net/eth.h"
#include "net/net.h"
#include "net/tap.h" /* tap_has_ufo, ... */
#include "qemu/error-report.h"
@ -688,9 +689,18 @@ static void tap_win32_send(void *opaque)
uint8_t *buf;
int max_size = 4096;
int size;
uint8_t min_pkt[ETH_ZLEN];
size_t min_pktsz = sizeof(min_pkt);
size = tap_win32_read(s->handle, &buf, max_size);
if (size > 0) {
if (!s->nc.peer->do_not_pad) {
if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) {
buf = min_pkt;
size = min_pktsz;
}
}
qemu_send_packet(&s->nc, buf, size);
tap_win32_free_buffer(s->handle, buf);
}

View file

@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <net/if.h>
#include "net/eth.h"
#include "net/net.h"
#include "clients.h"
#include "monitor/monitor.h"
@ -189,6 +190,8 @@ static void tap_send(void *opaque)
while (true) {
uint8_t *buf = s->buf;
uint8_t min_pkt[ETH_ZLEN];
size_t min_pktsz = sizeof(min_pkt);
size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
if (size <= 0) {
@ -200,6 +203,13 @@ static void tap_send(void *opaque)
size -= s->host_vnet_hdr_len;
}
if (!s->nc.peer->do_not_pad) {
if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) {
buf = min_pkt;
size = min_pktsz;
}
}
size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
if (size == 0) {
tap_read_poll(s, false);

View file

@ -0,0 +1,53 @@
/*
* QTest testcase for e1000e device generated by fuzzer
*
* Copyright (c) 2021 Red Hat, Inc.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "libqos/libqtest.h"
/*
* https://bugs.launchpad.net/qemu/+bug/1879531
*/
static void test_lp1879531_eth_get_rss_ex_dst_addr(void)
{
QTestState *s;
s = qtest_init("-nographic -monitor none -serial none -M pc-q35-5.0");
qtest_outl(s, 0xcf8, 0x80001010);
qtest_outl(s, 0xcfc, 0xe1020000);
qtest_outl(s, 0xcf8, 0x80001004);
qtest_outw(s, 0xcfc, 0x7);
qtest_writeb(s, 0x25, 0x86);
qtest_writeb(s, 0x26, 0xdd);
qtest_writeb(s, 0x4f, 0x2b);
qtest_writel(s, 0xe1020030, 0x190002e1);
qtest_writew(s, 0xe102003a, 0x0807);
qtest_writel(s, 0xe1020048, 0x12077cdd);
qtest_writel(s, 0xe1020400, 0xba077cdd);
qtest_writel(s, 0xe1020420, 0x190002e1);
qtest_writel(s, 0xe1020428, 0x3509d807);
qtest_writeb(s, 0xe1020438, 0xe2);
qtest_writeb(s, 0x4f, 0x2b);
qtest_quit(s);
}
int main(int argc, char **argv)
{
const char *arch = qtest_get_arch();
g_test_init(&argc, &argv, NULL);
if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
qtest_add_func("fuzz/test_lp1879531_eth_get_rss_ex_dst_addr",
test_lp1879531_eth_get_rss_ex_dst_addr);
}
return g_test_run();
}

View file

@ -67,6 +67,7 @@ qtests_i386 = \
(config_all_devices.has_key('CONFIG_TPM_TIS_ISA') ? ['tpm-tis-test'] : []) + \
(config_all_devices.has_key('CONFIG_TPM_TIS_ISA') ? ['tpm-tis-swtpm-test'] : []) + \
(config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) + \
(config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) + \
qtests_pci + \
['fdc-test',
'ide-test',