diff mbox series

net: eepro100: validate various address values

Message ID 20210218140629.373646-1-ppandit@redhat.com (mailing list archive)
State New, archived
Headers show
Series net: eepro100: validate various address values | expand

Commit Message

Prasad Pandit Feb. 18, 2021, 2:06 p.m. UTC
From: Prasad J Pandit <pjp@fedoraproject.org>

While processing controller commands, eepro100 emulator gets
command unit(CU) base address OR receive unit (RU) base address
OR command block (CB) address from guest. If these values are not
checked, it may lead to an infinite loop kind of issues. Add checks
to avoid it.

Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
---
 hw/net/eepro100.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

no-reply@patchew.org Feb. 18, 2021, 2:18 p.m. UTC | #1
Patchew URL: https://patchew.org/QEMU/20210218140629.373646-1-ppandit@redhat.com/



Hi,

This series seems to have some coding style problems. See output below for
more information:

Type: series
Message-id: 20210218140629.373646-1-ppandit@redhat.com
Subject: [PATCH] net: eepro100: validate various address values

=== TEST SCRIPT BEGIN ===
#!/bin/bash
git rev-parse base > /dev/null || exit 0
git config --local diff.renamelimit 0
git config --local diff.renames True
git config --local diff.algorithm histogram
./scripts/checkpatch.pl --mailback base..
=== TEST SCRIPT END ===

Updating 3c8cf5a9c21ff8782164d1def7f44bd888713384
From https://github.com/patchew-project/qemu
 * [new tag]         patchew/20210218140629.373646-1-ppandit@redhat.com -> patchew/20210218140629.373646-1-ppandit@redhat.com
Switched to a new branch 'test'
1cccc2d net: eepro100: validate various address values

=== OUTPUT BEGIN ===
ERROR: space prohibited between function name and open parenthesis '('
#30: FILE: hw/net/eepro100.c:847:
+        assert (s->cb_address >= s->cu_base);

total: 1 errors, 0 warnings, 36 lines checked

Commit 1cccc2d7c7da (net: eepro100: validate various address values) has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.
=== OUTPUT END ===

Test command exited with code: 1


