diff mbox series

hw/net: Discard overly fragmented packets

Message ID 20210705084011.814175-1-philmd@redhat.com
State New
Headers show
Series hw/net: Discard overly fragmented packets | expand

Commit Message

Philippe Mathieu-Daudé July 5, 2021, 8:40 a.m. UTC
Our infrastructure can handle fragmented packets up to
NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
been proven enough in production for years. If it is
reached, it is likely an evil crafted packet. Discard it.

Include the qtest reproducer provided by Alexander Bulekov:

  $ make check-qtest-i386
  ...
  Running test qtest-i386/fuzz-vmxnet3-test
  qemu-system-i386: net/eth.c:334: void eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t, size_t, size_t, _Bool):
  Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.

Cc: qemu-stable@nongnu.org
Reported-by: OSS-Fuzz (Issue 35799)
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
 hw/net/net_tx_pkt.c             |   8 ++
 tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
 MAINTAINERS                     |   1 +
 tests/qtest/meson.build         |   1 +
 4 files changed, 205 insertions(+)
 create mode 100644 tests/qtest/fuzz-vmxnet3-test.c

Comments

Mauro Matteo Cascella July 6, 2021, 9 a.m. UTC | #1
Hello Philippe,

I think you don't need root privileges to craft such a highly
fragmented packet from within the guest (tools like hping3 or nmap
come to mind). Right? If so, we may consider allocating a CVE for this
bug. If not, this is not CVE worthy - root does not need an assertion
failure to cause damage to the system.

