mii_read_host() uses the guest-supplied register index directly to read from Mii.regs[], which has only MII_REG_MAX (16) entries. The adjacent mii_write_host() already rejects idx >= MII_REG_MAX, but the read path did not, so a guest writing MIIADDRESS.RGAD to 16..31 and then setting MIICOMMAND.RSTAT caused a host-side out-of-bounds read.
Return 0xffff for out-of-range indices; this matches the value already used for reads with FIAD != DEFAULT_PHY and is the conventional value for unimplemented MII registers. Add a qtest on the lx60 board that programs RGAD == 16 and triggers the read command, verifying MIIRX_DATA reads 0xffff instead of crashing or tripping sanitizer checks. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4364 Signed-off-by: Bin Guo <[email protected]> --- hw/net/opencores_eth.c | 3 ++ tests/qtest/meson.build | 3 ++ tests/qtest/opencores-eth-test.c | 66 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 tests/qtest/opencores-eth-test.c diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 5a07bcde09..a4cf72e0c4 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -130,6 +130,9 @@ static void mii_write_host(Mii *s, unsigned idx, uint16_t v) static uint16_t mii_read_host(Mii *s, unsigned idx) { + if (idx >= MII_REG_MAX) { + return 0xffff; + } trace_open_eth_mii_read(idx, s->regs[idx]); return s->regs[idx]; } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c3593f7530..deb1c59c1f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -217,6 +217,9 @@ qtests_sparc64 = \ qtests_filter + \ ['prom-env-test', 'boot-serial-test'] +qtests_xtensa = \ + (config_all_devices.has_key('CONFIG_OPENCORES_ETH') ? ['opencores-eth-test'] : []) + qtests_npcm7xx = \ ['npcm7xx_adc-test', 'npcm7xx_gpio-test', diff --git a/tests/qtest/opencores-eth-test.c b/tests/qtest/opencores-eth-test.c new file mode 100644 index 0000000000..39f04fd4c3 --- /dev/null +++ b/tests/qtest/opencores-eth-test.c @@ -0,0 +1,66 @@ +/* + * QTest regression test for OpenCores Ethernet MII register read + * + * Copyright (c) 2026 Bin Guo + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +/* + * lx60 (MMU) maps its system_io at 0xf0000000; open_eth registers live at + * offset 0x0d030000 inside that region. + */ +#define OPEN_ETH_BASE 0xfd030000 + +/* 32-bit word register indices */ +#define OPEN_ETH_MIICOMMAND (OPEN_ETH_BASE + 0x2c) +#define OPEN_ETH_MIIADDRESS (OPEN_ETH_BASE + 0x30) +#define OPEN_ETH_MIIRX_DATA (OPEN_ETH_BASE + 0x38) + +#define MIIADDRESS_FIAD 0x00000001 +#define MIIADDRESS_RGAD_SHIFT 8 +#define MIICOMMAND_RSTAT 0x00000002 + +/* + * Regression test for GitLab issue #4364: + * MIIADDRESS.RGAD is a 5-bit field (0..31), but the local PHY register + * array only has 16 entries. A read with RGAD >= 16 must not perform an + * out-of-bounds access. + */ +static void test_mii_register_out_of_range(void) +{ + QTestState *s; + + s = qtest_init("-machine lx60"); + + /* Select default PHY (FIAD == 1) and first out-of-range register. */ + qtest_writel(s, OPEN_ETH_MIIADDRESS, + MIIADDRESS_FIAD | (16 << MIIADDRESS_RGAD_SHIFT)); + + /* Trigger MII read command. */ + qtest_writel(s, OPEN_ETH_MIICOMMAND, MIICOMMAND_RSTAT); + + /* + * With the bug, the read path evaluates s->regs[16] and trips ASan/UBSan. + * With the fix, MIIRX_DATA.PRSD should read as 0xffff (unimplemented PHY + * register), matching the non-default-PHY fallback in the driver. + */ + g_assert_cmphex(qtest_readl(s, OPEN_ETH_MIIRX_DATA), ==, 0xffff); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + if (qtest_has_machine("lx60")) { + qtest_add_func("/opencores-eth/mii-register-out-of-range", + test_mii_register_out_of_range); + } + + return g_test_run(); +} -- 2.50.1 (Apple Git-155)
