|  | /* Copyright (c) 2018, Google Inc. | 
|  | * | 
|  | * Permission to use, copy, modify, and/or distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
|  | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | 
|  | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
|  | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ | 
|  |  | 
|  | #include "abi_test.h" | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <array> | 
|  |  | 
|  | #include <openssl/mem.h> | 
|  | #include <openssl/rand.h> | 
|  | #include <openssl/span.h> | 
|  |  | 
|  | #if defined(OPENSSL_X86_64) && defined(SUPPORTS_ABI_TEST) | 
|  | #if defined(OPENSSL_LINUX) && defined(BORINGSSL_HAVE_LIBUNWIND) | 
|  | #define SUPPORTS_UNWIND_TEST | 
|  | #define UNW_LOCAL_ONLY | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <libunwind.h> | 
|  | #include <pthread.h> | 
|  | #include <signal.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  | #include <unistd.h> | 
|  | #elif defined(OPENSSL_WINDOWS) | 
|  | #define SUPPORTS_UNWIND_TEST | 
|  | OPENSSL_MSVC_PRAGMA(warning(push, 3)) | 
|  | #include <windows.h> | 
|  | #include <dbghelp.h> | 
|  | OPENSSL_MSVC_PRAGMA(warning(pop)) | 
|  | #endif | 
|  | #endif  // X86_64 && SUPPORTS_ABI_TEST | 
|  |  | 
|  | // FIPS mode breaks unwind tests. See https://crbug.com/boringssl/289. | 
|  | #if defined(BORINGSSL_FIPS) | 
|  | #undef SUPPORTS_UNWIND_TEST | 
|  | #endif | 
|  |  | 
|  |  | 
|  | namespace abi_test { | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | static bool g_unwind_tests_enabled = false; | 
|  |  | 
|  | std::string FixVAArgsString(const char *str) { | 
|  | std::string ret = str; | 
|  | size_t idx = ret.find(','); | 
|  | if (idx == std::string::npos) { | 
|  | return ret + "()"; | 
|  | } | 
|  | size_t idx2 = idx + 1; | 
|  | while (idx2 < ret.size() && ret[idx2] == ' ') { | 
|  | idx2++; | 
|  | } | 
|  | while (idx > 0 && ret[idx - 1] == ' ') { | 
|  | idx--; | 
|  | } | 
|  | return ret.substr(0, idx) + "(" + ret.substr(idx2) + ")"; | 
|  | } | 
|  |  | 
|  | #if defined(SUPPORTS_ABI_TEST) | 
|  | // ForEachMismatch calls |func| for each register where |a| and |b| differ. | 
|  | template <typename Func> | 
|  | static void ForEachMismatch(const CallerState &a, const CallerState &b, | 
|  | const Func &func) { | 
|  | #define CALLER_STATE_REGISTER(type, name) \ | 
|  | if (a.name != b.name) {                 \ | 
|  | func(#name);                          \ | 
|  | } | 
|  | LOOP_CALLER_STATE_REGISTERS() | 
|  | #undef CALLER_STATE_REGISTER | 
|  | } | 
|  |  | 
|  | // ReadUnwindResult adds the results of the most recent unwind test to |out|. | 
|  | static void ReadUnwindResult(Result *out); | 
|  |  | 
|  | crypto_word_t RunTrampoline(Result *out, crypto_word_t func, | 
|  | const crypto_word_t *argv, size_t argc, | 
|  | bool unwind) { | 
|  | CallerState state; | 
|  | RAND_bytes(reinterpret_cast<uint8_t *>(&state), sizeof(state)); | 
|  |  | 
|  | unwind &= g_unwind_tests_enabled; | 
|  | CallerState state2 = state; | 
|  | crypto_word_t ret = abi_test_trampoline(func, &state2, argv, argc, unwind); | 
|  | #if defined(OPENSSL_X86_64) || defined(OPENSSL_X86) | 
|  | // Query and clear the direction flag early, so negative tests do not | 
|  | // interfere with |malloc|. | 
|  | bool direction_flag = abi_test_get_and_clear_direction_flag(); | 
|  | #endif  // OPENSSL_X86_64 || OPENSSL_X86 | 
|  |  | 
|  | *out = Result(); | 
|  | ForEachMismatch(state, state2, [&](const char *reg) { | 
|  | out->errors.push_back(std::string(reg) + " was not restored after return"); | 
|  | }); | 
|  | #if defined(OPENSSL_X86_64) || defined(OPENSSL_X86) | 
|  | // Linux and Windows ABIs for x86 require the direction flag be cleared on | 
|  | // return. (Some OpenSSL assembly preserves it, which is stronger, but we only | 
|  | // require what is specified by the ABI so |CHECK_ABI| works with C compiler | 
|  | // output.) | 
|  | if (direction_flag) { | 
|  | out->errors.emplace_back("Direction flag set after return"); | 
|  | } | 
|  | #endif  // OPENSSL_X86_64 || OPENSSL_X86 | 
|  | if (unwind) { | 
|  | ReadUnwindResult(out); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  | #endif  // SUPPORTS_ABI_TEST | 
|  |  | 
|  | #if defined(SUPPORTS_UNWIND_TEST) | 
|  | // We test unwind metadata by running the function under test with the trap flag | 
|  | // set. This results in |SIGTRAP| and |EXCEPTION_SINGLE_STEP| on Linux and | 
|  | // Windows, respectively. We hande these and verify libunwind or the Windows | 
|  | // unwind APIs unwind successfully. | 
|  |  | 
|  | // IsAncestorStackFrame returns true if |a_sp| is an ancestor stack frame of | 
|  | // |b_sp|. | 
|  | static bool IsAncestorStackFrame(crypto_word_t a_sp, crypto_word_t b_sp) { | 
|  | #if defined(OPENSSL_X86_64) | 
|  | // The stack grows down, so ancestor stack frames have higher addresses. | 
|  | return a_sp > b_sp; | 
|  | #else | 
|  | #error "unknown architecture" | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // Implement some string formatting utilties. Ideally we would use |snprintf|, | 
|  | // but this is called in a signal handler and |snprintf| is not async-signal- | 
|  | // safe. | 
|  |  | 
|  | #if !defined(OPENSSL_WINDOWS) | 
|  | static std::array<char, DECIMAL_SIZE(crypto_word_t) + 1> WordToDecimal( | 
|  | crypto_word_t v) { | 
|  | std::array<char, DECIMAL_SIZE(crypto_word_t) + 1> ret; | 
|  | size_t len = 0; | 
|  | do { | 
|  | ret[len++] = '0' + v % 10; | 
|  | v /= 10; | 
|  | } while (v != 0); | 
|  | for (size_t i = 0; i < len / 2; i++) { | 
|  | std::swap(ret[i], ret[len - 1 - i]); | 
|  | } | 
|  | ret[len] = '\0'; | 
|  | return ret; | 
|  | } | 
|  | #endif  // !OPENSSL_WINDOWS | 
|  |  | 
|  | static std::array<char, sizeof(crypto_word_t) * 2 + 1> WordToHex( | 
|  | crypto_word_t v) { | 
|  | static const char kHex[] = "0123456789abcdef"; | 
|  | std::array<char, sizeof(crypto_word_t) * 2 + 1> ret; | 
|  | for (size_t i = sizeof(crypto_word_t) - 1; i < sizeof(crypto_word_t); i--) { | 
|  | uint8_t b = v & 0xff; | 
|  | v >>= 8; | 
|  | ret[i * 2] = kHex[b >> 4]; | 
|  | ret[i * 2 + 1] = kHex[b & 0xf]; | 
|  | } | 
|  | ret[sizeof(crypto_word_t) * 2] = '\0'; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void StrCatSignalSafeImpl(bssl::Span<char> out) {} | 
|  |  | 
|  | template <typename... Args> | 
|  | static void StrCatSignalSafeImpl(bssl::Span<char> out, const char *str, | 
|  | Args... args) { | 
|  | OPENSSL_strlcat(out.data(), str, out.size()); | 
|  | StrCatSignalSafeImpl(out, args...); | 
|  | } | 
|  |  | 
|  | template <typename... Args> | 
|  | static void StrCatSignalSafe(bssl::Span<char> out, Args... args) { | 
|  | if (out.empty()) { | 
|  | return; | 
|  | } | 
|  | out[0] = '\0'; | 
|  | StrCatSignalSafeImpl(out, args...); | 
|  | } | 
|  |  | 
|  | template <typename... Args> | 
|  | [[noreturn]] static void FatalError(Args... args) { | 
|  | // We cannot use |snprintf| here because it is not async-signal-safe. | 
|  | char buf[512]; | 
|  | StrCatSignalSafe(buf, args..., "\n"); | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); | 
|  | if (stderr_handle != INVALID_HANDLE_VALUE) { | 
|  | DWORD unused; | 
|  | WriteFile(stderr_handle, buf, strlen(buf), &unused, nullptr); | 
|  | } | 
|  | #else | 
|  | write(STDERR_FILENO, buf, strlen(buf)); | 
|  | #endif | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | class UnwindStatus { | 
|  | public: | 
|  | UnwindStatus() : err_(nullptr) {} | 
|  | explicit UnwindStatus(const char *err) : err_(err) {} | 
|  |  | 
|  | bool ok() const { return err_ == nullptr; } | 
|  | const char *Error() const { return err_; } | 
|  |  | 
|  | private: | 
|  | const char *err_; | 
|  | }; | 
|  |  | 
|  | template<typename T> | 
|  | class UnwindStatusOr { | 
|  | public: | 
|  | UnwindStatusOr(UnwindStatus status) : status_(status) { | 
|  | assert(!status_.ok()); | 
|  | } | 
|  |  | 
|  | UnwindStatusOr(const T &value) : status_(UnwindStatus()), value_(value) {} | 
|  |  | 
|  | bool ok() const { return status_.ok(); } | 
|  | const char *Error() const { return status_.Error(); } | 
|  |  | 
|  | const T &ValueOrDie(const char *msg = "Unexpected error") const { | 
|  | if (!ok()) { | 
|  | FatalError(msg, ": ", Error()); | 
|  | } | 
|  | return value_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | UnwindStatus status_; | 
|  | T value_; | 
|  | }; | 
|  |  | 
|  | // UnwindCursor abstracts between libunwind and Windows unwind APIs. It is | 
|  | // async-signal-safe. | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | class UnwindCursor { | 
|  | public: | 
|  | explicit UnwindCursor(const CONTEXT &ctx) : ctx_(ctx) { | 
|  | starting_ip_ = ctx_.Rip; | 
|  | } | 
|  |  | 
|  | crypto_word_t starting_ip() const { return starting_ip_; } | 
|  |  | 
|  | // Step unwinds the cursor by one frame. On success, it returns whether there | 
|  | // were more frames to unwind. | 
|  | UnwindStatusOr<bool> Step() { | 
|  | bool is_top = is_top_; | 
|  | is_top_ = false; | 
|  |  | 
|  | DWORD64 image_base; | 
|  | RUNTIME_FUNCTION *entry = | 
|  | RtlLookupFunctionEntry(ctx_.Rip, &image_base, nullptr); | 
|  | if (entry == nullptr) { | 
|  | // This is a leaf function. Leaf functions do not touch stack or | 
|  | // callee-saved registers, so they may be unwound by simulating a ret. | 
|  | if (!is_top) { | 
|  | return UnwindStatus("leaf function found below the top frame"); | 
|  | } | 
|  | memcpy(&ctx_.Rip, reinterpret_cast<const void *>(ctx_.Rsp), | 
|  | sizeof(ctx_.Rip)); | 
|  | ctx_.Rsp += 8; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // This is a frame function. Call into the Windows unwinder. | 
|  | void *handler_data; | 
|  | DWORD64 establisher_frame; | 
|  | RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, ctx_.Rip, entry, &ctx_, | 
|  | &handler_data, &establisher_frame, nullptr); | 
|  | return ctx_.Rip != 0; | 
|  | } | 
|  |  | 
|  | // GetIP returns the instruction pointer at the current frame. | 
|  | UnwindStatusOr<crypto_word_t> GetIP() { return ctx_.Rip; } | 
|  |  | 
|  | // GetSP returns the stack pointer at the current frame. | 
|  | UnwindStatusOr<crypto_word_t> GetSP() { return ctx_.Rsp; } | 
|  |  | 
|  | // GetCallerState returns the callee-saved registers at the current frame. | 
|  | UnwindStatusOr<CallerState> GetCallerState() { | 
|  | CallerState state; | 
|  | state.rbx = ctx_.Rbx; | 
|  | state.rbp = ctx_.Rbp; | 
|  | state.rdi = ctx_.Rdi; | 
|  | state.rsi = ctx_.Rsi; | 
|  | state.r12 = ctx_.R12; | 
|  | state.r13 = ctx_.R13; | 
|  | state.r14 = ctx_.R14; | 
|  | state.r15 = ctx_.R15; | 
|  | memcpy(&state.xmm6, &ctx_.Xmm6, sizeof(Reg128)); | 
|  | memcpy(&state.xmm7, &ctx_.Xmm7, sizeof(Reg128)); | 
|  | memcpy(&state.xmm8, &ctx_.Xmm8, sizeof(Reg128)); | 
|  | memcpy(&state.xmm9, &ctx_.Xmm9, sizeof(Reg128)); | 
|  | memcpy(&state.xmm10, &ctx_.Xmm10, sizeof(Reg128)); | 
|  | memcpy(&state.xmm11, &ctx_.Xmm11, sizeof(Reg128)); | 
|  | memcpy(&state.xmm12, &ctx_.Xmm12, sizeof(Reg128)); | 
|  | memcpy(&state.xmm13, &ctx_.Xmm13, sizeof(Reg128)); | 
|  | memcpy(&state.xmm14, &ctx_.Xmm14, sizeof(Reg128)); | 
|  | memcpy(&state.xmm15, &ctx_.Xmm15, sizeof(Reg128)); | 
|  | return state; | 
|  | } | 
|  |  | 
|  | // ToString returns a human-readable representation of the address the cursor | 
|  | // started at. | 
|  | const char *ToString() { | 
|  | StrCatSignalSafe(starting_ip_buf_, "0x", WordToHex(starting_ip_).data()); | 
|  | return starting_ip_buf_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | CONTEXT ctx_; | 
|  | crypto_word_t starting_ip_; | 
|  | char starting_ip_buf_[64]; | 
|  | bool is_top_ = true; | 
|  | }; | 
|  | #else  // !OPENSSL_WINDOWS | 
|  | class UnwindCursor { | 
|  | public: | 
|  | explicit UnwindCursor(unw_context_t *ctx) : ctx_(ctx) { | 
|  | int ret = InitAtSignalFrame(&cursor_); | 
|  | if (ret < 0) { | 
|  | FatalError("Error getting unwind context: ", unw_strerror(ret)); | 
|  | } | 
|  | starting_ip_ = GetIP().ValueOrDie("Error getting instruction pointer"); | 
|  | } | 
|  |  | 
|  | // Step unwinds the cursor by one frame. On success, it returns whether there | 
|  | // were more frames to unwind. | 
|  | UnwindStatusOr<bool> Step() { | 
|  | int ret = unw_step(&cursor_); | 
|  | if (ret < 0) { | 
|  | return UNWError(ret); | 
|  | } | 
|  | return ret != 0; | 
|  | } | 
|  |  | 
|  | // GetIP returns the instruction pointer at the current frame. | 
|  | UnwindStatusOr<crypto_word_t> GetIP() { | 
|  | crypto_word_t ip; | 
|  | int ret = GetReg(&ip, UNW_REG_IP); | 
|  | if (ret < 0) { | 
|  | return UNWError(ret); | 
|  | } | 
|  | return ip; | 
|  | } | 
|  |  | 
|  | // GetSP returns the stack pointer at the current frame. | 
|  | UnwindStatusOr<crypto_word_t> GetSP() { | 
|  | crypto_word_t sp; | 
|  | int ret = GetReg(&sp, UNW_REG_SP); | 
|  | if (ret < 0) { | 
|  | return UNWError(ret); | 
|  | } | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | // GetCallerState returns the callee-saved registers at the current frame. | 
|  | UnwindStatusOr<CallerState> GetCallerState() { | 
|  | CallerState state; | 
|  | int ret = 0; | 
|  | #if defined(OPENSSL_X86_64) | 
|  | ret = ret < 0 ? ret : GetReg(&state.rbx, UNW_X86_64_RBX); | 
|  | ret = ret < 0 ? ret : GetReg(&state.rbp, UNW_X86_64_RBP); | 
|  | ret = ret < 0 ? ret : GetReg(&state.r12, UNW_X86_64_R12); | 
|  | ret = ret < 0 ? ret : GetReg(&state.r13, UNW_X86_64_R13); | 
|  | ret = ret < 0 ? ret : GetReg(&state.r14, UNW_X86_64_R14); | 
|  | ret = ret < 0 ? ret : GetReg(&state.r15, UNW_X86_64_R15); | 
|  | #else | 
|  | #error "unknown architecture" | 
|  | #endif | 
|  | if (ret < 0) { | 
|  | return UNWError(ret); | 
|  | } | 
|  | return state; | 
|  | } | 
|  |  | 
|  | // ToString returns a human-readable representation of the address the cursor | 
|  | // started at, using debug information if available. | 
|  | const char *ToString() { | 
|  | // Use a new cursor. |cursor_| has already been unwound, and | 
|  | // |unw_get_proc_name| is slow so we do not sample it unconditionally in the | 
|  | // constructor. | 
|  | unw_cursor_t cursor; | 
|  | unw_word_t off; | 
|  | if (InitAtSignalFrame(&cursor) != 0 || | 
|  | unw_get_proc_name(&cursor, starting_ip_buf_, sizeof(starting_ip_buf_), | 
|  | &off) != 0) { | 
|  | StrCatSignalSafe(starting_ip_buf_, "0x", WordToHex(starting_ip_).data()); | 
|  | return starting_ip_buf_; | 
|  | } | 
|  | size_t len = strlen(starting_ip_buf_); | 
|  | // Print the offset in decimal, to match gdb's disassembly output and ease | 
|  | // debugging. | 
|  | StrCatSignalSafe(bssl::Span<char>(starting_ip_buf_).subspan(len), "+", | 
|  | WordToDecimal(off).data(), " (0x", | 
|  | WordToHex(starting_ip_).data(), ")"); | 
|  | return starting_ip_buf_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | static UnwindStatus UNWError(int ret) { | 
|  | assert(ret < 0); | 
|  | const char *msg = unw_strerror(ret); | 
|  | return UnwindStatus(msg == nullptr ? "unknown error" : msg); | 
|  | } | 
|  |  | 
|  | int InitAtSignalFrame(unw_cursor_t *cursor) { | 
|  | // Work around a bug in libunwind which breaks rax and rdx recovery. This | 
|  | // breaks functions which temporarily use rax as the CFA register. See | 
|  | // https://git.savannah.gnu.org/gitweb/?p=libunwind.git;a=commit;h=819bf51bbd2da462c2ec3401e8ac9153b6e725e3 | 
|  | OPENSSL_memset(cursor, 0, sizeof(*cursor)); | 
|  | int ret = unw_init_local(cursor, ctx_); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | for (;;) { | 
|  | ret = unw_is_signal_frame(cursor); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | if (ret != 0) { | 
|  | return 0;  // Found the signal frame. | 
|  | } | 
|  | ret = unw_step(cursor); | 
|  | if (ret < 0) { | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int GetReg(crypto_word_t *out, unw_regnum_t reg) { | 
|  | unw_word_t val; | 
|  | int ret = unw_get_reg(&cursor_, reg, &val); | 
|  | if (ret >= 0) { | 
|  | static_assert(sizeof(crypto_word_t) == sizeof(unw_word_t), | 
|  | "crypto_word_t and unw_word_t are inconsistent"); | 
|  | *out = val; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | unw_context_t *ctx_; | 
|  | unw_cursor_t cursor_; | 
|  | crypto_word_t starting_ip_; | 
|  | char starting_ip_buf_[64]; | 
|  | }; | 
|  | #endif  // OPENSSL_WINDOWS | 
|  |  | 
|  | // g_in_trampoline is true if we are in an instrumented |abi_test_trampoline| | 
|  | // call, in the region that triggers |SIGTRAP|. | 
|  | static bool g_in_trampoline = false; | 
|  | // g_unwind_function_done, if |g_in_trampoline| is true, is whether the function | 
|  | // under test has returned. It is undefined otherwise. | 
|  | static bool g_unwind_function_done; | 
|  | // g_trampoline_state, if |g_in_trampoline| is true, is the state the function | 
|  | // under test must preserve. It is undefined otherwise. | 
|  | static CallerState g_trampoline_state; | 
|  | // g_trampoline_sp, if |g_in_trampoline| is true, is the stack pointer of the | 
|  | // trampoline frame. It is undefined otherwise. | 
|  | static crypto_word_t g_trampoline_sp; | 
|  |  | 
|  | // kMaxUnwindErrors is the maximum number of unwind errors reported per | 
|  | // function. If a function's unwind tables are wrong, we are otherwise likely to | 
|  | // repeat the same error at multiple addresses. | 
|  | static constexpr size_t kMaxUnwindErrors = 10; | 
|  |  | 
|  | // Errors are saved in a signal handler. We use a static buffer to avoid | 
|  | // allocation. | 
|  | static size_t g_num_unwind_errors = 0; | 
|  |  | 
|  | struct UnwindError { | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | crypto_word_t ip; | 
|  | #endif | 
|  | char str[512]; | 
|  | }; | 
|  |  | 
|  | static UnwindError g_unwind_errors[kMaxUnwindErrors]; | 
|  |  | 
|  | template <typename... Args> | 
|  | static void AddUnwindError(UnwindCursor *cursor, Args... args) { | 
|  | if (g_num_unwind_errors >= kMaxUnwindErrors) { | 
|  | return; | 
|  | } | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | // Windows symbol functions should not be called when handling an | 
|  | // exception. Stash the instruction pointer, to be symbolized later. | 
|  | g_unwind_errors[g_num_unwind_errors].ip = cursor->starting_ip(); | 
|  | StrCatSignalSafe(g_unwind_errors[g_num_unwind_errors].str, args...); | 
|  | #else | 
|  | StrCatSignalSafe(g_unwind_errors[g_num_unwind_errors].str, | 
|  | "unwinding at ", cursor->ToString(), ": ", args...); | 
|  | #endif | 
|  | g_num_unwind_errors++; | 
|  | } | 
|  |  | 
|  | static void CheckUnwind(UnwindCursor *cursor) { | 
|  | const crypto_word_t kStartAddress = | 
|  | reinterpret_cast<crypto_word_t>(&abi_test_unwind_start); | 
|  | const crypto_word_t kReturnAddress = | 
|  | reinterpret_cast<crypto_word_t>(&abi_test_unwind_return); | 
|  | const crypto_word_t kStopAddress = | 
|  | reinterpret_cast<crypto_word_t>(&abi_test_unwind_stop); | 
|  |  | 
|  | crypto_word_t sp = cursor->GetSP().ValueOrDie("Error getting stack pointer"); | 
|  | crypto_word_t ip = | 
|  | cursor->GetIP().ValueOrDie("Error getting instruction pointer"); | 
|  | if (!g_in_trampoline) { | 
|  | if (ip != kStartAddress) { | 
|  | FatalError("Unexpected SIGTRAP at ", cursor->ToString()); | 
|  | } | 
|  |  | 
|  | // Save the current state and begin. | 
|  | g_in_trampoline = true; | 
|  | g_unwind_function_done = false; | 
|  | g_trampoline_sp = sp; | 
|  | g_trampoline_state = cursor->GetCallerState().ValueOrDie( | 
|  | "Error getting initial caller state"); | 
|  | } else { | 
|  | if (sp == g_trampoline_sp || g_unwind_function_done) { | 
|  | // |g_unwind_function_done| should imply |sp| is |g_trampoline_sp|, but | 
|  | // clearing the trap flag in x86 briefly displaces the stack pointer. | 
|  | // | 
|  | // Also note we check both |ip| and |sp| below, in case the function under | 
|  | // test is also |abi_test_trampoline|. | 
|  | if (ip == kReturnAddress && sp == g_trampoline_sp) { | 
|  | g_unwind_function_done = true; | 
|  | } | 
|  | if (ip == kStopAddress && sp == g_trampoline_sp) { | 
|  | // |SIGTRAP| is fatal again. | 
|  | g_in_trampoline = false; | 
|  | } | 
|  | } else if (IsAncestorStackFrame(sp, g_trampoline_sp)) { | 
|  | // This should never happen. We went past |g_trampoline_sp| without | 
|  | // stopping at |kStopAddress|. | 
|  | AddUnwindError(cursor, "stack frame is before caller"); | 
|  | g_in_trampoline = false; | 
|  | } else if (g_num_unwind_errors < kMaxUnwindErrors) { | 
|  | for (;;) { | 
|  | UnwindStatusOr<bool> step_ret = cursor->Step(); | 
|  | if (!step_ret.ok()) { | 
|  | AddUnwindError(cursor, "error unwinding: ", step_ret.Error()); | 
|  | break; | 
|  | } | 
|  | // |Step| returns whether there was a frame to unwind. | 
|  | if (!step_ret.ValueOrDie()) { | 
|  | AddUnwindError(cursor, "could not unwind to starting frame"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | UnwindStatusOr<crypto_word_t> cur_sp = cursor->GetSP(); | 
|  | if (!cur_sp.ok()) { | 
|  | AddUnwindError(cursor, | 
|  | "error recovering stack pointer: ", cur_sp.Error()); | 
|  | break; | 
|  | } | 
|  | if (IsAncestorStackFrame(cur_sp.ValueOrDie(), g_trampoline_sp)) { | 
|  | AddUnwindError(cursor, "unwound past starting frame"); | 
|  | break; | 
|  | } | 
|  | if (cur_sp.ValueOrDie() == g_trampoline_sp) { | 
|  | // We found the parent frame. Check the return address. | 
|  | UnwindStatusOr<crypto_word_t> cur_ip = cursor->GetIP(); | 
|  | if (!cur_ip.ok()) { | 
|  | AddUnwindError(cursor, | 
|  | "error recovering return address: ", cur_ip.Error()); | 
|  | } else if (cur_ip.ValueOrDie() != kReturnAddress) { | 
|  | AddUnwindError(cursor, "wrong return address"); | 
|  | } | 
|  |  | 
|  | // Check the remaining registers. | 
|  | UnwindStatusOr<CallerState> state = cursor->GetCallerState(); | 
|  | if (!state.ok()) { | 
|  | AddUnwindError(cursor, | 
|  | "error recovering registers: ", state.Error()); | 
|  | } else { | 
|  | ForEachMismatch(state.ValueOrDie(), g_trampoline_state, | 
|  | [&](const char *reg) { | 
|  | AddUnwindError(cursor, reg, " was not recovered"); | 
|  | }); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void ReadUnwindResult(Result *out) { | 
|  | for (size_t i = 0; i < g_num_unwind_errors; i++) { | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | const crypto_word_t ip = g_unwind_errors[i].ip; | 
|  | char buf[256]; | 
|  | DWORD64 displacement; | 
|  | struct { | 
|  | SYMBOL_INFO info; | 
|  | char name_buf[128]; | 
|  | } symbol; | 
|  | memset(&symbol, 0, sizeof(symbol)); | 
|  | symbol.info.SizeOfStruct = sizeof(symbol.info); | 
|  | symbol.info.MaxNameLen = sizeof(symbol.name_buf); | 
|  | if (SymFromAddr(GetCurrentProcess(), ip, &displacement, &symbol.info)) { | 
|  | snprintf(buf, sizeof(buf), "unwinding at %s+%llu (0x%s): %s", | 
|  | symbol.info.Name, displacement, WordToHex(ip).data(), | 
|  | g_unwind_errors[i].str); | 
|  | } else { | 
|  | snprintf(buf, sizeof(buf), "unwinding at 0x%s: %s", | 
|  | WordToHex(ip).data(), g_unwind_errors[i].str); | 
|  | } | 
|  | out->errors.emplace_back(buf); | 
|  | #else | 
|  | out->errors.emplace_back(g_unwind_errors[i].str); | 
|  | #endif | 
|  | } | 
|  | if (g_num_unwind_errors == kMaxUnwindErrors) { | 
|  | out->errors.emplace_back("(additional errors omitted)"); | 
|  | } | 
|  | g_num_unwind_errors = 0; | 
|  | } | 
|  |  | 
|  | #if defined(OPENSSL_WINDOWS) | 
|  | static DWORD g_main_thread; | 
|  |  | 
|  | static long ExceptionHandler(EXCEPTION_POINTERS *info) { | 
|  | if (info->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP || | 
|  | GetCurrentThreadId() != g_main_thread) { | 
|  | return EXCEPTION_CONTINUE_SEARCH; | 
|  | } | 
|  |  | 
|  | UnwindCursor cursor(*info->ContextRecord); | 
|  | CheckUnwind(&cursor); | 
|  | if (g_in_trampoline) { | 
|  | // Windows clears the trap flag, so we must restore it. | 
|  | info->ContextRecord->EFlags |= 0x100; | 
|  | } | 
|  | return EXCEPTION_CONTINUE_EXECUTION; | 
|  | } | 
|  |  | 
|  | static void EnableUnwindTestsImpl() { | 
|  | if (IsDebuggerPresent()) { | 
|  | // Unwind tests drive logic via |EXCEPTION_SINGLE_STEP|, which conflicts with | 
|  | // debuggers. | 
|  | fprintf(stderr, "Debugger detected. Disabling unwind tests.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_main_thread = GetCurrentThreadId(); | 
|  |  | 
|  | SymSetOptions(SYMOPT_DEFERRED_LOADS); | 
|  | if (!SymInitialize(GetCurrentProcess(), nullptr, TRUE)) { | 
|  | fprintf(stderr, "Could not initialize symbols.\n"); | 
|  | } | 
|  |  | 
|  | if (AddVectoredExceptionHandler(0, ExceptionHandler) == nullptr) { | 
|  | fprintf(stderr, "Error installing exception handler.\n"); | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | g_unwind_tests_enabled = true; | 
|  | } | 
|  | #else  // !OPENSSL_WINDOWS | 
|  | // HandleEINTR runs |func| and returns the result, retrying the operation on | 
|  | // |EINTR|. | 
|  | template <typename Func> | 
|  | static auto HandleEINTR(const Func &func) -> decltype(func()) { | 
|  | decltype(func()) ret; | 
|  | do { | 
|  | ret = func(); | 
|  | } while (ret < 0 && errno == EINTR); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static bool ReadFileToString(std::string *out, const char *path) { | 
|  | out->clear(); | 
|  |  | 
|  | int fd = HandleEINTR([&] { return open(path, O_RDONLY); }); | 
|  | if (fd < 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (;;) { | 
|  | char buf[1024]; | 
|  | ssize_t ret = HandleEINTR([&] { return read(fd, buf, sizeof(buf)); }); | 
|  | if (ret < 0) { | 
|  | close(fd); | 
|  | return false; | 
|  | } | 
|  | if (ret == 0) { | 
|  | close(fd); | 
|  | return true; | 
|  | } | 
|  | out->append(buf, static_cast<size_t>(ret)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool IsBeingDebugged() { | 
|  | std::string status; | 
|  | if (!ReadFileToString(&status, "/proc/self/status")) { | 
|  | perror("error reading /proc/self/status"); | 
|  | return false; | 
|  | } | 
|  | std::string key = "\nTracerPid:\t"; | 
|  | size_t idx = status.find(key); | 
|  | if (idx == std::string::npos) { | 
|  | return false; | 
|  | } | 
|  | idx += key.size(); | 
|  | return idx < status.size() && status[idx] != '0'; | 
|  | } | 
|  |  | 
|  | static pthread_t g_main_thread; | 
|  |  | 
|  | static void TrapHandler(int sig) { | 
|  | // Note this is a signal handler, so only async-signal-safe functions may be | 
|  | // used here. See signal-safety(7). libunwind promises local unwind is | 
|  | // async-signal-safe. | 
|  |  | 
|  | // |pthread_equal| is not listed as async-signal-safe, but this is clearly an | 
|  | // oversight. | 
|  | if (!pthread_equal(g_main_thread, pthread_self())) { | 
|  | FatalError("SIGTRAP on background thread"); | 
|  | } | 
|  |  | 
|  | unw_context_t ctx; | 
|  | int ret = unw_getcontext(&ctx); | 
|  | if (ret < 0) { | 
|  | FatalError("Error getting unwind context: ", unw_strerror(ret)); | 
|  | } | 
|  |  | 
|  | UnwindCursor cursor(&ctx); | 
|  | CheckUnwind(&cursor); | 
|  | } | 
|  |  | 
|  | static void EnableUnwindTestsImpl() { | 
|  | if (IsBeingDebugged()) { | 
|  | // Unwind tests drive logic via |SIGTRAP|, which conflicts with debuggers. | 
|  | fprintf(stderr, "Debugger detected. Disabling unwind tests.\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | g_main_thread = pthread_self(); | 
|  |  | 
|  | struct sigaction trap_action; | 
|  | OPENSSL_memset(&trap_action, 0, sizeof(trap_action)); | 
|  | sigemptyset(&trap_action.sa_mask); | 
|  | trap_action.sa_handler = TrapHandler; | 
|  | if (sigaction(SIGTRAP, &trap_action, NULL) != 0) { | 
|  | perror("sigaction"); | 
|  | abort(); | 
|  | } | 
|  |  | 
|  | g_unwind_tests_enabled = true; | 
|  | } | 
|  | #endif  // OPENSSL_WINDOWS | 
|  |  | 
|  | #else  // !SUPPORTS_UNWIND_TEST | 
|  |  | 
|  | #if defined(SUPPORTS_ABI_TEST) | 
|  | static void ReadUnwindResult(Result *) {} | 
|  | #endif | 
|  | static void EnableUnwindTestsImpl() {} | 
|  |  | 
|  | #endif  // SUPPORTS_UNWIND_TEST | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | void EnableUnwindTests() { internal::EnableUnwindTestsImpl(); } | 
|  |  | 
|  | bool UnwindTestsEnabled() { return internal::g_unwind_tests_enabled; } | 
|  |  | 
|  | }  // namespace abi_test |