Copy out rseq::cpu_id_start and rseq::cpu_id even if they don't change.

Userspace can mutate them, and this is consistent with Linux.

PiperOrigin-RevId: 610902711
This commit is contained in:
Jamie Liu
2024-02-27 16:00:13 -08:00
committed by gVisor bot
parent 54c81b0033
commit e0c2c50fd0
7 changed files with 207 additions and 22 deletions
+14 -20
View File
@@ -201,26 +201,20 @@ func (app *runApp) execute(t *Task) taskRunState {
if t.rseqPreempted {
t.rseqPreempted = false
if t.rseqAddr != 0 || t.oldRSeqCPUAddr != 0 {
// Linux writes the CPU on every preemption. We only do
// so if it changed. Thus we may delay delivery of
// SIGSEGV if rseqAddr/oldRSeqCPUAddr is invalid.
cpu := int32(hostcpu.GetCPU())
if t.rseqCPU != cpu {
t.rseqCPU = cpu
if err := t.rseqCopyOutCPU(); err != nil {
t.Debugf("Failed to copy CPU to %#x for rseq: %v", t.rseqAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
return (*runApp)(nil)
}
if err := t.oldRSeqCopyOutCPU(); err != nil {
t.Debugf("Failed to copy CPU to %#x for old rseq: %v", t.oldRSeqCPUAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
return (*runApp)(nil)
}
t.rseqCPU = int32(hostcpu.GetCPU())
if err := t.rseqCopyOutCPU(); err != nil {
t.Debugf("Failed to copy CPU to %#x for rseq: %v", t.rseqAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
return (*runApp)(nil)
}
if err := t.oldRSeqCopyOutCPU(); err != nil {
t.Debugf("Failed to copy CPU to %#x for old rseq: %v", t.oldRSeqCPUAddr, err)
t.forceSignal(linux.SIGSEGV, false)
t.SendSignal(SignalInfoPriv(linux.SIGSEGV))
// Re-enter the task run loop for signal delivery.
return (*runApp)(nil)
}
}
t.rseqInterrupt()
+13
View File
@@ -13,6 +13,7 @@
// limitations under the License.
#include <errno.h>
#include <linux/membarrier.h>
#include <signal.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -196,6 +197,18 @@ TEST(RseqTest, InvalidAbortClearsCS) {
RunChildTest(kRseqTestInvalidAbortClearsCS, 0);
}
// TODO(b/326665974): Implement clone() in rseq/start_arm64.S.
#if defined(__x86_64__)
TEST(RseqTest, MembarrierResetsCpuIdStart) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported()));
int cmds = syscall(SYS_membarrier, MEMBARRIER_CMD_QUERY, 0);
SKIP_IF(cmds < 0);
SKIP_IF((cmds & MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ) == 0);
RunChildTest(kRseqTestMembarrierResetsCpuIdStart, 0);
}
#endif
} // namespace
} // namespace testing
+75
View File
@@ -319,6 +319,78 @@ int TestInvalidAbortClearsCS() {
return 0;
}
// rseq.cpu_id_start is overwritten by RSEQ fence.
int TestMembarrierResetsCpuIdStart() {
struct rseq r = {};
int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature);
if (sys_errno(ret) != 0) {
return 1;
}
ret = sys_membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, 0);
if (sys_errno(ret) != 0) {
return 1;
}
constexpr size_t kStackSize = 2 << 20; // 2 MB
uintptr_t child_stack_start =
sys_mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (sys_errno(child_stack_start) != 0) {
return 1;
}
uintptr_t child_stack_end = child_stack_start + kStackSize;
// Set cpu_id_start to a negative value.
__atomic_store_n(&r.cpu_id_start, 0xffffffff, __ATOMIC_RELAXED);
// Start a thread to invoke the RSEQ fence.
uint32_t child_cleartid = 0;
bool fenced = false;
auto tid = clone(
+[](void* arg) {
int ret = sys_membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, 0);
if (sys_errno(ret) != 0) {
sys_exit_group(1);
}
__atomic_store_n(static_cast<bool*>(arg), true, __ATOMIC_RELAXED);
return 0;
},
child_stack_end,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD |
CLONE_CHILD_CLEARTID | CLONE_CHILD_SETTID,
&fenced, &child_cleartid);
if (sys_errno(tid) != 0) {
return 1;
}
// Expect that cpu_id_start will be overwritten with a real CPU number when
// the thread invokes the RSEQ fence.
while (true) {
if (__atomic_load_n(&fenced, __ATOMIC_ACQUIRE)) {
if (static_cast<int32_t>(
__atomic_load_n(&r.cpu_id_start, __ATOMIC_RELAXED)) < 0) {
return 1;
}
break;
}
}
// Wait for the thread to exit.
while (true) {
uint32_t cur_child_cleartid =
__atomic_load_n(&child_cleartid, __ATOMIC_RELAXED);
if (cur_child_cleartid == 0) {
break;
}
auto ret =
sys_futex(&child_cleartid, FUTEX_WAIT, cur_child_cleartid, nullptr);
if (ret != 0 && sys_errno(ret) != EAGAIN && sys_errno(ret) != EINTR) {
return 1;
}
}
return 0;
}
// Exit codes:
// 0 - Pass
// 1 - Fail
@@ -369,6 +441,9 @@ extern "C" int main(int argc, char** argv, char** envp) {
if (strcmp(argv[1], kRseqTestInvalidAbortClearsCS) == 0) {
return TestInvalidAbortClearsCS();
}
if (strcmp(argv[1], kRseqTestMembarrierResetsCpuIdStart) == 0) {
return TestMembarrierResetsCpuIdStart();
}
return 3;
}
+40
View File
@@ -43,3 +43,43 @@ raw_syscall:
.size raw_syscall,.-raw_syscall
.section .note.GNU-stack,"",@progbits
.text
.globl clone
.type clone, @function
clone:
// Initially:
// %rdi = fn
// %rsi = stack
// %edx = flags
// %rcx = arg
// %r8 = child_tid
push %rbx
push %r12
mov %rdi,%rbx
mov %rcx,%r12
mov %edx,%edi // flags
// stack already in %rsi
xor %edx,%edx // parent_tid
mov %r8,%r10 // child_tid
xor %r9d,%r9d // tls
mov $56,%rax // SYS_clone
syscall // clobbers %rcx, %r11
cmp $0,%rax
je child
pop %r12
pop %rbx
ret
child:
mov %rsi,%rsp
xor %rbp,%rbp
mov %r12,%rdi
call *%rbx
mov %rax,%rdi
mov $60,%rax // SYS_exit
syscall
hlt
.size clone,.-clone
.section .note.GNU-stack,"",@progbits
+12
View File
@@ -43,3 +43,15 @@ raw_syscall:
.size raw_syscall,.-raw_syscall
.section .note.GNU-stack,"",@progbits
.text
.globl clone
.type clone, @function
clone:
// TODO(b/326665974)
mov x0,#-38 // ENOSYS
ret
.size clone,.-clone
.section .note.GNU-stack,"",@progbits
+51 -2
View File
@@ -19,11 +19,17 @@
// Syscall numbers.
#if defined(__x86_64__)
constexpr int kGetpid = 39;
constexpr int kExitGroup = 231;
constexpr int kFutex = 202;
constexpr int kGetpid = 39;
constexpr int kMembarrier = 324;
constexpr int kMmap = 9;
#elif defined(__aarch64__)
constexpr int kGetpid = 172;
constexpr int kExitGroup = 94;
constexpr int kFutex = 98;
constexpr int kGetpid = 172;
constexpr int kMembarrier = 283;
constexpr int kMmap = 222;
#else
#error "Unknown architecture"
#endif
@@ -41,6 +47,8 @@ constexpr int kMaxErrno = 4095;
// Errno values.
#define EPERM 1
#define EINTR 4
#define EAGAIN 11
#define EFAULT 14
#define EBUSY 16
#define EINVAL 22
@@ -56,13 +64,54 @@ static inline int sys_errno(uintptr_t rval) {
extern "C" uintptr_t raw_syscall(int number, ...);
extern "C" int clone(int (*fn)(void*), uintptr_t stack, int flags, void* arg,
uint32_t* child_tid);
// clone flags:
#define CLONE_VM 0x00000100
#define CLONE_FS 0x00000200
#define CLONE_FILES 0x00000400
#define CLONE_SIGHAND 0x00000800
#define CLONE_THREAD 0x00010000
#define CLONE_CHILD_CLEARTID 0x00200000
#define CLONE_CHILD_SETTID 0x01000000
static inline void sys_exit_group(int status) {
raw_syscall(kExitGroup, status);
}
static inline uintptr_t sys_futex(uint32_t* uaddr, int futex_op, uint32_t val,
const struct timespec* timeout) {
return raw_syscall(kFutex, uaddr, futex_op, val, timeout);
}
// futex ops:
#define FUTEX_WAIT 0
static inline int sys_getpid() {
return static_cast<int>(raw_syscall(kGetpid));
}
static inline uintptr_t sys_membarrier(int cmd, unsigned int flags) {
return raw_syscall(kMembarrier, cmd, flags);
}
// membarrier commands:
#define MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ (1 << 7)
#define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ (1 << 8)
static inline uintptr_t sys_mmap(void* addr, size_t length, int prot, int flags,
int fd, int64_t offset) {
return raw_syscall(kMmap, addr, length, prot, flags, fd, offset);
}
// mmap options:
#define PROT_READ 0x1
#define PROT_WRITE 0x2
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_STACK 0x020000
} // namespace testing
} // namespace gvisor
+2
View File
@@ -34,6 +34,8 @@ constexpr char kRseqTestAbortSignature[] = "abort-signature";
constexpr char kRseqTestAbortPreCommit[] = "abort-precommit";
constexpr char kRseqTestAbortClearsCS[] = "abort-clears-cs";
constexpr char kRseqTestInvalidAbortClearsCS[] = "invalid-abort-clears-cs";
constexpr char kRseqTestMembarrierResetsCpuIdStart[] =
"membarrier-resets-cpu-id-start";
} // namespace testing
} // namespace gvisor