mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user