Merge changes If42905f3,Id351a993,I8a082fd0
* changes: fdtrack: don't do anything while vforked. fdsan: don't do anything when vforked. Track whether a thread is currently vforked.
This commit is contained in:
commit
e3bc50d44f
10 changed files with 152 additions and 15 deletions
|
@ -31,17 +31,27 @@
|
||||||
|
|
||||||
ENTRY(vfork)
|
ENTRY(vfork)
|
||||||
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
||||||
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
|
// r3 = &__get_tls()[TLS_SLOT_THREAD_ID]
|
||||||
mrc p15, 0, r3, c13, c0, 3
|
mrc p15, 0, r3, c13, c0, 3
|
||||||
ldr r3, [r3, #(TLS_SLOT_THREAD_ID * 4)]
|
ldr r3, [r3, #(TLS_SLOT_THREAD_ID * 4)]
|
||||||
mov r0, #0
|
|
||||||
|
// Set cached_pid_ to 0, vforked_ to 1, and stash the previous value.
|
||||||
|
mov r0, #0x80000000
|
||||||
|
ldr r1, [r3, #12]
|
||||||
str r0, [r3, #12]
|
str r0, [r3, #12]
|
||||||
|
|
||||||
mov ip, r7
|
mov ip, r7
|
||||||
ldr r7, =__NR_vfork
|
ldr r7, =__NR_vfork
|
||||||
swi #0
|
swi #0
|
||||||
mov r7, ip
|
mov r7, ip
|
||||||
|
|
||||||
|
teq r0, #0
|
||||||
|
bxeq lr
|
||||||
|
|
||||||
|
// rc != 0: reset cached_pid_ and vforked_.
|
||||||
|
str r1, [r3, #12]
|
||||||
cmn r0, #(MAX_ERRNO + 1)
|
cmn r0, #(MAX_ERRNO + 1)
|
||||||
|
|
||||||
bxls lr
|
bxls lr
|
||||||
neg r0, r0
|
neg r0, r0
|
||||||
b __set_errno_internal
|
b __set_errno_internal
|
||||||
|
|
|
@ -36,10 +36,14 @@
|
||||||
|
|
||||||
ENTRY(vfork)
|
ENTRY(vfork)
|
||||||
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
||||||
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
|
// x9 = __get_tls()[TLS_SLOT_THREAD_ID]
|
||||||
mrs x0, tpidr_el0
|
mrs x9, tpidr_el0
|
||||||
ldr x0, [x0, #(TLS_SLOT_THREAD_ID * 8)]
|
ldr x9, [x9, #(TLS_SLOT_THREAD_ID * 8)]
|
||||||
str wzr, [x0, #20]
|
|
||||||
|
// Set cached_pid_ to 0, vforked_ to 1, and stash the previous value.
|
||||||
|
mov w0, #0x80000000
|
||||||
|
ldr w10, [x9, #20]
|
||||||
|
str w0, [x9, #20]
|
||||||
|
|
||||||
mov x0, #(CLONE_VM | CLONE_VFORK | SIGCHLD)
|
mov x0, #(CLONE_VM | CLONE_VFORK | SIGCHLD)
|
||||||
mov x1, xzr
|
mov x1, xzr
|
||||||
|
@ -50,6 +54,10 @@ __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
||||||
mov x8, __NR_clone
|
mov x8, __NR_clone
|
||||||
svc #0
|
svc #0
|
||||||
|
|
||||||
|
cbz x0, .L_exit
|
||||||
|
|
||||||
|
// rc != 0: reset cached_pid_ and vforked_.
|
||||||
|
str w10, [x9, #20]
|
||||||
cmn x0, #(MAX_ERRNO + 1)
|
cmn x0, #(MAX_ERRNO + 1)
|
||||||
cneg x0, x0, hi
|
cneg x0, x0, hi
|
||||||
b.hi __set_errno_internal
|
b.hi __set_errno_internal
|
||||||
|
|
|
@ -37,13 +37,25 @@ __BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
||||||
.cfi_adjust_cfa_offset 4
|
.cfi_adjust_cfa_offset 4
|
||||||
.cfi_rel_offset ecx, 0
|
.cfi_rel_offset ecx, 0
|
||||||
|
|
||||||
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
|
// Set cached_pid_ to 0, vforked_ to 1, and stash the previous value.
|
||||||
movl %gs:0, %eax
|
movl %gs:0, %eax
|
||||||
movl (TLS_SLOT_THREAD_ID * 4)(%eax), %eax
|
movl (TLS_SLOT_THREAD_ID * 4)(%eax), %eax
|
||||||
movl $0, 12(%eax)
|
movl 12(%eax), %edx
|
||||||
|
movl $0x80000000, 12(%eax)
|
||||||
|
|
||||||
movl $__NR_vfork, %eax
|
movl $__NR_vfork, %eax
|
||||||
int $0x80
|
int $0x80
|
||||||
|
|
||||||
|
test %eax, %eax
|
||||||
|
jz 1f
|
||||||
|
|
||||||
|
// rc != 0: restore the previous cached_pid_/vforked_ values.
|
||||||
|
pushl %ecx
|
||||||
|
movl %gs:0, %ecx
|
||||||
|
movl (TLS_SLOT_THREAD_ID * 4)(%ecx), %ecx
|
||||||
|
movl %edx, 12(%ecx)
|
||||||
|
popl %ecx
|
||||||
|
|
||||||
cmpl $-MAX_ERRNO, %eax
|
cmpl $-MAX_ERRNO, %eax
|
||||||
jb 1f
|
jb 1f
|
||||||
negl %eax
|
negl %eax
|
||||||
|
|
|
@ -35,14 +35,22 @@ ENTRY(vfork)
|
||||||
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
__BIONIC_WEAK_ASM_FOR_NATIVE_BRIDGE(vfork)
|
||||||
popq %rdi // Grab the return address.
|
popq %rdi // Grab the return address.
|
||||||
|
|
||||||
// __get_tls()[TLS_SLOT_THREAD_ID]->cached_pid_ = 0
|
// Set cached_pid_ to 0, vforked_ to 1, and stash the previous value.
|
||||||
mov %fs:0, %rax
|
mov %fs:0, %r8
|
||||||
mov (TLS_SLOT_THREAD_ID * 8)(%rax), %rax
|
mov (TLS_SLOT_THREAD_ID * 8)(%r8), %r8
|
||||||
movl $0, 20(%rax)
|
movl 20(%r8), %r9d
|
||||||
|
movl $0x80000000, 20(%r8)
|
||||||
|
|
||||||
movl $__NR_vfork, %eax
|
movl $__NR_vfork, %eax
|
||||||
syscall
|
syscall
|
||||||
pushq %rdi // Restore the return address.
|
pushq %rdi // Restore the return address.
|
||||||
|
|
||||||
|
test %eax, %eax
|
||||||
|
jz 1f
|
||||||
|
|
||||||
|
// rc != 0: restore the previous cached_pid_/vforked_ values.
|
||||||
|
movl %r9d, 20(%r8)
|
||||||
|
|
||||||
cmpq $-MAX_ERRNO, %rax
|
cmpq $-MAX_ERRNO, %rax
|
||||||
jb 1f
|
jb 1f
|
||||||
negl %eax
|
negl %eax
|
||||||
|
|
|
@ -246,6 +246,10 @@ uint64_t android_fdsan_get_tag_value(uint64_t tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int android_fdsan_close_with_tag(int fd, uint64_t expected_tag) {
|
int android_fdsan_close_with_tag(int fd, uint64_t expected_tag) {
|
||||||
|
if (__get_thread()->is_vforked()) {
|
||||||
|
return __close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
FDTRACK_CLOSE(fd);
|
FDTRACK_CLOSE(fd);
|
||||||
FdEntry* fde = GetFdEntry(fd);
|
FdEntry* fde = GetFdEntry(fd);
|
||||||
if (!fde) {
|
if (!fde) {
|
||||||
|
@ -296,6 +300,10 @@ uint64_t android_fdsan_get_owner_tag(int fd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) {
|
void android_fdsan_exchange_owner_tag(int fd, uint64_t expected_tag, uint64_t new_tag) {
|
||||||
|
if (__get_thread()->is_vforked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FdEntry* fde = GetFdEntry(fd);
|
FdEntry* fde = GetFdEntry(fd);
|
||||||
if (!fde) {
|
if (!fde) {
|
||||||
return;
|
return;
|
||||||
|
@ -332,6 +340,10 @@ android_fdsan_error_level android_fdsan_get_error_level() {
|
||||||
}
|
}
|
||||||
|
|
||||||
android_fdsan_error_level android_fdsan_set_error_level(android_fdsan_error_level new_level) {
|
android_fdsan_error_level android_fdsan_set_error_level(android_fdsan_error_level new_level) {
|
||||||
|
if (__get_thread()->is_vforked()) {
|
||||||
|
return android_fdsan_get_error_level();
|
||||||
|
}
|
||||||
|
|
||||||
return atomic_exchange(&GetFdTable().error_level, new_level);
|
return atomic_exchange(&GetFdTable().error_level, new_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,9 +70,12 @@ class pthread_internal_t {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pid_t cached_pid_;
|
uint32_t cached_pid_ : 31;
|
||||||
|
uint32_t vforked_ : 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
bool is_vforked() { return vforked_; }
|
||||||
|
|
||||||
pid_t invalidate_cached_pid() {
|
pid_t invalidate_cached_pid() {
|
||||||
pid_t old_value;
|
pid_t old_value;
|
||||||
get_cached_pid(&old_value);
|
get_cached_pid(&old_value);
|
||||||
|
|
|
@ -47,7 +47,8 @@ extern "C" _Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
|
||||||
#define FDTRACK_CREATE_NAME(name, fd_value) \
|
#define FDTRACK_CREATE_NAME(name, fd_value) \
|
||||||
({ \
|
({ \
|
||||||
int __fd = (fd_value); \
|
int __fd = (fd_value); \
|
||||||
if (__fd != -1 && __predict_false(__android_fdtrack_hook)) { \
|
if (__fd != -1 && __predict_false(__android_fdtrack_hook) && \
|
||||||
|
!__predict_false(__get_thread()->is_vforked())) { \
|
||||||
bionic_tls& tls = __get_bionic_tls(); \
|
bionic_tls& tls = __get_bionic_tls(); \
|
||||||
/* fdtrack_disabled is only true during reentrant calls. */ \
|
/* fdtrack_disabled is only true during reentrant calls. */ \
|
||||||
if (!__predict_false(tls.fdtrack_disabled)) { \
|
if (!__predict_false(tls.fdtrack_disabled)) { \
|
||||||
|
@ -76,7 +77,8 @@ extern "C" _Atomic(android_fdtrack_hook_t) __android_fdtrack_hook;
|
||||||
#define FDTRACK_CLOSE(fd_value) \
|
#define FDTRACK_CLOSE(fd_value) \
|
||||||
({ \
|
({ \
|
||||||
int __fd = (fd_value); \
|
int __fd = (fd_value); \
|
||||||
if (__fd != -1 && __predict_false(__android_fdtrack_hook)) { \
|
if (__fd != -1 && __predict_false(__android_fdtrack_hook) && \
|
||||||
|
!__predict_false(__get_thread()->is_vforked())) { \
|
||||||
bionic_tls& tls = __get_bionic_tls(); \
|
bionic_tls& tls = __get_bionic_tls(); \
|
||||||
if (!__predict_false(tls.fdtrack_disabled)) { \
|
if (!__predict_false(tls.fdtrack_disabled)) { \
|
||||||
int saved_errno = errno; \
|
int saved_errno = errno; \
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#if defined(__BIONIC__)
|
#if defined(__BIONIC__)
|
||||||
#include <android/fdsan.h>
|
#include <android/fdsan.h>
|
||||||
|
@ -192,3 +193,21 @@ TEST_F(FdsanTest, unique_fd_unowned_close_after_move) {
|
||||||
EXPECT_FDSAN_DEATH(close(fd_moved.get()), "expected to be unowned, actually owned by unique_fd");
|
EXPECT_FDSAN_DEATH(close(fd_moved.get()), "expected to be unowned, actually owned by unique_fd");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(FdsanTest, vfork) {
|
||||||
|
android::base::unique_fd fd(open("/dev/null", O_RDONLY));
|
||||||
|
|
||||||
|
pid_t rc = vfork();
|
||||||
|
ASSERT_NE(-1, rc);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
close(fd.get());
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
pid_t wait_result = waitpid(rc, &status, 0);
|
||||||
|
ASSERT_EQ(wait_result, rc);
|
||||||
|
ASSERT_TRUE(WIFEXITED(status));
|
||||||
|
ASSERT_EQ(0, WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
|
|
@ -289,4 +289,24 @@ FDTRACK_TEST(recvmsg, ({
|
||||||
ASSERT_EQ(3, ReceiveFileDescriptors(sockets[1], buf, sizeof(buf), &received_fd));
|
ASSERT_EQ(3, ReceiveFileDescriptors(sockets[1], buf, sizeof(buf), &received_fd));
|
||||||
received_fd.release();
|
received_fd.release();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
FDTRACK_TEST_NAME(vfork, "open", ({
|
||||||
|
int fd = open("/dev/null", O_RDONLY);
|
||||||
|
|
||||||
|
pid_t rc = vfork();
|
||||||
|
ASSERT_NE(-1, rc);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
close(fd);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
pid_t wait_result = waitpid(rc, &status, 0);
|
||||||
|
ASSERT_EQ(wait_result, rc);
|
||||||
|
ASSERT_TRUE(WIFEXITED(status));
|
||||||
|
ASSERT_EQ(0, WEXITSTATUS(status));
|
||||||
|
|
||||||
|
fd;
|
||||||
|
}));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
|
@ -41,6 +41,10 @@
|
||||||
|
|
||||||
#include "private/get_cpu_count_from_string.h"
|
#include "private/get_cpu_count_from_string.h"
|
||||||
|
|
||||||
|
#if defined(__BIONIC__)
|
||||||
|
#include "bionic/pthread_internal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NOFORTIFY)
|
#if defined(NOFORTIFY)
|
||||||
#define UNISTD_TEST unistd_nofortify
|
#define UNISTD_TEST unistd_nofortify
|
||||||
#define UNISTD_DEATHTEST unistd_nofortify_DeathTest
|
#define UNISTD_DEATHTEST unistd_nofortify_DeathTest
|
||||||
|
@ -431,6 +435,45 @@ TEST(UNISTD_TEST, syncfs) {
|
||||||
TestSyncFunction(syncfs);
|
TestSyncFunction(syncfs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(UNISTD_TEST, vfork) {
|
||||||
|
#if defined(__BIONIC__)
|
||||||
|
pthread_internal_t* self = __get_thread();
|
||||||
|
|
||||||
|
pid_t cached_pid;
|
||||||
|
ASSERT_TRUE(self->get_cached_pid(&cached_pid));
|
||||||
|
ASSERT_EQ(syscall(__NR_getpid), cached_pid);
|
||||||
|
ASSERT_FALSE(self->is_vforked());
|
||||||
|
|
||||||
|
pid_t rc = vfork();
|
||||||
|
ASSERT_NE(-1, rc);
|
||||||
|
if (rc == 0) {
|
||||||
|
if (self->get_cached_pid(&cached_pid)) {
|
||||||
|
const char* error = "__get_thread()->cached_pid_ set after vfork\n";
|
||||||
|
write(STDERR_FILENO, error, strlen(error));
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->is_vforked()) {
|
||||||
|
const char* error = "__get_thread()->vforked_ not set after vfork\n";
|
||||||
|
write(STDERR_FILENO, error, strlen(error));
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit(0);
|
||||||
|
} else {
|
||||||
|
ASSERT_TRUE(self->get_cached_pid(&cached_pid));
|
||||||
|
ASSERT_EQ(syscall(__NR_getpid), cached_pid);
|
||||||
|
ASSERT_FALSE(self->is_vforked());
|
||||||
|
|
||||||
|
int status;
|
||||||
|
pid_t wait_result = waitpid(rc, &status, 0);
|
||||||
|
ASSERT_EQ(wait_result, rc);
|
||||||
|
ASSERT_TRUE(WIFEXITED(status));
|
||||||
|
ASSERT_EQ(0, WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static void AssertGetPidCorrect() {
|
static void AssertGetPidCorrect() {
|
||||||
// The loop is just to make manual testing/debugging with strace easier.
|
// The loop is just to make manual testing/debugging with strace easier.
|
||||||
pid_t getpid_syscall_result = syscall(__NR_getpid);
|
pid_t getpid_syscall_result = syscall(__NR_getpid);
|
||||||
|
|
Loading…
Reference in a new issue