The full log is available at
http://patchew.org/logs/20210218140629.373646-1-ppandit@redhat.com/testing.checkpatch/?type=message.
---
Email generated automatically by Patchew [https://patchew.org/].
Please send your feedback to patchew-devel@redhat.com
Peter Maydell Feb. 18, 2021, 2:41 p.m. UTC | #2
On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
>
> From: Prasad J Pandit <pjp@fedoraproject.org>
>
> While processing controller commands, eepro100 emulator gets
> command unit(CU) base address OR receive unit (RU) base address
> OR command block (CB) address from guest. If these values are not
> checked, it may lead to an infinite loop kind of issues. Add checks
> to avoid it.
>
> Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> ---
>  hw/net/eepro100.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> index 16e95ef9cc..afa1c9b2aa 100644
> --- a/hw/net/eepro100.c
> +++ b/hw/net/eepro100.c
> @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
>          bool bit_i;
>          bool bit_nc;
>          uint16_t ok_status = STATUS_OK;
> -        s->cb_address = s->cu_base + s->cu_offset;
> +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> +        assert (s->cb_address >= s->cu_base);

We get these values from the guest; you can't just assert() on them.
You need to do something else.

My reading of the 8255x data sheet is that there is nothing
in the hardware that forbids the guest from programming the
device such that the cu_base + cu_offset wraps around:
http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
-- page 30 says that this is all doing 32-bit arithmetic
on addresses and doesn't say that there is any special case
handling by the device of overflow of that addition.

Your commit message isn't very clear about what the failure
case is here, but I think the fix has to be something
different from this.

thanks
-- PMM
Stefan Weil Feb. 18, 2021, 4:10 p.m. UTC | #3
Am 18.02.21 um 15:41 schrieb Peter Maydell:

> On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
>> From: Prasad J Pandit <pjp@fedoraproject.org>
>>
>> While processing controller commands, eepro100 emulator gets
>> command unit(CU) base address OR receive unit (RU) base address
>> OR command block (CB) address from guest. If these values are not
>> checked, it may lead to an infinite loop kind of issues. Add checks
>> to avoid it.
>>
>> Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
>> Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
>> ---
>>   hw/net/eepro100.c | 8 +++++++-
>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
>> index 16e95ef9cc..afa1c9b2aa 100644
>> --- a/hw/net/eepro100.c
>> +++ b/hw/net/eepro100.c
>> @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
>>           bool bit_i;
>>           bool bit_nc;
>>           uint16_t ok_status = STATUS_OK;
>> -        s->cb_address = s->cu_base + s->cu_offset;
>> +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
>> +        assert (s->cb_address >= s->cu_base);
> We get these values from the guest; you can't just assert() on them.
> You need to do something else.
>
> My reading of the 8255x data sheet is that there is nothing
> in the hardware that forbids the guest from programming the
> device such that the cu_base + cu_offset wraps around:
> http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> -- page 30 says that this is all doing 32-bit arithmetic
> on addresses and doesn't say that there is any special case
> handling by the device of overflow of that addition.
>
> Your commit message isn't very clear about what the failure
> case is here, but I think the fix has to be something
> different from this.


I agree with Peter. The hardware emulation in QEMU should try to do the 
same as the real hardware.

Assertions (not with assert(), but with g_assert() and related 
functions) are good to catch implementation errors in QEMU code, but not 
to catch bad programming in guest code.

In this special case the emulation code already includes a hack to catch 
endless loops caused by buggy guest code: it has a limit of 16 cycles 
and then prints a log message. This is a compromise to emulate the real 
hardware as good as possible without making QEMU running an endless loop 
or requiring an extra thread to emulate the hardware loop until it is 
stopped by new I/O operations.

Stefan
Alexander Bulekov Feb. 19, 2021, 1:54 a.m. UTC | #4
On 210218 1441, Peter Maydell wrote:
> On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
> >
> > From: Prasad J Pandit <pjp@fedoraproject.org>
> >
> > While processing controller commands, eepro100 emulator gets
> > command unit(CU) base address OR receive unit (RU) base address
> > OR command block (CB) address from guest. If these values are not
> > checked, it may lead to an infinite loop kind of issues. Add checks
> > to avoid it.
> >
> > Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > ---
> >  hw/net/eepro100.c | 8 +++++++-
> >  1 file changed, 7 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> > index 16e95ef9cc..afa1c9b2aa 100644
> > --- a/hw/net/eepro100.c
> > +++ b/hw/net/eepro100.c
> > @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
> >          bool bit_i;
> >          bool bit_nc;
> >          uint16_t ok_status = STATUS_OK;
> > -        s->cb_address = s->cu_base + s->cu_offset;
> > +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> > +        assert (s->cb_address >= s->cu_base);
> 
> We get these values from the guest; you can't just assert() on them.
> You need to do something else.
> 
> My reading of the 8255x data sheet is that there is nothing
> in the hardware that forbids the guest from programming the
> device such that the cu_base + cu_offset wraps around:
> http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> -- page 30 says that this is all doing 32-bit arithmetic
> on addresses and doesn't say that there is any special case
> handling by the device of overflow of that addition.
> 
> Your commit message isn't very clear about what the failure
> case is here, but I think the fix has to be something
> different from this.

Maybe the infinite loop mentioned in the commit message is actually a
DMA recursion issue? I'm providing a reproducer for a DMA re-entracy
issue below. With this patch applied, the reproducer triggers the
assert(), rather than overflowing the stack, so maybe it is the same
issue?
-Alex

cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
-qtest stdio
outl 0xcf8 0x80001014
outl 0xcfc 0xc000
outl 0xcf8 0x80001010
outl 0xcfc 0xe0020000
outl 0xcf8 0x80001004
outw 0xcfc 0x7
write 0x1ffffc0b 0x1 0x55
write 0x1ffffc0c 0x1 0xfc
write 0x1ffffc0d 0x1 0x46
write 0x1ffffc0e 0x1 0x07
write 0x746fc59 0x1 0x02
write 0x746fc5b 0x1 0x02
write 0x746fc5c 0x1 0xe0
write 0x4 0x1 0x07
write 0x5 0x1 0xfc
write 0x6 0x1 0xff
write 0x7 0x1 0x1f
outw 0xc002 0x20
EOF

Formatted for committing a regression-test:

static void test_fuzz(void)
{
    QTestState *s =
        qtest_init("-display none , -m 512M -device i82559er,netdev=net0 "
                   "-netdev user,id=net0 -nodefaults");
    qtest_outl(s, 0xcf8, 0x80001014);
    qtest_outl(s, 0xcfc, 0xc000);
    qtest_outl(s, 0xcf8, 0x80001010);
    qtest_outl(s, 0xcfc, 0xe0020000);
    qtest_outl(s, 0xcf8, 0x80001004);
    qtest_outw(s, 0xcfc, 0x7);
    qtest_bufwrite(s, 0x1ffffc0b, "\x55", 0x1);
    qtest_bufwrite(s, 0x1ffffc0c, "\xfc", 0x1);
    qtest_bufwrite(s, 0x1ffffc0d, "\x46", 0x1);
    qtest_bufwrite(s, 0x1ffffc0e, "\x07", 0x1);
    qtest_bufwrite(s, 0x746fc59, "\x02", 0x1);
    qtest_bufwrite(s, 0x746fc5b, "\x02", 0x1);
    qtest_bufwrite(s, 0x746fc5c, "\xe0", 0x1);
    qtest_bufwrite(s, 0x4, "\x07", 0x1);
    qtest_bufwrite(s, 0x5, "\xfc", 0x1);
    qtest_bufwrite(s, 0x6, "\xff", 0x1);
    qtest_bufwrite(s, 0x7, "\x1f", 0x1);
    qtest_outw(s, 0xc002, 0x20);
    qtest_quit(s);
}


> 
> thanks
> -- PMM
>
Li Qiang Feb. 19, 2021, 2:06 a.m. UTC | #5
Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午9:56写道:
>
> On 210218 1441, Peter Maydell wrote:
> > On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
> > >
> > > From: Prasad J Pandit <pjp@fedoraproject.org>
> > >
> > > While processing controller commands, eepro100 emulator gets
> > > command unit(CU) base address OR receive unit (RU) base address
> > > OR command block (CB) address from guest. If these values are not
> > > checked, it may lead to an infinite loop kind of issues. Add checks
> > > to avoid it.


So could you please provide a backtrack?


Thanks,
Li Qiang

> > >
> > > Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> > > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > > ---
> > >  hw/net/eepro100.c | 8 +++++++-
> > >  1 file changed, 7 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> > > index 16e95ef9cc..afa1c9b2aa 100644
> > > --- a/hw/net/eepro100.c
> > > +++ b/hw/net/eepro100.c
> > > @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
> > >          bool bit_i;
> > >          bool bit_nc;
> > >          uint16_t ok_status = STATUS_OK;
> > > -        s->cb_address = s->cu_base + s->cu_offset;
> > > +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> > > +        assert (s->cb_address >= s->cu_base);
> >
> > We get these values from the guest; you can't just assert() on them.
> > You need to do something else.
> >
> > My reading of the 8255x data sheet is that there is nothing
> > in the hardware that forbids the guest from programming the
> > device such that the cu_base + cu_offset wraps around:
> > http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> > -- page 30 says that this is all doing 32-bit arithmetic
> > on addresses and doesn't say that there is any special case
> > handling by the device of overflow of that addition.
> >
> > Your commit message isn't very clear about what the failure
> > case is here, but I think the fix has to be something
> > different from this.
>
> Maybe the infinite loop mentioned in the commit message is actually a
> DMA recursion issue? I'm providing a reproducer for a DMA re-entracy
> issue below. With this patch applied, the reproducer triggers the
> assert(), rather than overflowing the stack, so maybe it is the same
> issue?
> -Alex
>
> cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
> 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
> -qtest stdio
> outl 0xcf8 0x80001014
> outl 0xcfc 0xc000
> outl 0xcf8 0x80001010
> outl 0xcfc 0xe0020000
> outl 0xcf8 0x80001004
> outw 0xcfc 0x7
> write 0x1ffffc0b 0x1 0x55
> write 0x1ffffc0c 0x1 0xfc
> write 0x1ffffc0d 0x1 0x46
> write 0x1ffffc0e 0x1 0x07
> write 0x746fc59 0x1 0x02
> write 0x746fc5b 0x1 0x02
> write 0x746fc5c 0x1 0xe0
> write 0x4 0x1 0x07
> write 0x5 0x1 0xfc
> write 0x6 0x1 0xff
> write 0x7 0x1 0x1f
> outw 0xc002 0x20
> EOF
>
> Formatted for committing a regression-test:
>
> static void test_fuzz(void)
> {
>     QTestState *s =
>         qtest_init("-display none , -m 512M -device i82559er,netdev=net0 "
>                    "-netdev user,id=net0 -nodefaults");
>     qtest_outl(s, 0xcf8, 0x80001014);
>     qtest_outl(s, 0xcfc, 0xc000);
>     qtest_outl(s, 0xcf8, 0x80001010);
>     qtest_outl(s, 0xcfc, 0xe0020000);
>     qtest_outl(s, 0xcf8, 0x80001004);
>     qtest_outw(s, 0xcfc, 0x7);
>     qtest_bufwrite(s, 0x1ffffc0b, "\x55", 0x1);
>     qtest_bufwrite(s, 0x1ffffc0c, "\xfc", 0x1);
>     qtest_bufwrite(s, 0x1ffffc0d, "\x46", 0x1);
>     qtest_bufwrite(s, 0x1ffffc0e, "\x07", 0x1);
>     qtest_bufwrite(s, 0x746fc59, "\x02", 0x1);
>     qtest_bufwrite(s, 0x746fc5b, "\x02", 0x1);
>     qtest_bufwrite(s, 0x746fc5c, "\xe0", 0x1);
>     qtest_bufwrite(s, 0x4, "\x07", 0x1);
>     qtest_bufwrite(s, 0x5, "\xfc", 0x1);
>     qtest_bufwrite(s, 0x6, "\xff", 0x1);
>     qtest_bufwrite(s, 0x7, "\x1f", 0x1);
>     qtest_outw(s, 0xc002, 0x20);
>     qtest_quit(s);
> }
>
>
> >
> > thanks
> > -- PMM
> >
>
Alexander Bulekov Feb. 19, 2021, 2:14 a.m. UTC | #6
On 210219 1006, Li Qiang wrote:
> Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午9:56写道:
> >
> > On 210218 1441, Peter Maydell wrote:
> > > On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
> > > >
> > > > From: Prasad J Pandit <pjp@fedoraproject.org>
> > > >
> > > > While processing controller commands, eepro100 emulator gets
> > > > command unit(CU) base address OR receive unit (RU) base address
> > > > OR command block (CB) address from guest. If these values are not
> > > > checked, it may lead to an infinite loop kind of issues. Add checks
> > > > to avoid it.
> 
> 
> So could you please provide a backtrack?
>

I don't know if you are asking me or Prasad, but here is the stacktrace
for the one I provided:
==2715275==ERROR: AddressSanitizer: stack-overflow on address
0x7ffc5262ba28 (pc 0x55d83b103ac6 bp 0x7ffc5262c270 sp 0x7ffc5262ba30
T0)
#0 in __asan_memcpy (qemu-system-i386+0x2aa3ac6)
#1 in flatview_do_translate ../softmmu/physmem.c:518:12
#2 in flatview_translate ../softmmu/physmem.c:568:15
#3 in flatview_read ../softmmu/physmem.c:2878:10
#4 in address_space_read_full ../softmmu/physmem.c:2892:18
#5 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
#6 in dma_memory_rw include/sysemu/dma.h:127:12
#7 in pci_dma_rw include/hw/pci/pci.h:803:12
#8 in pci_dma_read include/hw/pci/pci.h:821:12
#9 in read_cb ../hw/net/eepro100.c:726:5
#10 in action_command ../hw/net/eepro100.c:847:9
#11 in eepro100_cu_command ../hw/net/eepro100.c:969:13
#12 in eepro100_write_command ../hw/net/eepro100.c:1063:5
#13 in eepro100_write2 ../hw/net/eepro100.c:1510:9
#14 in eepro100_write ../hw/net/eepro100.c:1593:9
#15 in memory_region_write_accessor ../softmmu/memory.c:491:5
#16 in access_with_adjusted_size ../softmmu/memory.c:552:18
#17 in memory_region_dispatch_write ../softmmu/memory.c
#18 in flatview_write_continue ../softmmu/physmem.c:2776:23
#19 in flatview_write ../softmmu/physmem.c:2816:14
#20 in address_space_write ../softmmu/physmem.c:2908:18
#21 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
#22 in dma_memory_rw include/sysemu/dma.h:127:12
#23 in dma_memory_write include/sysemu/dma.h:163:12
#24 in stw_le_dma include/sysemu/dma.h:259:1
#25 in stw_le_pci_dma include/hw/pci/pci.h:855:1
#26 in action_command ../hw/net/eepro100.c:913:9
#27 in eepro100_cu_command ../hw/net/eepro100.c:969:13
#28 in eepro100_write_command ../hw/net/eepro100.c:1063:5
#29 in eepro100_write2 ../hw/net/eepro100.c:1510:9
#30 in eepro100_write ../hw/net/eepro100.c:1593:9
... till there's no more stack ...

> 
> Thanks,
> Li Qiang
> 
> > > >
> > > > Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> > > > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > > > ---
> > > >  hw/net/eepro100.c | 8 +++++++-
> > > >  1 file changed, 7 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> > > > index 16e95ef9cc..afa1c9b2aa 100644
> > > > --- a/hw/net/eepro100.c
> > > > +++ b/hw/net/eepro100.c
> > > > @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
> > > >          bool bit_i;
> > > >          bool bit_nc;
> > > >          uint16_t ok_status = STATUS_OK;
> > > > -        s->cb_address = s->cu_base + s->cu_offset;
> > > > +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> > > > +        assert (s->cb_address >= s->cu_base);
> > >
> > > We get these values from the guest; you can't just assert() on them.
> > > You need to do something else.
> > >
> > > My reading of the 8255x data sheet is that there is nothing
> > > in the hardware that forbids the guest from programming the
> > > device such that the cu_base + cu_offset wraps around:
> > > http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> > > -- page 30 says that this is all doing 32-bit arithmetic
> > > on addresses and doesn't say that there is any special case
> > > handling by the device of overflow of that addition.
> > >
> > > Your commit message isn't very clear about what the failure
> > > case is here, but I think the fix has to be something
> > > different from this.
> >
> > Maybe the infinite loop mentioned in the commit message is actually a
> > DMA recursion issue? I'm providing a reproducer for a DMA re-entracy
> > issue below. With this patch applied, the reproducer triggers the
> > assert(), rather than overflowing the stack, so maybe it is the same
> > issue?
> > -Alex
> >
> > cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
> > 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
> > -qtest stdio
> > outl 0xcf8 0x80001014
> > outl 0xcfc 0xc000
> > outl 0xcf8 0x80001010
> > outl 0xcfc 0xe0020000
> > outl 0xcf8 0x80001004
> > outw 0xcfc 0x7
> > write 0x1ffffc0b 0x1 0x55
> > write 0x1ffffc0c 0x1 0xfc
> > write 0x1ffffc0d 0x1 0x46
> > write 0x1ffffc0e 0x1 0x07
> > write 0x746fc59 0x1 0x02
> > write 0x746fc5b 0x1 0x02
> > write 0x746fc5c 0x1 0xe0
> > write 0x4 0x1 0x07
> > write 0x5 0x1 0xfc
> > write 0x6 0x1 0xff
> > write 0x7 0x1 0x1f
> > outw 0xc002 0x20
> > EOF
> >
> > Formatted for committing a regression-test:
> >
> > static void test_fuzz(void)
> > {
> >     QTestState *s =
> >         qtest_init("-display none , -m 512M -device i82559er,netdev=net0 "
> >                    "-netdev user,id=net0 -nodefaults");
> >     qtest_outl(s, 0xcf8, 0x80001014);
> >     qtest_outl(s, 0xcfc, 0xc000);
> >     qtest_outl(s, 0xcf8, 0x80001010);
> >     qtest_outl(s, 0xcfc, 0xe0020000);
> >     qtest_outl(s, 0xcf8, 0x80001004);
> >     qtest_outw(s, 0xcfc, 0x7);
> >     qtest_bufwrite(s, 0x1ffffc0b, "\x55", 0x1);
> >     qtest_bufwrite(s, 0x1ffffc0c, "\xfc", 0x1);
> >     qtest_bufwrite(s, 0x1ffffc0d, "\x46", 0x1);
> >     qtest_bufwrite(s, 0x1ffffc0e, "\x07", 0x1);
> >     qtest_bufwrite(s, 0x746fc59, "\x02", 0x1);
> >     qtest_bufwrite(s, 0x746fc5b, "\x02", 0x1);
> >     qtest_bufwrite(s, 0x746fc5c, "\xe0", 0x1);
> >     qtest_bufwrite(s, 0x4, "\x07", 0x1);
> >     qtest_bufwrite(s, 0x5, "\xfc", 0x1);
> >     qtest_bufwrite(s, 0x6, "\xff", 0x1);
> >     qtest_bufwrite(s, 0x7, "\x1f", 0x1);
> >     qtest_outw(s, 0xc002, 0x20);
> >     qtest_quit(s);
> > }
> >
> >
> > >
> > > thanks
> > > -- PMM
> > >
> >
Li Qiang Feb. 19, 2021, 4:43 a.m. UTC | #7
Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午10:15写道:
>
> On 210219 1006, Li Qiang wrote:
> > Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午9:56写道:
> > >
> > > On 210218 1441, Peter Maydell wrote:
> > > > On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
> > > > >
> > > > > From: Prasad J Pandit <pjp@fedoraproject.org>
> > > > >
> > > > > While processing controller commands, eepro100 emulator gets
> > > > > command unit(CU) base address OR receive unit (RU) base address
> > > > > OR command block (CB) address from guest. If these values are not
> > > > > checked, it may lead to an infinite loop kind of issues. Add checks
> > > > > to avoid it.
> >
> >
> > So could you please provide a backtrack?
> >
>
> I don't know if you are asking me or Prasad, but here is the stacktrace


Yes, a typical DMA reentry issue.
Any progress to solve these DMA reentry issues? seems more and more
this kind of issues.
Just return from the busy things as a new father and not focus this
quite a time.

Thanks,
Li Qiang

> for the one I provided:
> ==2715275==ERROR: AddressSanitizer: stack-overflow on address
> 0x7ffc5262ba28 (pc 0x55d83b103ac6 bp 0x7ffc5262c270 sp 0x7ffc5262ba30
> T0)
> #0 in __asan_memcpy (qemu-system-i386+0x2aa3ac6)
> #1 in flatview_do_translate ../softmmu/physmem.c:518:12
> #2 in flatview_translate ../softmmu/physmem.c:568:15
> #3 in flatview_read ../softmmu/physmem.c:2878:10
> #4 in address_space_read_full ../softmmu/physmem.c:2892:18
> #5 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
> #6 in dma_memory_rw include/sysemu/dma.h:127:12
> #7 in pci_dma_rw include/hw/pci/pci.h:803:12
> #8 in pci_dma_read include/hw/pci/pci.h:821:12
> #9 in read_cb ../hw/net/eepro100.c:726:5
> #10 in action_command ../hw/net/eepro100.c:847:9
> #11 in eepro100_cu_command ../hw/net/eepro100.c:969:13
> #12 in eepro100_write_command ../hw/net/eepro100.c:1063:5
> #13 in eepro100_write2 ../hw/net/eepro100.c:1510:9
> #14 in eepro100_write ../hw/net/eepro100.c:1593:9
> #15 in memory_region_write_accessor ../softmmu/memory.c:491:5
> #16 in access_with_adjusted_size ../softmmu/memory.c:552:18
> #17 in memory_region_dispatch_write ../softmmu/memory.c
> #18 in flatview_write_continue ../softmmu/physmem.c:2776:23
> #19 in flatview_write ../softmmu/physmem.c:2816:14
> #20 in address_space_write ../softmmu/physmem.c:2908:18
> #21 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
> #22 in dma_memory_rw include/sysemu/dma.h:127:12
> #23 in dma_memory_write include/sysemu/dma.h:163:12
> #24 in stw_le_dma include/sysemu/dma.h:259:1
> #25 in stw_le_pci_dma include/hw/pci/pci.h:855:1
> #26 in action_command ../hw/net/eepro100.c:913:9
> #27 in eepro100_cu_command ../hw/net/eepro100.c:969:13
> #28 in eepro100_write_command ../hw/net/eepro100.c:1063:5
> #29 in eepro100_write2 ../hw/net/eepro100.c:1510:9
> #30 in eepro100_write ../hw/net/eepro100.c:1593:9
> ... till there's no more stack ...
>
> >
> > Thanks,
> > Li Qiang
> >
> > > > >
> > > > > Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> > > > > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > > > > ---
> > > > >  hw/net/eepro100.c | 8 +++++++-
> > > > >  1 file changed, 7 insertions(+), 1 deletion(-)
> > > > >
> > > > > diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> > > > > index 16e95ef9cc..afa1c9b2aa 100644
> > > > > --- a/hw/net/eepro100.c
> > > > > +++ b/hw/net/eepro100.c
> > > > > @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
> > > > >          bool bit_i;
> > > > >          bool bit_nc;
> > > > >          uint16_t ok_status = STATUS_OK;
> > > > > -        s->cb_address = s->cu_base + s->cu_offset;
> > > > > +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> > > > > +        assert (s->cb_address >= s->cu_base);
> > > >
> > > > We get these values from the guest; you can't just assert() on them.
> > > > You need to do something else.
> > > >
> > > > My reading of the 8255x data sheet is that there is nothing
> > > > in the hardware that forbids the guest from programming the
> > > > device such that the cu_base + cu_offset wraps around:
> > > > http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> > > > -- page 30 says that this is all doing 32-bit arithmetic
> > > > on addresses and doesn't say that there is any special case
> > > > handling by the device of overflow of that addition.
> > > >
> > > > Your commit message isn't very clear about what the failure
> > > > case is here, but I think the fix has to be something
> > > > different from this.
> > >
> > > Maybe the infinite loop mentioned in the commit message is actually a
> > > DMA recursion issue? I'm providing a reproducer for a DMA re-entracy
> > > issue below. With this patch applied, the reproducer triggers the
> > > assert(), rather than overflowing the stack, so maybe it is the same
> > > issue?
> > > -Alex
> > >
> > > cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
> > > 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
> > > -qtest stdio
> > > outl 0xcf8 0x80001014
> > > outl 0xcfc 0xc000
> > > outl 0xcf8 0x80001010
> > > outl 0xcfc 0xe0020000
> > > outl 0xcf8 0x80001004
> > > outw 0xcfc 0x7
> > > write 0x1ffffc0b 0x1 0x55
> > > write 0x1ffffc0c 0x1 0xfc
> > > write 0x1ffffc0d 0x1 0x46
> > > write 0x1ffffc0e 0x1 0x07
> > > write 0x746fc59 0x1 0x02
> > > write 0x746fc5b 0x1 0x02
> > > write 0x746fc5c 0x1 0xe0
> > > write 0x4 0x1 0x07
> > > write 0x5 0x1 0xfc
> > > write 0x6 0x1 0xff
> > > write 0x7 0x1 0x1f
> > > outw 0xc002 0x20
> > > EOF
> > >
> > > Formatted for committing a regression-test:
> > >
> > > static void test_fuzz(void)
> > > {
> > >     QTestState *s =
> > >         qtest_init("-display none , -m 512M -device i82559er,netdev=net0 "
> > >                    "-netdev user,id=net0 -nodefaults");
> > >     qtest_outl(s, 0xcf8, 0x80001014);
> > >     qtest_outl(s, 0xcfc, 0xc000);
> > >     qtest_outl(s, 0xcf8, 0x80001010);
> > >     qtest_outl(s, 0xcfc, 0xe0020000);
> > >     qtest_outl(s, 0xcf8, 0x80001004);
> > >     qtest_outw(s, 0xcfc, 0x7);
> > >     qtest_bufwrite(s, 0x1ffffc0b, "\x55", 0x1);
> > >     qtest_bufwrite(s, 0x1ffffc0c, "\xfc", 0x1);
> > >     qtest_bufwrite(s, 0x1ffffc0d, "\x46", 0x1);
> > >     qtest_bufwrite(s, 0x1ffffc0e, "\x07", 0x1);
> > >     qtest_bufwrite(s, 0x746fc59, "\x02", 0x1);
> > >     qtest_bufwrite(s, 0x746fc5b, "\x02", 0x1);
> > >     qtest_bufwrite(s, 0x746fc5c, "\xe0", 0x1);
> > >     qtest_bufwrite(s, 0x4, "\x07", 0x1);
> > >     qtest_bufwrite(s, 0x5, "\xfc", 0x1);
> > >     qtest_bufwrite(s, 0x6, "\xff", 0x1);
> > >     qtest_bufwrite(s, 0x7, "\x1f", 0x1);
> > >     qtest_outw(s, 0xc002, 0x20);
> > >     qtest_quit(s);
> > > }
> > >
> > >
> > > >
> > > > thanks
> > > > -- PMM
> > > >
> > >
Prasad Pandit Feb. 19, 2021, 6:11 a.m. UTC | #8
Hello Alex, Stefan, all

+-- On Thu, 18 Feb 2021, Alexander Bulekov wrote --+
| Maybe the infinite loop mentioned in the commit message is actually a DMA 
| recursion issue? I'm providing a reproducer for a DMA re-entracy issue 
| below. With this patch applied, the reproducer triggers the assert(), rather 
| than overflowing the stack, so maybe it is the same issue? -Alex
| 
| cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
| 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
| -qtest stdio
| outl 0xcf8 0x80001014
| outl 0xcfc 0xc000
| outl 0xcf8 0x80001010
| outl 0xcfc 0xe0020000
| outl 0xcf8 0x80001004
| outw 0xcfc 0x7
| write 0x1ffffc0b 0x1 0x55
| write 0x1ffffc0c 0x1 0xfc
| write 0x1ffffc0d 0x1 0x46
| write 0x1ffffc0e 0x1 0x07
| write 0x746fc59 0x1 0x02
| write 0x746fc5b 0x1 0x02
| write 0x746fc5c 0x1 0xe0
| write 0x4 0x1 0x07
| write 0x5 0x1 0xfc
| write 0x6 0x1 0xff
| write 0x7 0x1 0x1f
| outw 0xc002 0x20
| EOF
| 

* Yes, it is an infinite recursion induced stack overflow. I should've said 
  recursion instead of loop.

  Thank you for sharing a reproducer and the stack trace.


+-- On Thu, 18 Feb 2021, Stefan Weil wrote --+
| Am 18.02.21 um 15:41 schrieb Peter Maydell:
||  +        assert (s->cb_address >= s->cu_base);
| > We get these values from the guest; you can't just assert() on them. You 
| > need to do something else.
| > http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
|
| I agree with Peter. The hardware emulation in QEMU should try to do the same 
| as the real hardware.

* Agreed.

* While the manual does not say how to handle uint32_t overflow in above 
  'cb_address' calculation, I doubt if overflow is expected.

  +    if (s->cb_address < s->cu_base) {
  +        logout ("invalid cb_address: %s: %u\n", __func__, s->cb_address);
  +        break;
  +    }

  I tried above fix first, it does not seem to arrest the recurssion induced 
  stack overflow. Hence assert(3).

* I also tried to find out if there's any cap on the 'cu_offset' value OR 
  number of command units (cu) that can be addressed.

  But in linear addressing mode offset is a 32bit value with base address set 
  to zero(0).

  And in 32bit segmented addressing mode 'offset' is 16bit value with 
  non-zero(0) base address. ie. maximum offset could be about ~4K for 16byte 
  command block IIUC. I'm not sure if segmented addressing mode is supported.

* I'd appreciate if you could suggest a right way to fix it OR propose/post 
  another patch. I'm okay either way.

Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
8685 545E B54C 486B C6EB 271E E285 8B5A F050 DE8D
Stefan Weil Feb. 19, 2021, 8:08 a.m. UTC | #9
Am 19.02.21 um 07:11 schrieb P J P:

>    Hello Alex, Stefan, all
>
> +-- On Thu, 18 Feb 2021, Alexander Bulekov wrote --+
> | Maybe the infinite loop mentioned in the commit message is actually a DMA
> | recursion issue? I'm providing a reproducer for a DMA re-entracy issue
> | below. With this patch applied, the reproducer triggers the assert(), rather
> | than overflowing the stack, so maybe it is the same issue? -Alex
> |
> | cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
> | 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
> | -qtest stdio
> | outl 0xcf8 0x80001014
> | outl 0xcfc 0xc000
> | outl 0xcf8 0x80001010
> | outl 0xcfc 0xe0020000
> | outl 0xcf8 0x80001004
> | outw 0xcfc 0x7
> | write 0x1ffffc0b 0x1 0x55
> | write 0x1ffffc0c 0x1 0xfc
> | write 0x1ffffc0d 0x1 0x46
> | write 0x1ffffc0e 0x1 0x07
> | write 0x746fc59 0x1 0x02
> | write 0x746fc5b 0x1 0x02
> | write 0x746fc5c 0x1 0xe0
> | write 0x4 0x1 0x07
> | write 0x5 0x1 0xfc
> | write 0x6 0x1 0xff
> | write 0x7 0x1 0x1f
> | outw 0xc002 0x20
> | EOF
> |
>
> * Yes, it is an infinite recursion induced stack overflow. I should've said
>    recursion instead of loop.
>
>    Thank you for sharing a reproducer and the stack trace.


Okay, I can confirm the infinite recursion now.

The test case triggers memory writes by the hardware which cause new 
actions of the same hardware and so on.

I don't know how the real hardware would handle that case.

For QEMU we can extend the current code which tries to prevent endless 
loops: the device status EEPRO100State can be extended by a recursion 
counter to limit the number of recursions, or maybe a boolean flag could 
be used to stop any recursion of action_command(). I prefer the second 
variant (no recursion at all) and suggest to add a diagnostic message as 
well like it is done for the endless loop case.

Stefan
Stefan Weil Feb. 19, 2021, 8:26 a.m. UTC | #10
Am 19.02.21 um 09:08 schrieb Stefan Weil:

> Okay, I can confirm the infinite recursion now.
>
> The test case triggers memory writes by the hardware which cause new 
> actions of the same hardware and so on.
>
> I don't know how the real hardware would handle that case.
>
> For QEMU we can extend the current code which tries to prevent endless 
> loops: the device status EEPRO100State can be extended by a recursion 
> counter to limit the number of recursions, or maybe a boolean flag 
> could be used to stop any recursion of action_command(). I prefer the 
> second variant (no recursion at all) and suggest to add a diagnostic 
> message as well like it is done for the endless loop case.


If there are no recursions in normal use, the following patch should work:

diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 16e95ef9cc..2474cf3dc2 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -279,6 +279,9 @@ typedef struct {
      /* Quasi static device properties (no need to save them). */
      uint16_t stats_size;
      bool has_extended_tcb_support;
+
+    /* Flag to avoid recursions. */
+    bool busy;
  } EEPRO100State;

  /* Word indices in EEPROM. */
@@ -837,6 +840,14 @@ static void action_command(EEPRO100State *s)
         Therefore we limit the number of iterations. */
      unsigned max_loop_count = 16;

+    if (s->busy) {
+        /* Prevent recursions. */
+        logout("recursion in %s:%u\n", __FILE__, __LINE__);
+        return;
+    }
+
+    s->busy = true;
+
      for (;;) {
          bool bit_el;
          bool bit_s;
@@ -933,6 +944,7 @@ static void action_command(EEPRO100State *s)
      }
      TRACE(OTHER, logout("CU list empty\n"));
      /* List is empty. Now CU is idle or suspended. */
+    s->busy = false;
  }

  static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
