diff options
| -rw-r--r-- | MAINTAINERS | 1 | ||||
| -rwxr-xr-x | configure | 2 | ||||
| -rw-r--r-- | contrib/ivshmem-client/ivshmem-client.c | 12 | ||||
| -rw-r--r-- | contrib/ivshmem-server/ivshmem-server.c | 12 | ||||
| -rw-r--r-- | docs/devel/testing.rst | 6 | ||||
| -rw-r--r-- | hw/net/virtio-net.c | 4 | ||||
| -rw-r--r-- | include/net/eth.h | 17 | ||||
| -rw-r--r-- | include/net/net.h | 1 | ||||
| -rw-r--r-- | net/colo-compare.c | 3 | ||||
| -rw-r--r-- | net/eth.c | 61 | ||||
| -rw-r--r-- | net/slirp.c | 10 | ||||
| -rw-r--r-- | net/tap-win32.c | 10 | ||||
| -rw-r--r-- | net/tap.c | 10 | ||||
| -rw-r--r-- | tests/qtest/fuzz-e1000e-test.c | 53 | ||||
| -rw-r--r-- | tests/qtest/meson.build | 1 | ||||
| -rw-r--r-- | tests/unit/test-block-iothread.c | 6 | ||||
| -rwxr-xr-x | tests/vm/freebsd | 16 |
17 files changed, 178 insertions, 47 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 25fc49d1dc..9147e9a429 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/configure b/configure index 847bc4d095..61872096a8 100755 --- a/configure +++ b/configure @@ -111,7 +111,7 @@ error_exit() { do_compiler() { # Run the compiler, capturing its output to the log. First argument # is compiler binary to execute. - local compiler="$1" + compiler="$1" shift if test -n "$BASH_VERSION"; then eval ' echo >>config.log " diff --git a/contrib/ivshmem-client/ivshmem-client.c b/contrib/ivshmem-client/ivshmem-client.c index b1274b236a..182c79d27c 100644 --- a/contrib/ivshmem-client/ivshmem-client.c +++ b/contrib/ivshmem-client/ivshmem-client.c @@ -178,7 +178,7 @@ ivshmem_client_init(IvshmemClient *client, const char *unix_sock_path, int ivshmem_client_connect(IvshmemClient *client) { - struct sockaddr_un sun; + struct sockaddr_un s_un; int fd, ret; int64_t tmp; @@ -192,16 +192,16 @@ ivshmem_client_connect(IvshmemClient *client) return -1; } - sun.sun_family = AF_UNIX; - ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + s_un.sun_family = AF_UNIX; + ret = snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", client->unix_sock_path); - if (ret < 0 || ret >= sizeof(sun.sun_path)) { + if (ret < 0 || ret >= sizeof(s_un.sun_path)) { IVSHMEM_CLIENT_DEBUG(client, "could not copy unix socket path\n"); goto err_close; } - if (connect(client->sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", sun.sun_path, + if (connect(client->sock_fd, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + IVSHMEM_CLIENT_DEBUG(client, "cannot connect to %s: %s\n", s_un.sun_path, strerror(errno)); goto err_close; } diff --git a/contrib/ivshmem-server/ivshmem-server.c b/contrib/ivshmem-server/ivshmem-server.c index 88daee812d..39a6ffdb5d 100644 --- a/contrib/ivshmem-server/ivshmem-server.c +++ b/contrib/ivshmem-server/ivshmem-server.c @@ -288,7 +288,7 @@ ivshmem_server_init(IvshmemServer *server, const char *unix_sock_path, int ivshmem_server_start(IvshmemServer *server) { - struct sockaddr_un sun; + struct sockaddr_un s_un; int shm_fd, sock_fd, ret; /* open shm file */ @@ -327,15 +327,15 @@ ivshmem_server_start(IvshmemServer *server) goto err_close_shm; } - sun.sun_family = AF_UNIX; - ret = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + s_un.sun_family = AF_UNIX; + ret = snprintf(s_un.sun_path, sizeof(s_un.sun_path), "%s", server->unix_sock_path); - if (ret < 0 || ret >= sizeof(sun.sun_path)) { + if (ret < 0 || ret >= sizeof(s_un.sun_path)) { IVSHMEM_SERVER_DEBUG(server, "could not copy unix socket path\n"); goto err_close_sock; } - if (bind(sock_fd, (struct sockaddr *)&sun, sizeof(sun)) < 0) { - IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", sun.sun_path, + if (bind(sock_fd, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) { + IVSHMEM_SERVER_DEBUG(server, "cannot connect to %s: %s\n", s_un.sun_path, strerror(errno)); goto err_close_sock; } diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 1434a50cc4..1da4c4e4c4 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -34,17 +34,17 @@ If you are writing new code in QEMU, consider adding a unit test, especially for utility modules that are relatively stateless or have few dependencies. To add a new unit test: -1. Create a new source file. For example, ``tests/foo-test.c``. +1. Create a new source file. For example, ``tests/unit/foo-test.c``. 2. Write the test. Normally you would include the header file which exports the module API, then verify the interface behaves as expected from your test. The test code should be organized with the glib testing framework. Copying and modifying an existing test is usually a good idea. -3. Add the test to ``tests/meson.build``. The unit tests are listed in a +3. Add the test to ``tests/unit/meson.build``. The unit tests are listed in a dictionary called ``tests``. The values are any additional sources and dependencies to be linked with the test. For a simple test whose source - is in ``tests/foo-test.c``, it is enough to add an entry like:: + is in ``tests/unit/foo-test.c``, it is enough to add an entry like:: { ... diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 96a3cc8357..66b9ff4511 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -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++) { diff --git a/include/net/eth.h b/include/net/eth.h index 0671be6916..7767ae880e 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -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 diff --git a/include/net/net.h b/include/net/net.h index a02949f6db..3559f3ca19 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -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; }; diff --git a/net/colo-compare.c b/net/colo-compare.c index 84db4978ac..9d1ad99941 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -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 diff --git a/net/eth.c b/net/eth.c index 1e0821c5f8..fe876d1a55 100644 --- a/net/eth.c +++ b/net/eth.c @@ -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; - - 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; - } + struct ip6_ext_hdr_routing rt_hdr; + size_t input_size = iov_size(pkt, pkt_frags); + size_t bytes_read; - bytes_read = iov_to_buf(pkt, pkt_frags, - rthdr_offset + sizeof(*ext_hdr), - dst_addr, sizeof(*dst_addr)); + if (input_size < ext_hdr_offset + sizeof(rt_hdr) + sizeof(*dst_addr)) { + return false; + } - return bytes_read == sizeof(*dst_addr); + 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 false; + 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; +} diff --git a/net/slirp.c b/net/slirp.c index 9454a673d6..a9fdc7a08f 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -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); } diff --git a/net/tap-win32.c b/net/tap-win32.c index 21e451107b..d7c2a8759c 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -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); } diff --git a/net/tap.c b/net/tap.c index 12a08d54fe..d6d8456188 100644 --- a/net/tap.c +++ b/net/tap.c @@ -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); diff --git a/tests/qtest/fuzz-e1000e-test.c b/tests/qtest/fuzz-e1000e-test.c new file mode 100644 index 0000000000..66229e6096 --- /dev/null +++ b/tests/qtest/fuzz-e1000e-test.c @@ -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(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 9731606c31..902cfef7cb 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -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', diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 3f866a35c6..8cf172cb7a 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -89,7 +89,7 @@ static void test_sync_op_pread(BdrvChild *c) static void test_sync_op_pwrite(BdrvChild *c) { - uint8_t buf[512]; + uint8_t buf[512] = { 0 }; int ret; /* Success */ @@ -117,7 +117,7 @@ static void test_sync_op_blk_pread(BlockBackend *blk) static void test_sync_op_blk_pwrite(BlockBackend *blk) { - uint8_t buf[512]; + uint8_t buf[512] = { 0 }; int ret; /* Success */ @@ -141,7 +141,7 @@ static void test_sync_op_load_vmstate(BdrvChild *c) static void test_sync_op_save_vmstate(BdrvChild *c) { - uint8_t buf[512]; + uint8_t buf[512] = { 0 }; int ret; /* Error: Driver does not support snapshots */ diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 09f3ee6cb8..6e20e84322 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -20,12 +20,16 @@ import socket import subprocess import basevm +FREEBSD_CONFIG = { + 'cpu' : "max,sse4.2=off", +} + class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.1/FreeBSD-12.1-RELEASE-amd64-disc1.iso.xz" - csum = "7394c3f60a1e236e7bd3a05809cf43ae39a3b8e5d42d782004cf2f26b1cfcd88" + link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.2/FreeBSD-12.2-RELEASE-amd64-disc1.iso.xz" + csum = "a4530246cafbf1dd42a9bd3ea441ca9a78a6a0cd070278cbdf63f3a6f803ecae" size = "20G" pkgs = [ # build tools @@ -61,6 +65,8 @@ class FreeBSDVM(basevm.BaseVM): "zstd", ] + # TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed + # See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71 BUILD_SCRIPT = """ set -e; rm -rf /home/qemu/qemu-test.* @@ -68,7 +74,7 @@ class FreeBSDVM(basevm.BaseVM): mkdir src build; cd src; tar -xf /dev/vtbd1; cd ../build - ../src/configure --python=python3.7 {configure_opts}; + ../src/configure --python=python3.7 --disable-gnutls {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ @@ -125,7 +131,7 @@ class FreeBSDVM(basevm.BaseVM): self.console_wait_send("IPv6", "n") self.console_wait_send("Resolver", "\n") - self.console_wait_send("Time Zone Selector", "a\n") + self.console_wait_send("Time Zone Selector", "0\n") self.console_wait_send("Confirmation", "y") self.console_wait_send("Time & Date", "\n") self.console_wait_send("Time & Date", "\n") @@ -206,4 +212,4 @@ class FreeBSDVM(basevm.BaseVM): self.print_step("All done") if __name__ == "__main__": - sys.exit(basevm.main(FreeBSDVM)) + sys.exit(basevm.main(FreeBSDVM, config=FREEBSD_CONFIG)) |