On Mon, Jul 5, 2021 at 10:40 AM Philippe Mathieu-Daudé
<philmd@redhat.com> wrote:
>
> Our infrastructure can handle fragmented packets up to
> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
> been proven enough in production for years. If it is
> reached, it is likely an evil crafted packet. Discard it.
>
> Include the qtest reproducer provided by Alexander Bulekov:
>
>   $ make check-qtest-i386
>   ...
>   Running test qtest-i386/fuzz-vmxnet3-test
>   qemu-system-i386: net/eth.c:334: void eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t, size_t, size_t, _Bool):
>   Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>
> Cc: qemu-stable@nongnu.org
> Reported-by: OSS-Fuzz (Issue 35799)
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
>  hw/net/net_tx_pkt.c             |   8 ++
>  tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>  MAINTAINERS                     |   1 +
>  tests/qtest/meson.build         |   1 +
>  4 files changed, 205 insertions(+)
>  create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
>
> diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
> index 1f9aa59eca2..77e9729a7ba 100644
> --- a/hw/net/net_tx_pkt.c
> +++ b/hw/net/net_tx_pkt.c
> @@ -590,6 +590,14 @@ static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt,
>          fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset,
>              fragment, &dst_idx);
>
> +        if (dst_idx == NET_MAX_FRAG_SG_LIST && fragment_len > 0) {
> +            /*
> +             * The packet is too fragmented for our infrastructure
> +             * (not enough iovec), don't even try to send.
> +             */
> +            return false;
> +        }
> +
>          more_frags = (fragment_offset + fragment_len < pkt->payload_len);
>
>          eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base,
> diff --git a/tests/qtest/fuzz-vmxnet3-test.c b/tests/qtest/fuzz-vmxnet3-test.c
> new file mode 100644
> index 00000000000..d69009bf5ce
> --- /dev/null
> +++ b/tests/qtest/fuzz-vmxnet3-test.c
> @@ -0,0 +1,195 @@
> +/*
> + * QTest testcase for vmxnet3 device generated by fuzzer
> + *
> + * Copyright Red Hat
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "libqos/libqtest.h"
> +
> +/*
> + * https://gitlab.com/qemu-project/qemu/-/issues/460
> + */
> +static void test_oss_35799_eth_setup_ip4_fragmentation(void)
> +{
> +    QTestState *s;
> +
> +    s = qtest_init("-machine q35 -m 32M -display none -nodefaults "
> +                   "-device vmxnet3,netdev=net0 -netdev user,id=net0");
> +    qtest_outl(s, 0xcf8, 0x80000814);
> +    qtest_outl(s, 0xcfc, 0xe0000000);
> +    qtest_outl(s, 0xcf8, 0x80000804);
> +    qtest_outw(s, 0xcfc, 0x06);
> +    qtest_outl(s, 0xcf8, 0x80000812);
> +    qtest_outl(s, 0xcfc, 0x2000);
> +    qtest_outl(s, 0xcf8, 0x80000815);
> +    qtest_outb(s, 0xcfc, 0x40);
> +    qtest_bufwrite(s, 0x0, "\xe1", 0x1);
> +    qtest_bufwrite(s, 0x1, "\xfe", 0x1);
> +    qtest_bufwrite(s, 0x2, "\xbe", 0x1);
> +    qtest_bufwrite(s, 0x3, "\xba", 0x1);
> +    qtest_bufwrite(s, 0x28, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x29, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2a, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2b, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2c, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2d, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2e, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x2f, "\xff", 0x1);
> +    qtest_bufwrite(s, 0x37, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3e, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xe0004020, "\x00\x00\xfe\xca", 0x4);
> +    qtest_bufwrite(s, 0x9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xd, "\x10", 0x1);
> +    qtest_bufwrite(s, 0x12, "\x10", 0x1);
> +    qtest_bufwrite(s, 0x19, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1b, "\x21", 0x1);
> +    qtest_bufwrite(s, 0x1d, "\x0c", 0x1);
> +    qtest_bufwrite(s, 0x2d, "\x00", 0x1);
> +    qtest_bufwrite(s, 0x10000c, "\x08", 0x1);
> +    qtest_bufwrite(s, 0x10000e, "\x45", 0x1);
> +    qtest_bufwrite(s, 0x100017, "\x11", 0x1);
> +    qtest_bufwrite(s, 0x20000600, "\x00", 0x1);
> +    qtest_bufwrite(s, 0x38, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x39, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x48, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x49, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x58, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x59, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x68, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x69, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x78, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x79, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x88, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x89, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x98, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x99, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xa8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xa9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xb8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xb9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xc8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xc9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xd8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xd9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xe8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xe9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xf8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0xf9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x108, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x109, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x118, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x119, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x128, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x129, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x138, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x139, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x148, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x149, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x158, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x159, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x168, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x169, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x178, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x179, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x188, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x189, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x198, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x199, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1a8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1a9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1b8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1b9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1c8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1c9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1d8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1d9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1e8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1e9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x1f8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x1f9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x208, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x209, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x218, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x219, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x228, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x229, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x238, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x239, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x248, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x249, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x258, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x259, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x268, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x269, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x278, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x279, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x288, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x289, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x298, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x299, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2a8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2a9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2b8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2b9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2c8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2c9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2d8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2d9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2e8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2e9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x2f8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x2f9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x308, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x309, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x318, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x319, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x328, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x329, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x338, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x339, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x348, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x349, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x358, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x359, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x368, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x369, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x378, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x379, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x388, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x389, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x398, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x399, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3a8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3a9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3b8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3b9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3c8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3c9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3d8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3d9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3e8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3e9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0x3f8, "\x01", 0x1);
> +    qtest_bufwrite(s, 0x3f9, "\x40", 0x1);
> +    qtest_bufwrite(s, 0xd, "\x10", 0x1);
> +    qtest_bufwrite(s, 0x20000600, "\x00", 0x1);
> +    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_oss_35799_eth_setup_ip4_fragmentation",
> +                       test_oss_35799_eth_setup_ip4_fragmentation);
> +    }
> +
> +    return g_test_run();
> +}
> diff --git a/MAINTAINERS b/MAINTAINERS
> index cb8f3ea2c2e..43e5050ad96 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2001,6 +2001,7 @@ S: Maintained
>  F: hw/net/vmxnet*
>  F: hw/scsi/vmw_pvscsi*
>  F: tests/qtest/vmxnet3-test.c
> +F: tests/qtest/fuzz-vmxnet3-test.c
>
>  Rocker
>  M: Jiri Pirko <jiri@resnulli.us>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index b03e8541700..42add92e9d4 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -66,6 +66,7 @@
>    (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'] : []) +   \
> +  (config_all_devices.has_key('CONFIG_VMXNET3_PCI') ? ['fuzz-vmxnet3-test'] : []) +   \
>    (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) +                 \
>    qtests_pci +                                                                              \
>    ['fdc-test',
> --
> 2.31.1
>
Philippe Mathieu-Daudé July 6, 2021, 9:09 a.m. UTC | #2
Hi Mauro,

On 7/6/21 11:00 AM, Mauro Matteo Cascella wrote:
> Hello Philippe,
> 
> I think you don't need root privileges to craft such a highly
> fragmented packet from within the guest (tools like hping3 or nmap
> come to mind). Right? If so, we may consider allocating a CVE for this
> bug. If not, this is not CVE worthy - root does not need an assertion
> failure to cause damage to the system.

Thanks for worrying about CVE. I have no clue, so I'll
defer that question to Andrew, Dmitry and Jason.

Regards,

Phil.

> On Mon, Jul 5, 2021 at 10:40 AM Philippe Mathieu-Daudé
> <philmd@redhat.com> wrote:
>>
>> Our infrastructure can handle fragmented packets up to
>> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
>> been proven enough in production for years. If it is
>> reached, it is likely an evil crafted packet. Discard it.
>>
>> Include the qtest reproducer provided by Alexander Bulekov:
>>
>>   $ make check-qtest-i386
>>   ...
>>   Running test qtest-i386/fuzz-vmxnet3-test
>>   qemu-system-i386: net/eth.c:334: void eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t, size_t, size_t, _Bool):
>>   Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>>
>> Cc: qemu-stable@nongnu.org
>> Reported-by: OSS-Fuzz (Issue 35799)
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>> ---
>>  hw/net/net_tx_pkt.c             |   8 ++
>>  tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>>  MAINTAINERS                     |   1 +
>>  tests/qtest/meson.build         |   1 +
>>  4 files changed, 205 insertions(+)
>>  create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
>>
>> diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
>> index 1f9aa59eca2..77e9729a7ba 100644
>> --- a/hw/net/net_tx_pkt.c
>> +++ b/hw/net/net_tx_pkt.c
>> @@ -590,6 +590,14 @@ static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt,
>>          fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset,
>>              fragment, &dst_idx);
>>
>> +        if (dst_idx == NET_MAX_FRAG_SG_LIST && fragment_len > 0) {
>> +            /*
>> +             * The packet is too fragmented for our infrastructure
>> +             * (not enough iovec), don't even try to send.
>> +             */
>> +            return false;
>> +        }
>> +
>>          more_frags = (fragment_offset + fragment_len < pkt->payload_len);
>>
>>          eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base,
Thomas Huth Aug. 3, 2021, 9:33 a.m. UTC | #3
On 05/07/2021 10.40, Philippe Mathieu-Daudé wrote:
> Our infrastructure can handle fragmented packets up to
> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
> been proven enough in production for years. If it is
> reached, it is likely an evil crafted packet. Discard it.
> 
> Include the qtest reproducer provided by Alexander Bulekov:
> 
>    $ make check-qtest-i386
>    ...
>    Running test qtest-i386/fuzz-vmxnet3-test
>    qemu-system-i386: net/eth.c:334: void eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t, size_t, size_t, _Bool):
>    Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
> 
> Cc: qemu-stable@nongnu.org
> Reported-by: OSS-Fuzz (Issue 35799)
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
>   hw/net/net_tx_pkt.c             |   8 ++
>   tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>   MAINTAINERS                     |   1 +
>   tests/qtest/meson.build         |   1 +
>   4 files changed, 205 insertions(+)
>   create mode 100644 tests/qtest/fuzz-vmxnet3-test.c