Prasad Pandit Feb. 19, 2021, 9:26 a.m. UTC | #11
Hello Stefan,

+-- On Fri, 19 Feb 2021, Stefan Weil wrote --+
| If there are no recursions in normal use, the following patch should work:
| 
| diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
| index 16e95ef9cc..2474cf3dc2 100644
| --- a/hw/net/eepro100.c
| +++ b/hw/net/eepro100.c
| @@ -279,6 +279,9 @@ typedef struct {
|      /* Quasi static device properties (no need to save them). */
|      uint16_t stats_size;
|      bool has_extended_tcb_support;
| +
| +    /* Flag to avoid recursions. */
| +    bool busy;
|  } EEPRO100State;
| 
|  /* Word indices in EEPROM. */
| @@ -837,6 +840,14 @@ static void action_command(EEPRO100State *s)
|         Therefore we limit the number of iterations. */
|      unsigned max_loop_count = 16;
| 
| +    if (s->busy) {
| +        /* Prevent recursions. */
| +        logout("recursion in %s:%u\n", __FILE__, __LINE__);
| +        return;
| +    }
| +
| +    s->busy = true;
| +
|      for (;;) {
|          bool bit_el;
|          bool bit_s;
| @@ -933,6 +944,7 @@ static void action_command(EEPRO100State *s)
|      }
|      TRACE(OTHER, logout("CU list empty\n"));
|      /* List is empty. Now CU is idle or suspended. */
| +    s->busy = false;
|  }
| 
|  static void eepro100_cu_command(EEPRO100State * s, uint8_t val)

