[PATCH] tests/tcg/x86_64: Add tests for zero_bss() with RX PT_LOAD segments

Razvan Ghiorghe posted 1 patch 3 days, 20 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260203073740.19913-2-razvanghiorghe16@gmail.com
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Richard Henderson <richard.henderson@linaro.org>, Eduardo Habkost <eduardo@habkost.net>
tests/tcg/x86_64/Makefile.target     | 15 ++++++++++++
tests/tcg/x86_64/rx_anonymous_bss.ld | 27 +++++++++++++++++++++
tests/tcg/x86_64/test-zerobss-1.c    | 20 ++++++++++++++++
tests/tcg/x86_64/test-zerobss-2.c    | 36 ++++++++++++++++++++++++++++
4 files changed, 98 insertions(+)
create mode 100644 tests/tcg/x86_64/rx_anonymous_bss.ld
create mode 100644 tests/tcg/x86_64/test-zerobss-1.c
create mode 100644 tests/tcg/x86_64/test-zerobss-2.c
[PATCH] tests/tcg/x86_64: Add tests for zero_bss() with RX PT_LOAD segments
Posted by Razvan Ghiorghe 3 days, 20 hours ago
Add 2 test cases to validate the fix for issue #3179, where the linux-user
ELF Loader incorrectly rejected binaries with .bss sections in R_X PT_LOAD
segments.

test-zerobss-1.c: defines a minimal _start routine that exists immediately
without accessing the .bss region. Although a .bss region is present in ELF,
it is never referenced at runtime. This test validates that the loader can
correctly handle and zero the fractional .bss region in an R_X segment
without triggering permission violations.

test-zerobss-2.c: explicitly reads from multiple .bss variables to verify
they are zeroed properly. If the .bss was correctly zeroed, the exit code will
be 0.

rx_anonymous_bss.ld: tests require a custom linker script to create the specific ELF
layout that triggers the bug. The linker script:
- creates a single PT_LOAD segment with FLAGS(5) = PF_R | PF_X (read-execute)
- places .text and .bss in this same segment
- sets .bss to ALIGN(1) to force it into the tail of the R_X segment
- results in p_filesz < p_memsz, where the gap is anonymous .bss

Expected behavior:
- Without the fix: Both tests fail with "PT_LOAD with non-writable bss"
- With the fix: Both tests pass (exit code 0)

Signed-off-by: Razvan Ghiorghe <razvanghiorghe16@gmail.com>
---
 tests/tcg/x86_64/Makefile.target     | 15 ++++++++++++
 tests/tcg/x86_64/rx_anonymous_bss.ld | 27 +++++++++++++++++++++
 tests/tcg/x86_64/test-zerobss-1.c    | 20 ++++++++++++++++
 tests/tcg/x86_64/test-zerobss-2.c    | 36 ++++++++++++++++++++++++++++
 4 files changed, 98 insertions(+)
 create mode 100644 tests/tcg/x86_64/rx_anonymous_bss.ld
 create mode 100644 tests/tcg/x86_64/test-zerobss-1.c
 create mode 100644 tests/tcg/x86_64/test-zerobss-2.c

diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target
index be20fc64e8..ca375a4318 100644
--- a/tests/tcg/x86_64/Makefile.target
+++ b/tests/tcg/x86_64/Makefile.target
@@ -19,6 +19,9 @@ X86_64_TESTS += test-1648
 X86_64_TESTS += test-2175
 X86_64_TESTS += cross-modifying-code
 X86_64_TESTS += fma
+
+X86_64_TESTS +=  test-zerobss-1
+X86_64_TESTS +=  test-zerobss-2
 TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
 else
 TESTS=$(MULTIARCH_TESTS)
@@ -36,5 +39,17 @@ test-x86_64: LDFLAGS+=-lm -lc
 test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h
 	$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
 