Reviewed-by: Thomas Huth <thuth@redhat.com>

Jason, I think this would even still qualify for QEMU v6.1 ?
Philippe Mathieu-Daudé Aug. 3, 2021, 9:51 a.m. UTC | #4
On 8/3/21 11:33 AM, Thomas Huth wrote:
> On 05/07/2021 10.40, Philippe Mathieu-Daudé wrote:
>> Our infrastructure can handle fragmented packets up to
>> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
>> been proven enough in production for years. If it is
>> reached, it is likely an evil crafted packet. Discard it.
>>
>> Include the qtest reproducer provided by Alexander Bulekov:
>>
>>    $ make check-qtest-i386
>>    ...
>>    Running test qtest-i386/fuzz-vmxnet3-test
>>    qemu-system-i386: net/eth.c:334: void
>> eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t,
>> size_t, size_t, _Bool):
>>    Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>>
>> Cc: qemu-stable@nongnu.org
>> Reported-by: OSS-Fuzz (Issue 35799)
>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>> ---
>>   hw/net/net_tx_pkt.c             |   8 ++
>>   tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>>   MAINTAINERS                     |   1 +
>>   tests/qtest/meson.build         |   1 +
>>   4 files changed, 205 insertions(+)
>>   create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
> 
> Reviewed-by: Thomas Huth <thuth@redhat.com>
> 
> Jason, I think this would even still qualify for QEMU v6.1 ?

