Browse Source

linux/nios2: carry the syscall-restart and ELF_ET_DYN_BASE fixes

Two nios2 kernel bugs that a uClibc-ng userland trips over immediately:

  * do_signal() decides whether to run syscall-restart processing using
    regs->r1, the user's 'at' register, which is an arbitrary value in
    pt_regs.  It therefore missed real restarts -- ERESTARTNOHAND leaked
    out of pselect6 as errno 514 -- and restarted successful syscalls
    whose return value collides with a restart code, so waitpid() of a
    child with pid 512..516 or a read() of 512..516 bytes went wrong.
    nios2 flags errors in r7, and restart codes are only ever errors.

  * ELF_ET_DYN_BASE was 0xD0000000 while nios2 ends its user address
    space at TASK_SIZE 0x7FFF0000.  load_elf_binary() maps an ET_DYN
    executable with a PT_INTERP there with MAP_FIXED_NOREPLACE, which
    do_mmap() turns into MAP_FIXED, so get_unmapped_area() rejects the
    out-of-range address with -ENOMEM -- after begin_new_exec(), past the
    point of no return.  Every exec of a PIE died with SIGSEGV; the
    register dump even shows the calling shell's registers, which makes
    it look like a crash inside the new program.  Use the expression sh
    and the other 32-bit architectures use.

Both are verified under qemu-system-nios2: without them the 11 -pie tests
of the uClibc-ng testsuite fail and popen/pselect/tst-eintr1 misbehave,
with them the suite is clean.  Added for the four current 6.x versions;
the bugs are older than that, so the 5.x directories would need the same
if anyone builds nios2 against them.

Signed-off-by: Ramin Moussavi <ramin.moussavi@yacoub.de>
Signed-off-by: Waldemar Brodkorb <wbx@openadk.org>
ramin 1 week ago
parent
commit
568262b3dc

+ 19 - 0
target/linux/patches/6.1.174/nios2-elf-et-dyn-base.patch

@@ -0,0 +1,19 @@
+# nios2: put ELF_ET_DYN_BASE inside the user address space.
+# It was 0xD0000000 while nios2 ends its user space at TASK_SIZE 0x7FFF0000,
+# so load_elf_binary() could not map a PIE there: the MAP_FIXED_NOREPLACE
+# mapping fails with -ENOMEM after begin_new_exec(), past the point of no
+# return, and every exec of a PIE dies with SIGSEGV.  Value as on sh.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/include/asm/elf.h linux-6.5.10/arch/nios2/include/asm/elf.h
+--- linux-6.5.10.orig/arch/nios2/include/asm/elf.h
++++ linux-6.5.10/arch/nios2/include/asm/elf.h
+@@ -23,7 +23,7 @@
+    the loader.  We need to make sure that it is out of the way of the program
+    that it will "exec", and that there is sufficient room for the brk.  */
+ 
+-#define ELF_ET_DYN_BASE		0xD0000000UL
++#define ELF_ET_DYN_BASE		(2 * TASK_SIZE / 3)
+ 
+ /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
+    now struct_user_regs, they are different) */

+ 21 - 0
target/linux/patches/6.1.174/nios2-syscall-restart.patch