+test-zerobss-1: $(SRC_PATH)/tests/tcg/x86_64/test-zerobss-1.c
+	$(CC) -nostdlib -static -fno-stack-protector -fno-pie -no-pie \
+	     -Wl,-T,$(SRC_PATH)/tests/tcg/x86_64/rx_anonymous_bss.ld \
+	     -Wl,--build-id=none \
+	     $< -o $@
+
+test-zerobss-2: $(SRC_PATH)/tests/tcg/x86_64/test-zerobss-2.c
+	$(CC) -nostdlib -static -fno-stack-protector -fno-pie -no-pie \
+	     -Wl,-T,$(SRC_PATH)/tests/tcg/x86_64/rx_anonymous_bss.ld \
+	     -Wl,--build-id=none \
+	     $< -o $@
+
 %: $(SRC_PATH)/tests/tcg/x86_64/%.c
 	$(CC) $(CFLAGS) $< -o $@ $(LDFLAGS)
diff --git a/tests/tcg/x86_64/rx_anonymous_bss.ld b/tests/tcg/x86_64/rx_anonymous_bss.ld
new file mode 100644
index 0000000000..3ec72fde6c
--- /dev/null
+++ b/tests/tcg/x86_64/rx_anonymous_bss.ld
@@ -0,0 +1,27 @@
+/* Linker script to create RX PT_LOAD with anonymous .bss tail
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+PHDRS
+{
+    text PT_LOAD FLAGS(5);
+}
+SECTIONS
+{
+    . = 0x400000;
+    .text : {
+        *(.text .text.*)
+        _etext = .;
+    } :text
+    .bss ALIGN(1) : {
+        _bss_start = .;
+        *(.bss .bss.* COMMON)
+        _bss_end = .;
+    } :text
+    /DISCARD/ : {
+        *(.note.GNU-stack)
+        *(.note.gnu.property)
+        *(.comment)
+        *(.eh_frame)
+    }
+}
+ENTRY(_start)
diff --git a/tests/tcg/x86_64/test-zerobss-1.c b/tests/tcg/x86_64/test-zerobss-1.c
new file mode 100644
index 0000000000..809ea3cb52
--- /dev/null
+++ b/tests/tcg/x86_64/test-zerobss-1.c
@@ -0,0 +1,20 @@
+ /*
+  * .bss region exists but unused
+  * SPDX-License-Identifier: GPL-2.0-or-later
+  */
+int x;
+void _start(void)
+{
+    /*
+     * Exit immediately & never touched x
+     */
+    __asm__ volatile(
+        "mov $60, %%rax\n"
+        "xor %%rdi, %%rdi\n"
+        "syscall\n"
+        : /* no out */
+        : /* no in */
+        : "rax", "rdi"
+    );
+    __builtin_unreachable();
+}
diff --git a/tests/tcg/x86_64/test-zerobss-2.c b/tests/tcg/x86_64/test-zerobss-2.c
new file mode 100644
index 0000000000..1f4f0d9704
--- /dev/null
+++ b/tests/tcg/x86_64/test-zerobss-2.c
@@ -0,0 +1,36 @@
+/*
+ * Test case for zero_bss() with anonymous BSS in RX PT_LOAD
+ *
+ * This binary has .bss in the same PT_LOAD as .text (R_X permissions),
+ * but the BSS is anonymous (beyond p_filesz), not file-backed.
+ * Actual behavior:
+ * old code: Fails with "PT_LOAD with non-writable bss"
+ * new code: Succeeds, zeros BSS, exits with code 0
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* These will be included in .bss (uninitialized anonymous memory) */
+int bss_value;
+int bss_array[64];
+
+void _start(void)
+{
+    int sum = bss_value;
+    int i;
+
+    for (i = 0; i < 64; i++) {
+        sum += bss_array[i];
+    }
+    /* If BSS was properly zeroed, sum should be 0 */
+    /* Exit with sum as exit code */
+    __asm__ volatile (
+        "movl %0, %%edi\n\t"
+        "movl $60, %%eax\n\t"
+        "syscall\n\t"
+        : /* no out*/
+        : "r" (sum)
+        : "rdi", "rax"
+    );
+
+    __builtin_unreachable();
+}
-- 
2.43.0