Yes, easy one for 6.1.
Jason Wang Aug. 4, 2021, 1:43 a.m. UTC | #5
在 2021/8/3 下午5:51, Philippe Mathieu-Daudé 写道:
> On 8/3/21 11:33 AM, Thomas Huth wrote:
>> On 05/07/2021 10.40, Philippe Mathieu-Daudé wrote:
>>> Our infrastructure can handle fragmented packets up to
>>> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
>>> been proven enough in production for years. If it is
>>> reached, it is likely an evil crafted packet. Discard it.
>>>
>>> Include the qtest reproducer provided by Alexander Bulekov:
>>>
>>>     $ make check-qtest-i386
>>>     ...
>>>     Running test qtest-i386/fuzz-vmxnet3-test
>>>     qemu-system-i386: net/eth.c:334: void
>>> eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t,
>>> size_t, size_t, _Bool):
>>>     Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>>>
>>> Cc: qemu-stable@nongnu.org
>>> Reported-by: OSS-Fuzz (Issue 35799)
>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
>>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>>> ---
>>>    hw/net/net_tx_pkt.c             |   8 ++
>>>    tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>>>    MAINTAINERS                     |   1 +
>>>    tests/qtest/meson.build         |   1 +
>>>    4 files changed, 205 insertions(+)
>>>    create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>
>> Jason, I think this would even still qualify for QEMU v6.1 ?
> Yes, easy one for 6.1.


Yes, this will be included for rc3.

Thanks


>
Jason Wang Aug. 11, 2021, 4:08 a.m. UTC | #6
在 2021/8/4 上午9:43, Jason Wang 写道:
>
> 在 2021/8/3 下午5:51, Philippe Mathieu-Daudé 写道:
>> On 8/3/21 11:33 AM, Thomas Huth wrote:
>>> On 05/07/2021 10.40, Philippe Mathieu-Daudé wrote:
>>>> Our infrastructure can handle fragmented packets up to
>>>> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
>>>> been proven enough in production for years. If it is
>>>> reached, it is likely an evil crafted packet. Discard it.
>>>>
>>>> Include the qtest reproducer provided by Alexander Bulekov:
>>>>
>>>>     $ make check-qtest-i386
>>>>     ...
>>>>     Running test qtest-i386/fuzz-vmxnet3-test
>>>>     qemu-system-i386: net/eth.c:334: void
>>>> eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t,
>>>> size_t, size_t, _Bool):
>>>>     Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>>>>
>>>> Cc: qemu-stable@nongnu.org
>>>> Reported-by: OSS-Fuzz (Issue 35799)
>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
>>>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>>>> ---
>>>>    hw/net/net_tx_pkt.c             |   8 ++
>>>>    tests/qtest/fuzz-vmxnet3-test.c | 195 
>>>> ++++++++++++++++++++++++++++++++
>>>>    MAINTAINERS                     |   1 +
>>>>    tests/qtest/meson.build         |   1 +
>>>>    4 files changed, 205 insertions(+)
>>>>    create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>
>>> Jason, I think this would even still qualify for QEMU v6.1 ?
>> Yes, easy one for 6.1.
>
>
> Yes, this will be included for rc3.
>
> Thanks


For some reasons it misses rc3.

I will include it for 6.2.

Sorry.


