The nvmetest_oob_cmb_test was designed to deliberately perform an
out-of-bounds write on a PCI BAR. This was intended as a regression
test for CVE-2018-16847.
The recent change to libqos introduced strict bounds checking on all
BAR accessors, which correctly caused this test to fail with a fatal
assertion, as it was performing an illegal memory access.
This change reworks the test to honor its original intent—verifying
safe accesses at the BAR boundary—without violating the new API contract.
Instead of attempting an illegal write, the test now performs several
valid read/write operations at the very end of the BAR (at offsets
size - 1, size - 2, and size - 4) to confirm the entire region
is accessible.
This makes the test compatible with the safer libqos API while still
serving as a regression test for the original issue.
Signed-off-by: Navid Emamdoost <navidem@google.com>
---
tests/qtest/nvme-test.c | 32 +++++++++++++++++++++++---------
1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/tests/qtest/nvme-test.c b/tests/qtest/nvme-test.c
index 5ad6821f7a..8be37ae043 100644
--- a/tests/qtest/nvme-test.c
+++ b/tests/qtest/nvme-test.c
@@ -48,23 +48,37 @@ static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
/* This used to cause a NULL pointer dereference. */
static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc)
{
- const int cmb_bar_size = 2 * MiB;
QNvme *nvme = obj;
QPCIDevice *pdev = &nvme->dev;
QPCIBar bar;
+ const uint64_t expected_cmb_size = 2 * MiB;
+ /* Enable the device's I/O and memory resources at the PCI level. */
qpci_device_enable(pdev);
+
+ /* Map BAR 2, which is the dedicated BAR for the Controller Memory Buffer. */
bar = qpci_iomap(pdev, 2, NULL);
- qpci_io_writel(pdev, bar, 0, 0xccbbaa99);
- g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99);
- g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99);
+ /* Sanity check that the probed BAR size matches the command line. */
+ g_assert_cmpint(bar.size, ==, expected_cmb_size);
+
+ /*
+ * Perform read/write checks at the very end of the BAR to ensure
+ * that the entire region is accessible and that boundary accesses of
+ * different sizes are handled correctly.
+ */
+
+ /* Test the last valid byte (the fix for the CVE was about 1-byte access) */
+ qpci_io_writeb(pdev, bar, bar.size - 1, 0x11);
+ g_assert_cmpint(qpci_io_readb(pdev, bar, bar.size - 1), ==, 0x11);
+
+ /* Test the last valid word */
+ qpci_io_writew(pdev, bar, bar.size - 2, 0x2233);
+ g_assert_cmpint(qpci_io_readw(pdev, bar, bar.size - 2), ==, 0x2233);
- /* Test partially out-of-bounds accesses. */
- qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211);
- g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11);
- g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211);
- g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211);
+ /* Test the last valid dword */
+ qpci_io_writel(pdev, bar, bar.size - 4, 0x44556677);
+ g_assert_cmpint(qpci_io_readl(pdev, bar, bar.size - 4), ==, 0x44556677);
}
static void nvmetest_reg_read_test(void *obj, void *data, QGuestAllocator *alloc)
--
2.52.0.158.g65b55ccf14-goog