Please see:
  -> https://ruhr-uni-bochum.sciebo.de/s/NNWP2GfwzYKeKwE?path=%2Feepro100_stackoverflow1

* It does not seem to address above case.

Thank you.
--
Prasad J Pandit / Red Hat Product Security Team
8685 545E B54C 486B C6EB 271E E285 8B5A F050 DE8D
Stefan Weil Feb. 19, 2021, 9:52 a.m. UTC | #12
Am 19.02.21 um 10:26 schrieb P J P:

>    Hello Stefan,
>
> +-- On Fri, 19 Feb 2021, Stefan Weil wrote --+
> | If there are no recursions in normal use, the following patch should work:
> |
> | diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> | index 16e95ef9cc..2474cf3dc2 100644
> | --- a/hw/net/eepro100.c
> | +++ b/hw/net/eepro100.c
> | @@ -279,6 +279,9 @@ typedef struct {
> |      /* Quasi static device properties (no need to save them). */
> |      uint16_t stats_size;
> |      bool has_extended_tcb_support;
> | +
> | +    /* Flag to avoid recursions. */
> | +    bool busy;
> |  } EEPRO100State;
> |
> |  /* Word indices in EEPROM. */
> | @@ -837,6 +840,14 @@ static void action_command(EEPRO100State *s)
> |         Therefore we limit the number of iterations. */
> |      unsigned max_loop_count = 16;
> |
> | +    if (s->busy) {
> | +        /* Prevent recursions. */
> | +        logout("recursion in %s:%u\n", __FILE__, __LINE__);
> | +        return;
> | +    }
> | +
> | +    s->busy = true;
> | +
> |      for (;;) {
> |          bool bit_el;
> |          bool bit_s;
> | @@ -933,6 +944,7 @@ static void action_command(EEPRO100State *s)
> |      }
> |      TRACE(OTHER, logout("CU list empty\n"));
> |      /* List is empty. Now CU is idle or suspended. */
> | +    s->busy = false;
> |  }
> |
> |  static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
>
> Please see:
>    -> https://ruhr-uni-bochum.sciebo.de/s/NNWP2GfwzYKeKwE?path=%2Feepro100_stackoverflow1
>
> * It does not seem to address above case.