>
>
>>
Thomas Huth Aug. 5, 2022, 2:51 p.m. UTC | #7
On 11/08/2021 06.08, Jason Wang wrote:
> 
> 在 2021/8/4 上午9:43, Jason Wang 写道:
>>
>> 在 2021/8/3 下午5:51, Philippe Mathieu-Daudé 写道:
>>> On 8/3/21 11:33 AM, Thomas Huth wrote:
>>>> On 05/07/2021 10.40, Philippe Mathieu-Daudé wrote:
>>>>> Our infrastructure can handle fragmented packets up to
>>>>> NET_MAX_FRAG_SG_LIST (64) pieces. This hard limit has
>>>>> been proven enough in production for years. If it is
>>>>> reached, it is likely an evil crafted packet. Discard it.
>>>>>
>>>>> Include the qtest reproducer provided by Alexander Bulekov:
>>>>>
>>>>>     $ make check-qtest-i386
>>>>>     ...
>>>>>     Running test qtest-i386/fuzz-vmxnet3-test
>>>>>     qemu-system-i386: net/eth.c:334: void
>>>>> eth_setup_ip4_fragmentation(const void *, size_t, void *, size_t,
>>>>> size_t, size_t, _Bool):
>>>>>     Assertion `frag_offset % IP_FRAG_UNIT_SIZE == 0' failed.
>>>>>
>>>>> Cc: qemu-stable@nongnu.org
>>>>> Reported-by: OSS-Fuzz (Issue 35799)
>>>>> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/460
>>>>> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
>>>>> ---
>>>>>    hw/net/net_tx_pkt.c             |   8 ++
>>>>>    tests/qtest/fuzz-vmxnet3-test.c | 195 ++++++++++++++++++++++++++++++++
>>>>>    MAINTAINERS                     |   1 +
>>>>>    tests/qtest/meson.build         |   1 +
>>>>>    4 files changed, 205 insertions(+)
>>>>>    create mode 100644 tests/qtest/fuzz-vmxnet3-test.c
>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>>
>>>> Jason, I think this would even still qualify for QEMU v6.1 ?
>>> Yes, easy one for 6.1.
>>
>>
>> Yes, this will be included for rc3.
>>
>> Thanks
> 
> For some reasons it misses rc3.
> 
> I will include it for 6.2.
> 
> Sorry.

  Hi Jason,

looks like this fell through the cracks again ... could you maybe pick it up 
now for QEMU 7.1 ?

  Thomas
diff mbox series

Patch

diff --git a/hw/net/net_tx_pkt.c b/hw/net/net_tx_pkt.c
index 1f9aa59eca2..77e9729a7ba 100644
--- a/hw/net/net_tx_pkt.c
+++ b/hw/net/net_tx_pkt.c
@@ -590,6 +590,14 @@  static bool net_tx_pkt_do_sw_fragmentation(struct NetTxPkt *pkt,
         fragment_len = net_tx_pkt_fetch_fragment(pkt, &src_idx, &src_offset,
             fragment, &dst_idx);
 
+        if (dst_idx == NET_MAX_FRAG_SG_LIST && fragment_len > 0) {
+            /*
+             * The packet is too fragmented for our infrastructure
+             * (not enough iovec), don't even try to send.
+             */
+            return false;
+        }
+
         more_frags = (fragment_offset + fragment_len < pkt->payload_len);
 
         eth_setup_ip4_fragmentation(l2_iov_base, l2_iov_len, l3_iov_base,
diff --git a/tests/qtest/fuzz-vmxnet3-test.c b/tests/qtest/fuzz-vmxnet3-test.c
new file mode 100644
index 00000000000..d69009bf5ce
--- /dev/null
+++ b/tests/qtest/fuzz-vmxnet3-test.c
@@ -0,0 +1,195 @@ 
+/*
+ * QTest testcase for vmxnet3 device generated by fuzzer
+ *
+ * Copyright Red Hat
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqos/libqtest.h"
+
+/*
+ * https://gitlab.com/qemu-project/qemu/-/issues/460
+ */
+static void test_oss_35799_eth_setup_ip4_fragmentation(void)
+{
+    QTestState *s;
+
+    s = qtest_init("-machine q35 -m 32M -display none -nodefaults "
+                   "-device vmxnet3,netdev=net0 -netdev user,id=net0");
+    qtest_outl(s, 0xcf8, 0x80000814);
+    qtest_outl(s, 0xcfc, 0xe0000000);
+    qtest_outl(s, 0xcf8, 0x80000804);
+    qtest_outw(s, 0xcfc, 0x06);
+    qtest_outl(s, 0xcf8, 0x80000812);
+    qtest_outl(s, 0xcfc, 0x2000);
+    qtest_outl(s, 0xcf8, 0x80000815);
+    qtest_outb(s, 0xcfc, 0x40);
+    qtest_bufwrite(s, 0x0, "\xe1", 0x1);
+    qtest_bufwrite(s, 0x1, "\xfe", 0x1);
+    qtest_bufwrite(s, 0x2, "\xbe", 0x1);
+    qtest_bufwrite(s, 0x3, "\xba", 0x1);
+    qtest_bufwrite(s, 0x28, "\xff", 0x1);
+    qtest_bufwrite(s, 0x29, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2a, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2b, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2c, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2d, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2e, "\xff", 0x1);
+    qtest_bufwrite(s, 0x2f, "\xff", 0x1);
+    qtest_bufwrite(s, 0x37, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3e, "\x01", 0x1);
+    qtest_bufwrite(s, 0xe0004020, "\x00\x00\xfe\xca", 0x4);
+    qtest_bufwrite(s, 0x9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xd, "\x10", 0x1);
+    qtest_bufwrite(s, 0x12, "\x10", 0x1);
+    qtest_bufwrite(s, 0x19, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1b, "\x21", 0x1);
+    qtest_bufwrite(s, 0x1d, "\x0c", 0x1);
+    qtest_bufwrite(s, 0x2d, "\x00", 0x1);
+    qtest_bufwrite(s, 0x10000c, "\x08", 0x1);
+    qtest_bufwrite(s, 0x10000e, "\x45", 0x1);
+    qtest_bufwrite(s, 0x100017, "\x11", 0x1);
+    qtest_bufwrite(s, 0x20000600, "\x00", 0x1);
+    qtest_bufwrite(s, 0x38, "\x01", 0x1);
+    qtest_bufwrite(s, 0x39, "\x40", 0x1);
+    qtest_bufwrite(s, 0x48, "\x01", 0x1);
+    qtest_bufwrite(s, 0x49, "\x40", 0x1);
+    qtest_bufwrite(s, 0x58, "\x01", 0x1);
+    qtest_bufwrite(s, 0x59, "\x40", 0x1);
+    qtest_bufwrite(s, 0x68, "\x01", 0x1);
+    qtest_bufwrite(s, 0x69, "\x40", 0x1);
+    qtest_bufwrite(s, 0x78, "\x01", 0x1);
+    qtest_bufwrite(s, 0x79, "\x40", 0x1);
+    qtest_bufwrite(s, 0x88, "\x01", 0x1);
+    qtest_bufwrite(s, 0x89, "\x40", 0x1);
+    qtest_bufwrite(s, 0x98, "\x01", 0x1);
+    qtest_bufwrite(s, 0x99, "\x40", 0x1);
+    qtest_bufwrite(s, 0xa8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xa9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xb8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xb9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xc8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xc9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xd8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xd9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xe8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xe9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xf8, "\x01", 0x1);
+    qtest_bufwrite(s, 0xf9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x108, "\x01", 0x1);
+    qtest_bufwrite(s, 0x109, "\x40", 0x1);
+    qtest_bufwrite(s, 0x118, "\x01", 0x1);
+    qtest_bufwrite(s, 0x119, "\x40", 0x1);
+    qtest_bufwrite(s, 0x128, "\x01", 0x1);
+    qtest_bufwrite(s, 0x129, "\x40", 0x1);
+    qtest_bufwrite(s, 0x138, "\x01", 0x1);
+    qtest_bufwrite(s, 0x139, "\x40", 0x1);
+    qtest_bufwrite(s, 0x148, "\x01", 0x1);
+    qtest_bufwrite(s, 0x149, "\x40", 0x1);
+    qtest_bufwrite(s, 0x158, "\x01", 0x1);
+    qtest_bufwrite(s, 0x159, "\x40", 0x1);
+    qtest_bufwrite(s, 0x168, "\x01", 0x1);
+    qtest_bufwrite(s, 0x169, "\x40", 0x1);
+    qtest_bufwrite(s, 0x178, "\x01", 0x1);
+    qtest_bufwrite(s, 0x179, "\x40", 0x1);
+    qtest_bufwrite(s, 0x188, "\x01", 0x1);
+    qtest_bufwrite(s, 0x189, "\x40", 0x1);
+    qtest_bufwrite(s, 0x198, "\x01", 0x1);
+    qtest_bufwrite(s, 0x199, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1a8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1a9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1b8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1b9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1c8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1c9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1d8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1d9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1e8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1e9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x1f8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x1f9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x208, "\x01", 0x1);
+    qtest_bufwrite(s, 0x209, "\x40", 0x1);
+    qtest_bufwrite(s, 0x218, "\x01", 0x1);
+    qtest_bufwrite(s, 0x219, "\x40", 0x1);
+    qtest_bufwrite(s, 0x228, "\x01", 0x1);
+    qtest_bufwrite(s, 0x229, "\x40", 0x1);
+    qtest_bufwrite(s, 0x238, "\x01", 0x1);
+    qtest_bufwrite(s, 0x239, "\x40", 0x1);
+    qtest_bufwrite(s, 0x248, "\x01", 0x1);
+    qtest_bufwrite(s, 0x249, "\x40", 0x1);
+    qtest_bufwrite(s, 0x258, "\x01", 0x1);
+    qtest_bufwrite(s, 0x259, "\x40", 0x1);
+    qtest_bufwrite(s, 0x268, "\x01", 0x1);
+    qtest_bufwrite(s, 0x269, "\x40", 0x1);
+    qtest_bufwrite(s, 0x278, "\x01", 0x1);
+    qtest_bufwrite(s, 0x279, "\x40", 0x1);
+    qtest_bufwrite(s, 0x288, "\x01", 0x1);
+    qtest_bufwrite(s, 0x289, "\x40", 0x1);
+    qtest_bufwrite(s, 0x298, "\x01", 0x1);
+    qtest_bufwrite(s, 0x299, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2a8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2a9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2b8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2b9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2c8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2c9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2d8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2d9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2e8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2e9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x2f8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x2f9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x308, "\x01", 0x1);
+    qtest_bufwrite(s, 0x309, "\x40", 0x1);
+    qtest_bufwrite(s, 0x318, "\x01", 0x1);
+    qtest_bufwrite(s, 0x319, "\x40", 0x1);
+    qtest_bufwrite(s, 0x328, "\x01", 0x1);
+    qtest_bufwrite(s, 0x329, "\x40", 0x1);
+    qtest_bufwrite(s, 0x338, "\x01", 0x1);
+    qtest_bufwrite(s, 0x339, "\x40", 0x1);
+    qtest_bufwrite(s, 0x348, "\x01", 0x1);
+    qtest_bufwrite(s, 0x349, "\x40", 0x1);
+    qtest_bufwrite(s, 0x358, "\x01", 0x1);
+    qtest_bufwrite(s, 0x359, "\x40", 0x1);
+    qtest_bufwrite(s, 0x368, "\x01", 0x1);
+    qtest_bufwrite(s, 0x369, "\x40", 0x1);
+    qtest_bufwrite(s, 0x378, "\x01", 0x1);
+    qtest_bufwrite(s, 0x379, "\x40", 0x1);
+    qtest_bufwrite(s, 0x388, "\x01", 0x1);
+    qtest_bufwrite(s, 0x389, "\x40", 0x1);
+    qtest_bufwrite(s, 0x398, "\x01", 0x1);
+    qtest_bufwrite(s, 0x399, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3a8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3a9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3b8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3b9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3c8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3c9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3d8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3d9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3e8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3e9, "\x40", 0x1);
+    qtest_bufwrite(s, 0x3f8, "\x01", 0x1);
+    qtest_bufwrite(s, 0x3f9, "\x40", 0x1);
+    qtest_bufwrite(s, 0xd, "\x10", 0x1);
+    qtest_bufwrite(s, 0x20000600, "\x00", 0x1);
+    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_oss_35799_eth_setup_ip4_fragmentation",
+                       test_oss_35799_eth_setup_ip4_fragmentation);
+    }
+
+    return g_test_run();
+}
diff --git a/MAINTAINERS b/MAINTAINERS
index cb8f3ea2c2e..43e5050ad96 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2001,6 +2001,7 @@  S: Maintained
 F: hw/net/vmxnet*
 F: hw/scsi/vmw_pvscsi*
 F: tests/qtest/vmxnet3-test.c
+F: tests/qtest/fuzz-vmxnet3-test.c
 
 Rocker
 M: Jiri Pirko <jiri@resnulli.us>
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index b03e8541700..42add92e9d4 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -66,6 +66,7 @@ 
   (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'] : []) +   \
+  (config_all_devices.has_key('CONFIG_VMXNET3_PCI') ? ['fuzz-vmxnet3-test'] : []) +   \
   (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) +                 \
   qtests_pci +                                                                              \
   ['fdc-test',