See https://lore.kernel.org/qemu-devel/20260622092035.400959-1-borntraeger@linux.ibm.com/ for the QEMU fix. Add a regression test that races STSI 3.2.2 on one CPU against a second CPU that continuously forces an out-of-range count value. The out of bound access usually crashes/asserts QEMU with any sane distribution build of QEMU, so its more or less guest root can kill itself. We should test and fix nevertheless. Testcase piggybacks on the existing stsi test, so some cases will be tested twice. (with smp 1 and smp 2) Signed-off-by: Christian Borntraeger Cc: Cornelia Huck --- If wanted we could split this into a separate file or always run the test with smp=2 to avoid the duplication s390x/stsi.c | 76 ++++++++++++++++++++++++++++++++++++++++++++- s390x/unittests.cfg | 9 ++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/s390x/stsi.c b/s390x/stsi.c index 94a579dc..96361143 100644 --- a/s390x/stsi.c +++ b/s390x/stsi.c @@ -2,7 +2,7 @@ /* * Store System Information tests * - * Copyright (c) 2019 IBM Corp + * Copyright IBM Corp. 2019,2026 * * Authors: * Janosch Frank @@ -133,6 +133,79 @@ out: report_prefix_pop(); } +/* + * Number of STSI 3.2.2 calls raced against the count corruptor below. + * A memory write should be faster than an kvm->qemu exit, so 100 is + * good enough. + */ +#define RACE_ITERATIONS 100 +static u8 corrupt_count_value; + +static void count_corruptor(void) +{ + struct sysinfo_3_2_2 *data = (void *)pagebuf; + + for (;;) + *(volatile u8 *)&data->count = corrupt_count_value; +} + +/* + * Race STSI 3.2.2 on the boot CPU against a secondary CPU that continuously + * forces the given out-of-range value into the "count" field. Returns true + * if every STSI returned cc == 0, false on an unexpected condition code. + */ +static bool race_count_value(uint8_t value) +{ + int i, cc; + + corrupt_count_value = value; + smp_cpu_setup(1, PSW_WITH_CUR_MASK(count_corruptor)); + + for (i = 0; i < RACE_ITERATIONS; i++) { + cc = stsi(pagebuf, 3, 2, 2); + if (cc) { + report_fail("count 0x%02x: unexpected cc %d on iteration %d", + value, cc, i); + break; + } + } + + smp_cpu_stop(1); + smp_cpu_destroy(1); + + return i == RACE_ITERATIONS; +} + +/* + * The count value is 8 bit and valid values are 1-8 if stsi 3.2.2 is present. + * We test 0,9 as off-by-one, and 0xff as maximum value. + */ +static void test_3_2_2_race(void) +{ + report_prefix_push("3.2.2 count race"); + + if (stsi_get_fc() < 3) { + report_skip("Running under lpar, no level 3 to test."); + goto out; + } + + if (smp_query_num_cpus() < 2) { + report_skip("Need at least 2 CPUs to race the count field."); + goto out; + } + + if (race_count_value(0x0)) + report_pass("host survived racing STSI 3.2.2 count 0x00"); + + if (race_count_value(0x9)) + report_pass("host survived racing STSI 3.2.2 count 0x09"); + + if (race_count_value(0xff)) + report_pass("host survived racing STSI 3.2.2 count 0xff"); +out: + report_prefix_pop(); +} + int main(void) { report_prefix_push("stsi"); @@ -140,5 +213,6 @@ int main(void) test_specs(); test_fc(); test_3_2_2(); + test_3_2_2_race(); return report_summary(); } diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg index ed4d069e..c1462506 100644 --- a/s390x/unittests.cfg +++ b/s390x/unittests.cfg @@ -81,6 +81,15 @@ qemu_params=-device diag288,id=watchdog0 --watchdog-action inject-nmi file = stsi.elf qemu_params=-name kvm-unit-test --uuid 0fb84a86-727c-11ea-bc55-0242ac130003 -smp 1,maxcpus=8 +# Regression test for the QEMU STSI 3.2.2 count clamp. Needs a second CPU to +# race the guest-visible count field, and only applies to QEMU's KVM path. +[stsi-3-2-2-race] +file = stsi.elf +qemu_params=-name kvm-unit-test --uuid 0fb84a86-727c-11ea-bc55-0242ac130003 +smp = 2 +accel = kvm +timeout = 30 + [smp] file = smp.elf smp = 2 -- 2.53.0