My suggested patch fixes that test case: it no longer crashes because of 
an endless recursion.

Stefan
Alexander Bulekov Feb. 20, 2021, 3:05 a.m. UTC | #13
On 210219 1243, Li Qiang wrote:
> Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午10:15写道:
> >
> > On 210219 1006, Li Qiang wrote:
> > > Alexander Bulekov <alxndr@bu.edu> 于2021年2月19日周五 上午9:56写道:
> > > >
> > > > On 210218 1441, Peter Maydell wrote:
> > > > > On Thu, 18 Feb 2021 at 14:13, P J P <ppandit@redhat.com> wrote:
> > > > > >
> > > > > > From: Prasad J Pandit <pjp@fedoraproject.org>
> > > > > >
> > > > > > While processing controller commands, eepro100 emulator gets
> > > > > > command unit(CU) base address OR receive unit (RU) base address
> > > > > > OR command block (CB) address from guest. If these values are not
> > > > > > checked, it may lead to an infinite loop kind of issues. Add checks
> > > > > > to avoid it.
> > >
> > >
> > > So could you please provide a backtrack?
> > >
> >
> > I don't know if you are asking me or Prasad, but here is the stacktrace
> 
> 
> Yes, a typical DMA reentry issue.
> Any progress to solve these DMA reentry issues? seems more and more
> this kind of issues.

