fix(syscall): pin r10/r8 for 4- and 5-argument syscalls
This commit is contained in:
@@ -14,6 +14,9 @@
|
||||
* then point exactly at fake + VLIBC_TCB_ERRNO_OFF, a failing syscall
|
||||
* must land its errno in that slot, and the real thread pointer is
|
||||
* restored afterwards.
|
||||
* 4. register pinning: a 4-arg (SYS_rt_sigprocmask) and a 5-arg
|
||||
* (SYS_ppoll) syscall whose later arguments are load-bearing must see
|
||||
* them in r10/r8, where the x86_64 kernel ABI places args 4 and 5.
|
||||
*
|
||||
* All diagnostics go through raw SYS_write (no printf): between installing
|
||||
* and restoring the fake thread pointer the test must not call any libc
|
||||
@@ -32,6 +35,11 @@
|
||||
#define TEST_O_RDONLY 0
|
||||
#define TEST_O_CLOEXEC 0x80000
|
||||
|
||||
/* Constants the public <signal.h>/<poll.h> will own; test-local copies. */
|
||||
#define TEST_SIG_BLOCK 0
|
||||
#define TEST_SIG_SETMASK 2
|
||||
#define TEST_SIGUSR1 10
|
||||
|
||||
/* arch_prctl codes (kernel UAPI). */
|
||||
#define TEST_ARCH_SET_FS 0x1002
|
||||
#define TEST_ARCH_GET_FS 0x1003
|
||||
@@ -101,6 +109,54 @@ restore_real_tcb(unsigned long real_fs)
|
||||
return syscall_ret(__syscall2(SYS_arch_prctl, TEST_ARCH_SET_FS, (long)real_fs)) != 0;
|
||||
}
|
||||
|
||||
struct test_timespec
|
||||
{
|
||||
long tv_sec;
|
||||
long tv_nsec;
|
||||
};
|
||||
|
||||
/*
|
||||
* 4-arg regression probe: block SIGUSR1, read the mask back through a NULL
|
||||
* set, restore. The 4th argument (sigsetsize) must reach the kernel in r10;
|
||||
* placed anywhere else it fails with -EINVAL.
|
||||
*/
|
||||
static int
|
||||
probe_rt_sigprocmask(void)
|
||||
{
|
||||
unsigned long mask = 1UL << (TEST_SIGUSR1 - 1);
|
||||
unsigned long oldmask = 0;
|
||||
unsigned long readback = 0;
|
||||
|
||||
if (__syscall4(SYS_rt_sigprocmask, TEST_SIG_BLOCK, (long)&mask, (long)&oldmask, 8) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (__syscall4(SYS_rt_sigprocmask, TEST_SIG_BLOCK, 0, (long)&readback, 8) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (__syscall4(SYS_rt_sigprocmask, TEST_SIG_SETMASK, (long)&oldmask, 0, 8) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return (readback & (1UL << (TEST_SIGUSR1 - 1))) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 5-arg regression probe: ppoll with no fds and a zero timeout must return
|
||||
* 0. The 4th argument (sigmask) must reach r10 and the 5th (sigsetsize) r8;
|
||||
* misplaced, the kernel reads a pointer value where the set size belongs and
|
||||
* answers -EINVAL.
|
||||
*/
|
||||
static int
|
||||
probe_ppoll(void)
|
||||
{
|
||||
struct test_timespec ts = {0, 0};
|
||||
unsigned long mask = 0;
|
||||
|
||||
return __syscall5(SYS_ppoll, 0, 0, (long)&ts, (long)&mask, 8) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Failure scenario (-f): SYS_openat on a nonexistent path must return -1
|
||||
* and leave errno == ENOENT in the TCB slot. Prints the observed result to
|
||||
@@ -173,6 +229,10 @@ main(int argc, char **argv)
|
||||
check(*(int *)((char *)fake_tcb + VLIBC_TCB_ERRNO_OFF) == ENOENT,
|
||||
"fake TCB errno slot retains ENOENT after restore");
|
||||
|
||||
/* 4. Register pinning for 4- and 5-argument syscalls (r10/r8). */
|
||||
check(probe_rt_sigprocmask(), "rt_sigprocmask 4-arg r10 pinning");
|
||||
check(probe_ppoll(), "ppoll 5-arg r10/r8 pinning");
|
||||
|
||||
/*
|
||||
* VLIBC_TCB_ERRNO_OFF addresses slot 1 of whichever TCB the FS thread
|
||||
* pointer selects; under the host libc that slot is its private TLS
|
||||
|
||||
Reference in New Issue
Block a user