@@ -0,0 +1,21 @@
+# nios2: gate syscall restart on the error flag (r7), not the stale r1.
+# do_signal() used regs->r1 (the user's 'at' register, an arbitrary value) to
+# decide whether to do syscall-restart processing.  That both missed real
+# restarts (ERESTARTNOHAND leaked as errno 514 out of pselect6) and restarted
+# SUCCESSFUL syscalls whose return value collides with a restart code
+# (waitpid -> child pid 512..516, read -> 512..516 bytes -> exit 255).
+# Restart codes are only ever error returns, which nios2 flags in r7.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/kernel/signal.c linux-6.5.10/arch/nios2/kernel/signal.c
+--- linux-6.5.10.orig/arch/nios2/kernel/signal.c
++++ linux-6.5.10/arch/nios2/kernel/signal.c
+@@ -242,7 +242,7 @@
+ 	/*
+ 	 * If we were from a system call, check for system call restarting...
+ 	 */
+-	if (regs->orig_r2 >= 0 && regs->r1) {
++	if (regs->orig_r2 >= 0 && regs->r7) {
+ 		continue_addr = regs->ea;
+ 		restart_addr = continue_addr - 4;
+ 		retval = regs->r2;

+ 19 - 0
target/linux/patches/6.12.91/nios2-elf-et-dyn-base.patch

@@ -0,0 +1,19 @@
+# nios2: put ELF_ET_DYN_BASE inside the user address space.
+# It was 0xD0000000 while nios2 ends its user space at TASK_SIZE 0x7FFF0000,
+# so load_elf_binary() could not map a PIE there: the MAP_FIXED_NOREPLACE
+# mapping fails with -ENOMEM after begin_new_exec(), past the point of no
+# return, and every exec of a PIE dies with SIGSEGV.  Value as on sh.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/include/asm/elf.h linux-6.5.10/arch/nios2/include/asm/elf.h
+--- linux-6.5.10.orig/arch/nios2/include/asm/elf.h
++++ linux-6.5.10/arch/nios2/include/asm/elf.h
+@@ -23,7 +23,7 @@
+    the loader.  We need to make sure that it is out of the way of the program
+    that it will "exec", and that there is sufficient room for the brk.  */
+ 
+-#define ELF_ET_DYN_BASE		0xD0000000UL
++#define ELF_ET_DYN_BASE		(2 * TASK_SIZE / 3)
+ 
+ /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
+    now struct_user_regs, they are different) */

+ 21 - 0
target/linux/patches/6.12.91/nios2-syscall-restart.patch

@@ -0,0 +1,21 @@
+# nios2: gate syscall restart on the error flag (r7), not the stale r1.
+# do_signal() used regs->r1 (the user's 'at' register, an arbitrary value) to
+# decide whether to do syscall-restart processing.  That both missed real
+# restarts (ERESTARTNOHAND leaked as errno 514 out of pselect6) and restarted
+# SUCCESSFUL syscalls whose return value collides with a restart code
+# (waitpid -> child pid 512..516, read -> 512..516 bytes -> exit 255).
+# Restart codes are only ever error returns, which nios2 flags in r7.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/kernel/signal.c linux-6.5.10/arch/nios2/kernel/signal.c
+--- linux-6.5.10.orig/arch/nios2/kernel/signal.c
++++ linux-6.5.10/arch/nios2/kernel/signal.c
+@@ -242,7 +242,7 @@
+ 	/*
+ 	 * If we were from a system call, check for system call restarting...
+ 	 */
+-	if (regs->orig_r2 >= 0 && regs->r1) {
++	if (regs->orig_r2 >= 0 && regs->r7) {
+ 		continue_addr = regs->ea;
+ 		restart_addr = continue_addr - 4;
+ 		retval = regs->r2;

+ 19 - 0
target/linux/patches/6.18.40/nios2-elf-et-dyn-base.patch

@@ -0,0 +1,19 @@
+# nios2: put ELF_ET_DYN_BASE inside the user address space.
+# It was 0xD0000000 while nios2 ends its user space at TASK_SIZE 0x7FFF0000,
+# so load_elf_binary() could not map a PIE there: the MAP_FIXED_NOREPLACE
+# mapping fails with -ENOMEM after begin_new_exec(), past the point of no
+# return, and every exec of a PIE dies with SIGSEGV.  Value as on sh.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/include/asm/elf.h linux-6.5.10/arch/nios2/include/asm/elf.h
+--- linux-6.5.10.orig/arch/nios2/include/asm/elf.h
++++ linux-6.5.10/arch/nios2/include/asm/elf.h
+@@ -23,7 +23,7 @@
+    the loader.  We need to make sure that it is out of the way of the program
+    that it will "exec", and that there is sufficient room for the brk.  */
+ 
+-#define ELF_ET_DYN_BASE		0xD0000000UL
++#define ELF_ET_DYN_BASE		(2 * TASK_SIZE / 3)
+ 
+ /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
+    now struct_user_regs, they are different) */

+ 21 - 0
target/linux/patches/6.18.40/nios2-syscall-restart.patch

@@ -0,0 +1,21 @@
+# nios2: gate syscall restart on the error flag (r7), not the stale r1.
+# do_signal() used regs->r1 (the user's 'at' register, an arbitrary value) to
+# decide whether to do syscall-restart processing.  That both missed real
+# restarts (ERESTARTNOHAND leaked as errno 514 out of pselect6) and restarted
+# SUCCESSFUL syscalls whose return value collides with a restart code
+# (waitpid -> child pid 512..516, read -> 512..516 bytes -> exit 255).
+# Restart codes are only ever error returns, which nios2 flags in r7.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/kernel/signal.c linux-6.5.10/arch/nios2/kernel/signal.c
+--- linux-6.5.10.orig/arch/nios2/kernel/signal.c
++++ linux-6.5.10/arch/nios2/kernel/signal.c
+@@ -242,7 +242,7 @@
+ 	/*
+ 	 * If we were from a system call, check for system call restarting...
+ 	 */
+-	if (regs->orig_r2 >= 0 && regs->r1) {
++	if (regs->orig_r2 >= 0 && regs->r7) {
+ 		continue_addr = regs->ea;
+ 		restart_addr = continue_addr - 4;
+ 		retval = regs->r2;

+ 19 - 0
target/linux/patches/6.6.141/nios2-elf-et-dyn-base.patch

@@ -0,0 +1,19 @@
+# nios2: put ELF_ET_DYN_BASE inside the user address space.
+# It was 0xD0000000 while nios2 ends its user space at TASK_SIZE 0x7FFF0000,
+# so load_elf_binary() could not map a PIE there: the MAP_FIXED_NOREPLACE
+# mapping fails with -ENOMEM after begin_new_exec(), past the point of no
+# return, and every exec of a PIE dies with SIGSEGV.  Value as on sh.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/include/asm/elf.h linux-6.5.10/arch/nios2/include/asm/elf.h
+--- linux-6.5.10.orig/arch/nios2/include/asm/elf.h
++++ linux-6.5.10/arch/nios2/include/asm/elf.h
+@@ -23,7 +23,7 @@
+    the loader.  We need to make sure that it is out of the way of the program
+    that it will "exec", and that there is sufficient room for the brk.  */
+ 
+-#define ELF_ET_DYN_BASE		0xD0000000UL
++#define ELF_ET_DYN_BASE		(2 * TASK_SIZE / 3)
+ 
+ /* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
+    now struct_user_regs, they are different) */

+ 21 - 0
target/linux/patches/6.6.141/nios2-syscall-restart.patch

@@ -0,0 +1,21 @@
+# nios2: gate syscall restart on the error flag (r7), not the stale r1.
+# do_signal() used regs->r1 (the user's 'at' register, an arbitrary value) to
+# decide whether to do syscall-restart processing.  That both missed real
+# restarts (ERESTARTNOHAND leaked as errno 514 out of pselect6) and restarted
+# SUCCESSFUL syscalls whose return value collides with a restart code
+# (waitpid -> child pid 512..516, read -> 512..516 bytes -> exit 255).
+# Restart codes are only ever error returns, which nios2 flags in r7.
+# Not upstream yet; the submittable version lives in the uclibc-ng workspace
+# under git/kernel-submit/.
+diff -Nur linux-6.5.10.orig/arch/nios2/kernel/signal.c linux-6.5.10/arch/nios2/kernel/signal.c
+--- linux-6.5.10.orig/arch/nios2/kernel/signal.c
++++ linux-6.5.10/arch/nios2/kernel/signal.c
+@@ -242,7 +242,7 @@
+ 	/*
+ 	 * If we were from a system call, check for system call restarting...
+ 	 */
+-	if (regs->orig_r2 >= 0 && regs->r1) {
++	if (regs->orig_r2 >= 0 && regs->r7) {
+ 		continue_addr = regs->ea;
+ 		restart_addr = continue_addr - 4;
+ 		retval = regs->r2;