Unfortuantely, I don't think there's a solution yet.

> Just return from the busy things as a new father and not focus this
> quite a time.

Congrats!

> 
> Thanks,
> Li Qiang
> 
> > for the one I provided:
> > ==2715275==ERROR: AddressSanitizer: stack-overflow on address
> > 0x7ffc5262ba28 (pc 0x55d83b103ac6 bp 0x7ffc5262c270 sp 0x7ffc5262ba30
> > T0)
> > #0 in __asan_memcpy (qemu-system-i386+0x2aa3ac6)
> > #1 in flatview_do_translate ../softmmu/physmem.c:518:12
> > #2 in flatview_translate ../softmmu/physmem.c:568:15
> > #3 in flatview_read ../softmmu/physmem.c:2878:10
> > #4 in address_space_read_full ../softmmu/physmem.c:2892:18
> > #5 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
> > #6 in dma_memory_rw include/sysemu/dma.h:127:12
> > #7 in pci_dma_rw include/hw/pci/pci.h:803:12
> > #8 in pci_dma_read include/hw/pci/pci.h:821:12
> > #9 in read_cb ../hw/net/eepro100.c:726:5
> > #10 in action_command ../hw/net/eepro100.c:847:9
> > #11 in eepro100_cu_command ../hw/net/eepro100.c:969:13
> > #12 in eepro100_write_command ../hw/net/eepro100.c:1063:5
> > #13 in eepro100_write2 ../hw/net/eepro100.c:1510:9
> > #14 in eepro100_write ../hw/net/eepro100.c:1593:9
> > #15 in memory_region_write_accessor ../softmmu/memory.c:491:5
> > #16 in access_with_adjusted_size ../softmmu/memory.c:552:18
> > #17 in memory_region_dispatch_write ../softmmu/memory.c
> > #18 in flatview_write_continue ../softmmu/physmem.c:2776:23
> > #19 in flatview_write ../softmmu/physmem.c:2816:14
> > #20 in address_space_write ../softmmu/physmem.c:2908:18
> > #21 in dma_memory_rw_relaxed include/sysemu/dma.h:88:12
> > #22 in dma_memory_rw include/sysemu/dma.h:127:12
> > #23 in dma_memory_write include/sysemu/dma.h:163:12
> > #24 in stw_le_dma include/sysemu/dma.h:259:1
> > #25 in stw_le_pci_dma include/hw/pci/pci.h:855:1
> > #26 in action_command ../hw/net/eepro100.c:913:9
> > #27 in eepro100_cu_command ../hw/net/eepro100.c:969:13
> > #28 in eepro100_write_command ../hw/net/eepro100.c:1063:5
> > #29 in eepro100_write2 ../hw/net/eepro100.c:1510:9
> > #30 in eepro100_write ../hw/net/eepro100.c:1593:9
> > ... till there's no more stack ...
> >
> > >
> > > Thanks,
> > > Li Qiang
> > >
> > > > > >
> > > > > > Reported-by: Ruhr-University Bochum <bugs-syssec@rub.de>
> > > > > > Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
> > > > > > ---
> > > > > >  hw/net/eepro100.c | 8 +++++++-
> > > > > >  1 file changed, 7 insertions(+), 1 deletion(-)
> > > > > >
> > > > > > diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
> > > > > > index 16e95ef9cc..afa1c9b2aa 100644
> > > > > > --- a/hw/net/eepro100.c
> > > > > > +++ b/hw/net/eepro100.c
> > > > > > @@ -843,7 +843,8 @@ static void action_command(EEPRO100State *s)
> > > > > >          bool bit_i;
> > > > > >          bool bit_nc;
> > > > > >          uint16_t ok_status = STATUS_OK;
> > > > > > -        s->cb_address = s->cu_base + s->cu_offset;
> > > > > > +        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
> > > > > > +        assert (s->cb_address >= s->cu_base);
> > > > >
> > > > > We get these values from the guest; you can't just assert() on them.
> > > > > You need to do something else.
> > > > >
> > > > > My reading of the 8255x data sheet is that there is nothing
> > > > > in the hardware that forbids the guest from programming the
> > > > > device such that the cu_base + cu_offset wraps around:
> > > > > http://www.intel.com/content/dam/doc/manual/8255x-10-100-mbps-ethernet-controller-software-dev-manual.pdf
> > > > > -- page 30 says that this is all doing 32-bit arithmetic
> > > > > on addresses and doesn't say that there is any special case
> > > > > handling by the device of overflow of that addition.
> > > > >
> > > > > Your commit message isn't very clear about what the failure
> > > > > case is here, but I think the fix has to be something
> > > > > different from this.
> > > >
> > > > Maybe the infinite loop mentioned in the commit message is actually a
> > > > DMA recursion issue? I'm providing a reproducer for a DMA re-entracy
> > > > issue below. With this patch applied, the reproducer triggers the
> > > > assert(), rather than overflowing the stack, so maybe it is the same
> > > > issue?
> > > > -Alex
> > > >
> > > > cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, -m \
> > > > 512M -device i82559er,netdev=net0 -netdev user,id=net0 -nodefaults \
> > > > -qtest stdio
> > > > outl 0xcf8 0x80001014
> > > > outl 0xcfc 0xc000
> > > > outl 0xcf8 0x80001010
> > > > outl 0xcfc 0xe0020000
> > > > outl 0xcf8 0x80001004
> > > > outw 0xcfc 0x7
> > > > write 0x1ffffc0b 0x1 0x55
> > > > write 0x1ffffc0c 0x1 0xfc
> > > > write 0x1ffffc0d 0x1 0x46
> > > > write 0x1ffffc0e 0x1 0x07
> > > > write 0x746fc59 0x1 0x02
> > > > write 0x746fc5b 0x1 0x02
> > > > write 0x746fc5c 0x1 0xe0
> > > > write 0x4 0x1 0x07
> > > > write 0x5 0x1 0xfc
> > > > write 0x6 0x1 0xff
> > > > write 0x7 0x1 0x1f
> > > > outw 0xc002 0x20
> > > > EOF
> > > >
> > > > Formatted for committing a regression-test:
> > > >
> > > > static void test_fuzz(void)
> > > > {
> > > >     QTestState *s =
> > > >         qtest_init("-display none , -m 512M -device i82559er,netdev=net0 "
> > > >                    "-netdev user,id=net0 -nodefaults");
> > > >     qtest_outl(s, 0xcf8, 0x80001014);
> > > >     qtest_outl(s, 0xcfc, 0xc000);
> > > >     qtest_outl(s, 0xcf8, 0x80001010);
> > > >     qtest_outl(s, 0xcfc, 0xe0020000);
> > > >     qtest_outl(s, 0xcf8, 0x80001004);
> > > >     qtest_outw(s, 0xcfc, 0x7);
> > > >     qtest_bufwrite(s, 0x1ffffc0b, "\x55", 0x1);
> > > >     qtest_bufwrite(s, 0x1ffffc0c, "\xfc", 0x1);
> > > >     qtest_bufwrite(s, 0x1ffffc0d, "\x46", 0x1);
> > > >     qtest_bufwrite(s, 0x1ffffc0e, "\x07", 0x1);
> > > >     qtest_bufwrite(s, 0x746fc59, "\x02", 0x1);
> > > >     qtest_bufwrite(s, 0x746fc5b, "\x02", 0x1);
> > > >     qtest_bufwrite(s, 0x746fc5c, "\xe0", 0x1);
> > > >     qtest_bufwrite(s, 0x4, "\x07", 0x1);
> > > >     qtest_bufwrite(s, 0x5, "\xfc", 0x1);
> > > >     qtest_bufwrite(s, 0x6, "\xff", 0x1);
> > > >     qtest_bufwrite(s, 0x7, "\x1f", 0x1);
> > > >     qtest_outw(s, 0xc002, 0x20);
> > > >     qtest_quit(s);
> > > > }
> > > >
> > > >
> > > > >
> > > > > thanks
> > > > > -- PMM
> > > > >
> > > >
diff mbox series

