mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
runtime: fix Darwin syscall return value truncation and lost arguments
Go 1.26 replaced the individual Darwin syscall entry points (syscall, syscallX, syscallPtr, syscall6, syscall6X) with two variadic functions: syscalln and rawsyscalln. The old wrappers now have Go bodies that call these, then use errno/errnoX/errnoPtr to interpret the result. This caused two bugs in our rawsyscalln implementation: 1. Return value truncation: We used call_syscall/call_syscall6 which return int32, truncating 64-bit results (pointers from fdopendir, offsets from lseek, addresses from mmap). For example, a DIR* pointer returned by fdopendir would lose its upper 32 bits on arm64, causing SIGSEGV when later accessed. 2. Lost 4th argument: The case 4/5/6 fallthrough had 'a3 = args[3]' instead of 'a4 = args[3]', and a3 was immediately overwritten by 'a1, a2, a3 = args[0], args[1], args[2]'. This lost the 4th syscall argument entirely, breaking pread (offset=0) and causing wrong data to be read from files. Fix by using call_syscallX/call_syscall6X (returning full uintptr), always reading errno (letting the Go wrappers decide what to do with it), and correcting the argument assignment.
This commit is contained in:
@@ -2,6 +2,17 @@
|
||||
|
||||
package runtime
|
||||
|
||||
// Go 1.26 replaced the individual Darwin syscall functions (syscall, syscallX,
|
||||
// syscallPtr, syscall6, syscall6X) with two variadic entry points: syscalln and
|
||||
// rawsyscalln. All the old wrappers now have Go bodies that call these and then
|
||||
// interpret the result (errno, errnoX, errnoPtr). Because the callers decide
|
||||
// how to check for errors, we must:
|
||||
// - always return the full register-width result (use call_syscallX /
|
||||
// call_syscall6X which return uintptr, not call_syscall / call_syscall6
|
||||
// which truncate to int32), and
|
||||
// - always read errno so that the Go wrappers have the value available when
|
||||
// the error condition is met.
|
||||
|
||||
// syscall_syscalln is a wrapper around the libc call with variable arguments.
|
||||
//
|
||||
//go:nosplit
|
||||
@@ -30,7 +41,9 @@ func syscall_rawsyscalln(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
a1 = args[0]
|
||||
fallthrough
|
||||
case 0:
|
||||
return runtime_syscall(fn, a1, a2, a3)
|
||||
r1 = call_syscallX(fn, a1, a2, a3)
|
||||
err = uintptr(*libc_errno_location())
|
||||
return
|
||||
|
||||
case 6:
|
||||
a6 = args[5]
|
||||
@@ -39,31 +52,13 @@ func syscall_rawsyscalln(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
|
||||
a5 = args[4]
|
||||
fallthrough
|
||||
case 4:
|
||||
a3 = args[3]
|
||||
a4 = args[3]
|
||||
|
||||
a1, a2, a3 = args[0], args[1], args[2]
|
||||
return runtime_syscall6(fn, a1, a2, a3, a4, a5, a6)
|
||||
r1 = call_syscall6X(fn, a1, a2, a3, a4, a5, a6)
|
||||
err = uintptr(*libc_errno_location())
|
||||
return
|
||||
}
|
||||
|
||||
panic("syscall args not handled")
|
||||
}
|
||||
|
||||
func runtime_syscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
||||
result := call_syscall(fn, a1, a2, a3)
|
||||
r1 = uintptr(result)
|
||||
if result == -1 {
|
||||
// Syscall returns -1 on failure.
|
||||
err = uintptr(*libc_errno_location())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func runtime_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) {
|
||||
result := call_syscall6(fn, a1, a2, a3, a4, a5, a6)
|
||||
r1 = uintptr(result)
|
||||
if result == -1 {
|
||||
// Syscall returns -1 on failure.
|
||||
err = uintptr(*libc_errno_location())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user