Skip to content

Commit fec8a13

Browse files
authored
Safe fetch 64-bit value and pointer (#247)
* v0 * v1 * Fix * v2 * Remove old safe access handler
1 parent 880f08e commit fec8a13

File tree

4 files changed

+118
-75
lines changed

4 files changed

+118
-75
lines changed

ddprof-lib/src/main/cpp/profiler.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -886,28 +886,6 @@ bool Profiler::crashHandler(int signo, siginfo_t *siginfo, void *ucontext) {
886886
return false;
887887
}
888888

889-
uintptr_t length = SafeAccess::skipLoad(pc);
890-
if (length > 0) {
891-
// Skip the fault instruction, as if it successfully loaded NULL
892-
frame.pc() += length;
893-
frame.retval() = 0;
894-
if (thrd != nullptr) {
895-
thrd->exitCrashHandler();
896-
}
897-
return true;
898-
}
899-
900-
length = SafeAccess::skipLoadArg(pc);
901-
if (length > 0) {
902-
// Act as if the load returned default_value argument
903-
frame.pc() += length;
904-
frame.retval() = frame.arg1();
905-
if (thrd != nullptr) {
906-
thrd->exitCrashHandler();
907-
}
908-
return true;
909-
}
910-
911889
if (WX_MEMORY && Trap::isFaultInstruction(pc)) {
912890
if (thrd != nullptr) {
913891
thrd->exitCrashHandler();

ddprof-lib/src/main/cpp/safeAccess.cpp

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <ucontext.h>
2121

2222
extern "C" int safefetch32_cont(int* adr, int errValue);
23+
extern "C" int64_t safefetch64_cont(int64_t* adr, int64_t errValue);
2324

2425
#ifdef __APPLE__
2526
#if defined(__x86_64__)
@@ -37,12 +38,12 @@ extern "C" int safefetch32_cont(int* adr, int errValue);
3738
#endif
3839

3940
/**
40-
Loading a 32-bit value from specific address, the errValue will be returned
41+
Loading a 32-bit/64-bit value from specific address, the errValue will be returned
4142
if the address is invalid.
4243
The load is protected by the 'handle_safefetch` signal handler, who sets next `pc`
43-
to `safefetch32_cont`, upon returning from signal handler, `safefetch32_cont` returns `errValue`
44+
to `safefetch32_cont/safefetch64_cont`, upon returning from signal handler,
45+
`safefetch32_cont/safefetch64_cont` returns `errValue`
4446
**/
45-
4647
#if defined(__x86_64__)
4748
#ifdef __APPLE__
4849
asm(R"(
@@ -56,6 +57,16 @@ extern "C" int safefetch32_cont(int* adr, int errValue);
5657
_safefetch32_cont:
5758
movl %esi, %eax
5859
ret
60+
.globl _safefetch64_impl
61+
.private_extern _safefetch64_impl
62+
_safefetch64_impl:
63+
movq (%rdi), %rax
64+
ret
65+
.globl _safefetch64_cont
66+
.private_extern _safefetch64_cont
67+
_safefetch64_cont:
68+
movq %rsi, %rax
69+
ret
5970
)");
6071
#else
6172
asm(R"(
@@ -72,6 +83,18 @@ extern "C" int safefetch32_cont(int* adr, int errValue);
7283
safefetch32_cont:
7384
movl %esi, %eax
7485
ret
86+
.globl safefetch64_impl
87+
.hidden safefetch64_impl
88+
.type safefetch64_imp, %function
89+
safefetch64_impl:
90+
movq (%rdi), %rax
91+
ret
92+
.globl safefetch64_cont
93+
.hidden safefetch64_cont
94+
.type safefetch64_cont, %function
95+
safefetch64_cont:
96+
movq %rsi, %rax
97+
ret
7598
)");
7699
#endif // __APPLE__
77100
#elif defined(__aarch64__)
@@ -85,6 +108,16 @@ extern "C" int safefetch32_cont(int* adr, int errValue);
85108
.globl _safefetch32_cont
86109
.private_extern _safefetch32_cont
87110
_safefetch32_cont:
111+
mov w0, w1
112+
ret
113+
.globl _safefetch64_impl
114+
.private_extern _safefetch64_impl
115+
_safefetch64_impl:
116+
ldr x0, [x0]
117+
ret
118+
.globl _safefetch64_cont
119+
.private_extern _safefetch64_cont
120+
_safefetch64_cont:
88121
mov x0, x1
89122
ret
90123
)");
@@ -101,6 +134,18 @@ extern "C" int safefetch32_cont(int* adr, int errValue);
101134
.hidden safefetch32_cont
102135
.type safefetch32_cont, %function
103136
safefetch32_cont:
137+
mov w0, w1
138+
ret
139+
.globl safefetch64_impl
140+
.hidden safefetch64_impl
141+
.type safefetch64_impl, %function
142+
safefetch64_impl:
143+
ldr x0, [x0]
144+
ret
145+
.globl safefetch64_cont
146+
.hidden safefetch64_cont
147+
.type safefetch64_cont, %function
148+
safefetch64_cont:
104149
mov x0, x1
105150
ret
106151
)");
@@ -114,6 +159,9 @@ bool SafeAccess::handle_safefetch(int sig, void* context) {
114159
if (pc == (uintptr_t)safefetch32_impl) {
115160
uc->current_pc = (uintptr_t)safefetch32_cont;
116161
return true;
162+
} else if (pc == (uintptr_t)safefetch64_impl) {
163+
uc->current_pc = (uintptr_t)safefetch64_cont;
164+
return true;
117165
}
118166
}
119167
return false;

ddprof-lib/src/main/cpp/safeAccess.h

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <stdint.h>
2424

2525
extern "C" int safefetch32_impl(int* adr, int errValue);
26+
extern "C" int64_t safefetch64_impl(int64_t* adr, int64_t errValue);
2627

2728
#ifdef __clang__
2829
#define NOINLINE __attribute__((noinline))
@@ -38,62 +39,31 @@ class SafeAccess {
3839
return safefetch32_impl(ptr, errorValue);
3940
}
4041

41-
static bool handle_safefetch(int sig, void* context);
42-
43-
NOINLINE NOADDRSANITIZE __attribute__((aligned(16))) static void *load(void **ptr) {
44-
return *ptr;
42+
static inline int64_t safeFetch64(int64_t* ptr, int64_t errorValue) {
43+
return safefetch64_impl(ptr, errorValue);
4544
}
4645

47-
NOINLINE NOADDRSANITIZE __attribute__((aligned(16))) static u32 load32(u32 *ptr,
48-
u32 default_value) {
49-
return *ptr;
50-
}
46+
static bool handle_safefetch(int sig, void* context);
5147

52-
NOINLINE NOADDRSANITIZE __attribute__((aligned(16))) static void *
53-
loadPtr(void **ptr, void *default_value) {
54-
return *ptr;
48+
static inline void *load(void **ptr) {
49+
return loadPtr(ptr, nullptr);
5550
}
5651

57-
NOADDRSANITIZE static uintptr_t skipLoad(uintptr_t pc) {
58-
if (pc - (uintptr_t)load < sizeSafeLoadFunc) {
59-
#if defined(__x86_64__)
60-
return *(u16 *)pc == 0x8b48 ? 3 : 0; // mov rax, [reg]
61-
#elif defined(__i386__)
62-
return *(u8 *)pc == 0x8b ? 2 : 0; // mov eax, [reg]
63-
#elif defined(__arm__) || defined(__thumb__)
64-
return (*(instruction_t *)pc & 0x0e50f000) == 0x04100000
65-
? 4
66-
: 0; // ldr r0, [reg]
67-
#elif defined(__aarch64__)
68-
return (*(instruction_t *)pc & 0xffc0001f) == 0xf9400000
69-
? 4
70-
: 0; // ldr x0, [reg]
71-
#else
72-
return sizeof(instruction_t);
73-
#endif
74-
}
75-
return 0;
52+
static inline u32 load32(u32 *ptr, u32 default_value) {
53+
int res = safefetch32_impl((int*)ptr, (int)default_value);
54+
return static_cast<u32>(res);
7655
}
7756

78-
static uintptr_t skipLoadArg(uintptr_t pc) {
79-
#if defined(__aarch64__)
80-
if ((pc - (uintptr_t)load32) < sizeSafeLoadFunc ||
81-
(pc - (uintptr_t)loadPtr) < sizeSafeLoadFunc) {
82-
return 4;
83-
}
84-
#endif
85-
return 0;
57+
static inline void *loadPtr(void** ptr, void* default_value) {
58+
#if defined(__x86_64__) || defined(__aarch64__)
59+
int64_t res = safefetch64_impl((int64_t*)ptr, (int64_t)reinterpret_cast<uintptr_t>(default_value));
60+
return (void*)static_cast<uintptr_t>(res);
61+
#elif defined(__i386__) || defined(__arm__) || defined(__thumb__)
62+
int res = safefetch32_impl((int*)ptr, (int)default_value);
63+
return (void*)res;
64+
#endif
65+
return *ptr;
8666
}
87-
#ifndef __SANITIZE_ADDRESS__
88-
constexpr static size_t sizeSafeLoadFunc = 16;
89-
#else
90-
// asan significantly increases the size of the load function
91-
// checking disassembled code can help adjust the value
92-
// gdb --batch -ex 'disas _ZN10SafeAccess4loadEPPv' ./elfparser_ut
93-
// I see that the functions can also have a 156 bytes size for the load32
94-
// and 136 for the loadPtr functions
95-
constexpr static inline size_t sizeSafeLoadFunc = 132;
96-
#endif
9767
};
9868

9969
#endif // _SAFEACCESS_H

ddprof-lib/src/test/cpp/safefetch_ut.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <gmock/gmock.h>
22
#include <gtest/gtest.h>
3+
#include <climits>
34
#include <signal.h>
45

56
#include "safeAccess.h"
@@ -34,18 +35,64 @@ class SafeFetchTest : public ::testing::Test {
3435
};
3536

3637

37-
TEST_F(SafeFetchTest, validAccess) {
38+
TEST_F(SafeFetchTest, validAccess32) {
3839
int i = 42;
3940
int* p = &i;
4041
int res = SafeAccess::safeFetch32(p, -1);
4142
EXPECT_EQ(res, i);
43+
i = INT_MAX;
44+
res = SafeAccess::safeFetch32(p, -1);
45+
EXPECT_EQ(res, i);
46+
i = INT_MIN;
47+
res = SafeAccess::safeFetch32(p, 0);
48+
EXPECT_EQ(res, i);
4249
}
4350

4451

45-
TEST_F(SafeFetchTest, invalidAccess) {
52+
TEST_F(SafeFetchTest, invalidAccess32) {
4653
int* p = nullptr;
4754
int res = SafeAccess::safeFetch32(p, -1);
4855
EXPECT_EQ(res, -1);
4956
res = SafeAccess::safeFetch32(p, -2);
5057
EXPECT_EQ(res, -2);
5158
}
59+
60+
TEST_F(SafeFetchTest, validAccess64) {
61+
int64_t i = 42;
62+
int64_t* p = &i;
63+
int64_t res = SafeAccess::safeFetch64(p, -1);
64+
EXPECT_EQ(res, i);
65+
i = LONG_MIN;
66+
res = SafeAccess::safeFetch64(p, -19);
67+
EXPECT_EQ(res, i);
68+
i = LONG_MAX;
69+
res = SafeAccess::safeFetch64(p, -1);
70+
EXPECT_EQ(res, i);
71+
}
72+
73+
TEST_F(SafeFetchTest, invalidAccess64) {
74+
int64_t* p = nullptr;
75+
int64_t res = SafeAccess::safeFetch64(p, -1);
76+
EXPECT_EQ(res, -1);
77+
res = SafeAccess::safeFetch64(p, -2);
78+
EXPECT_EQ(res, -2);
79+
}
80+
81+
TEST_F(SafeFetchTest, validAccessPtr) {
82+
char c = 'a';
83+
void* p = (void*)&c;
84+
void** pp = &p;
85+
void* res = SafeAccess::loadPtr(pp, nullptr);
86+
EXPECT_EQ(res, p);
87+
}
88+
89+
TEST_F(SafeFetchTest, invalidAccessPtr) {
90+
int a, b;
91+
void* ap = (void*)&a;
92+
void* bp = (void*)&b;
93+
void** pp = nullptr;
94+
void* res = SafeAccess::loadPtr(pp, ap);
95+
EXPECT_EQ(res, ap);
96+
res = SafeAccess::loadPtr(pp, bp);
97+
EXPECT_EQ(res, bp);
98+
}

0 commit comments

Comments
 (0)