Patch

diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 16e95ef9cc..afa1c9b2aa 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -843,7 +843,8 @@  static void action_command(EEPRO100State *s)
         bool bit_i;
         bool bit_nc;
         uint16_t ok_status = STATUS_OK;
-        s->cb_address = s->cu_base + s->cu_offset;
+        s->cb_address = s->cu_base + s->cu_offset;  /* uint32_t overflow */
+        assert (s->cb_address >= s->cu_base);
         read_cb(s);
         bit_el = ((s->tx.command & COMMAND_EL) != 0);
         bit_s = ((s->tx.command & COMMAND_S) != 0);
@@ -860,6 +861,7 @@  static void action_command(EEPRO100State *s)
         }
 
         s->cu_offset = s->tx.link;
+        assert(s->cu_offset > 0);
         TRACE(OTHER,
               logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
                      s->tx.status, s->tx.command, s->tx.link));
@@ -990,8 +992,10 @@  static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
         break;
     case CU_CMD_BASE:
         /* Load CU base. */
+        assert(get_cu_state(s) == cu_idle);
         TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
         s->cu_base = e100_read_reg4(s, SCBPointer);
+        assert(!s->cu_base);
         break;
     case CU_DUMPSTATS:
         /* Dump and reset statistical counters. */
@@ -1048,8 +1052,10 @@  static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
         break;
     case RX_ADDR_LOAD:
         /* Load RU base. */
+        assert(get_ru_state(s) == ru_idle);
         TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
         s->ru_base = e100_read_reg4(s, SCBPointer);
+        assert(!s->ru_base);
         break;
     default:
         logout("val=0x%02x (undefined RU command)\n", val);