From 503b03daa30bfcebbd45f27feff87f74b11b45a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Geyslan=20Greg=C3=B3rio?= Date: Wed, 14 Oct 2026 20:53:01 -0300 Subject: [PATCH] std.os.linux: declare $a3 as an output of mips syscall4 and up On MIPS, $a3 ($7) carries the fourth argument [1] and returns the kernel's error flag [2]. The kernel writes the flag on every return, success included [3]. syscall4 and up declared $7 an input only, so the compiler assumed it still held the argument after the syscall. In release builds, a pointer passed as the fourth argument then reads back as the flag, 0 on success, and using it faults at address 0. Commit 5b29b4ffa6 ("Fix MIPS inline assembly clobbers") took $7 out of the clobbers, since an input cannot be one, but never made it an output. Add that output, as alpha.zig does for its argument registers [4] and musl does with "+r" [5]. $7 stays out of the clobbers, because a register that is input, output and clobber at once can miscompile (#35724). [1] - https://refspecs.linuxfoundation.org/elf/mipsabi.pdf#page=24 [2] - https://man7.org/linux/man-pages/man2/syscall.2.html [3] - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/mips/kernel/scall32-o32.S?h=v7.1#n112 [4] - https://codeberg.org/ziglang/zig/src/commit/d8938120130dbe48ee8764b502b18c6224ab9340/lib/std/os/linux/alpha.zig#L38-L46 [5] - https://git.musl-libc.org/cgit/musl/tree/arch/mips/syscall_arch.h?h=v1.2.6#n81 closes #37247 --- lib/std/os/linux/mips.zig | 12 ++++++++++++ lib/std/os/linux/mips64.zig | 9 +++++++++ lib/std/os/linux/mipsn32.zig | 9 +++++++++ 3 files changed, 30 insertions(+) diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 8409aad1023c53a93f21fb87cd202ada89c3d227..c9c8fc71a65d6f93ad7aa77c4203c943e6df83fa 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -75,12 +75,15 @@ pub fn syscall4( arg3: syscall_arg_t, arg4: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -100,6 +103,8 @@ pub fn syscall5( arg4: syscall_arg_t, arg5: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ subu $sp, $sp, 24 \\ sw %[arg5], 16($sp) @@ -109,6 +114,7 @@ pub fn syscall5( \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -127,6 +133,8 @@ pub fn syscall6( arg5: syscall_arg_t, arg6: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ subu $sp, $sp, 24 \\ sw %[arg5], 16($sp) @@ -137,6 +145,7 @@ pub fn syscall6( \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -157,6 +166,8 @@ pub fn syscall7( arg6: syscall_arg_t, arg7: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ subu $sp, $sp, 32 \\ sw %[arg5], 16($sp) @@ -168,6 +179,7 @@ pub fn syscall7( \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 50f65ef93136257e1c942c80719b0ef929adcef4..c744caf8e667701a6d8eb0813ce811693724d31e 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -75,12 +75,15 @@ pub fn syscall4( arg3: syscall_arg_t, arg4: syscall_arg_t, ) u64 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ dsubu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u64), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -97,12 +100,15 @@ pub fn syscall5( arg4: syscall_arg_t, arg5: syscall_arg_t, ) u64 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ dsubu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u64), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -121,12 +127,15 @@ pub fn syscall6( arg5: syscall_arg_t, arg6: syscall_arg_t, ) u64 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ dsubu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u64), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), diff --git a/lib/std/os/linux/mipsn32.zig b/lib/std/os/linux/mipsn32.zig index 414f96230b9dad0b923208bf055f0be912b4aae1..b96a3e928e1bcb0f04d68db346db2ca37af37541 100644 --- a/lib/std/os/linux/mipsn32.zig +++ b/lib/std/os/linux/mipsn32.zig @@ -75,12 +75,15 @@ pub fn syscall4( arg3: syscall_arg_t, arg4: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -97,12 +100,15 @@ pub fn syscall5( arg4: syscall_arg_t, arg5: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), @@ -121,12 +127,15 @@ pub fn syscall6( arg5: syscall_arg_t, arg6: syscall_arg_t, ) u32 { + // $7 is both an input and a clobber. + var r7_out: syscall_arg_t = undefined; return asm volatile ( \\ syscall \\ beq $a3, $zero, 1f \\ subu $v0, $zero, $v0 \\1: : [ret] "={$2}" (-> u32), + [r7_out] "={$7}" (r7_out), : [number] "{$2}" (@backingInt(number)), [arg1] "{$4}" (arg1), [arg2] "{$5}" (arg2), -- 2.54.0