| /* 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 | 
 | } | 
 | #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 | 
 |   ssize_t ret = write(STDERR_FILENO, buf, strlen(buf)); | 
 |   // We'll abort soon anyway, so if we fail to write the message, there's | 
 |   // nothing to do. | 
 |   (void)ret; | 
 | #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 = unw_init_local2(&cursor_, ctx_, UNW_INIT_SIGNAL_FRAME); | 
 |     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 (unw_init_local2(&cursor, ctx_, UNW_INIT_SIGNAL_FRAME) != 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 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, during an unwind-enabled ABI test, 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; | 
 |   } 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; | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // ReadUnwindResult adds the results of the most recent unwind test to |out|. | 
 | 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, siginfo_t *info, void *ucontext_v) { | 
 |   // 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. | 
 |   ucontext_t *ucontext = static_cast<ucontext_t*>(ucontext_v); | 
 |  | 
 |   // |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"); | 
 |   } | 
 |  | 
 |   UnwindCursor cursor(ucontext); | 
 |   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_flags = SA_SIGINFO; | 
 |   trap_action.sa_sigaction = 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 | 
 |  | 
 | #if defined(SUPPORTS_ABI_TEST) | 
 | 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; | 
 | #if defined(SUPPORTS_UNWIND_TEST) | 
 |   if (unwind) { | 
 |     // Save the caller state for the unwind tester to check for. | 
 |     g_trampoline_state = state; | 
 |   } | 
 | #endif | 
 |   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 | 
 |  | 
 | }  // namespace internal | 
 |  | 
 | void EnableUnwindTests() { internal::EnableUnwindTestsImpl(); } | 
 |  | 
 | bool UnwindTestsEnabled() { return internal::g_unwind_tests_enabled; } | 
 |  | 
 | }  // namespace abi_test |