Commit 1a3b3301 authored by Matt Morehouse's avatar Matt Morehouse
Browse files

[HWASan] Catch cases where libc populated jmp_buf.

Some setjmp calls within libc cannot be intercepted while their matching
longjmp calls can be.  This causes problems if our setjmp/longjmp
interceptors don't use the exact same format as libc for populating and
reading the jmp_buf.

We add a magic field to our jmp_buf and populate it in setjmp.  This
allows our longjmp interceptor to notice when a libc jmp_buf is passed
to it.

See discussion on https://reviews.llvm.org/D109699 and
https://reviews.llvm.org/D69045.

Fixes https://github.com/google/sanitizers/issues/1244.

Reviewed By: eugenis

Differential Revision: https://reviews.llvm.org/D109787
parent 1f3925e2
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -199,12 +199,17 @@ struct __hw_jmp_buf_struct {
  // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that
  // `__mask_was_saved' follows it.  Do not move these members or add others
  // before it.
  //
  // We add a __magic field to our struct to catch cases where libc's setjmp
  // populated the jmp_buf instead of our interceptor.
  __hw_register_buf __jmpbuf; // Calling environment.
  int __mask_was_saved;       // Saved the signal mask?
  unsigned __mask_was_saved : 1;  // Saved the signal mask?
  unsigned __magic : 31;      // Used to distinguish __hw_jmp_buf from jmp_buf.
  __hw_sigset_t __saved_mask; // Saved signal mask.
};
typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1];
typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1];
constexpr unsigned kHwJmpBufMagic = 0x248ACE77;
#endif // HWASAN_WITH_INTERCEPTORS && __aarch64__

#define ENSURE_HWASAN_INITED()      \
+21 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
#define SIG_BLOCK 0
#define SIG_SETMASK 2
extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
  env[0].__magic = kHwJmpBufMagic;
  env[0].__mask_was_saved =
      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
                               &env[0].__saved_mask) == 0);
@@ -103,6 +104,13 @@ InternalLongjmp(__hw_register_buf env, int retval) {
}

INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
  if (env[0].__magic != kHwJmpBufMagic) {
    Printf(
        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
        "there is a bug in HWASan.");
    return REAL(siglongjmp)(env, val);
  }

  if (env[0].__mask_was_saved)
    // Restore the saved signal mask.
    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
@@ -114,10 +122,18 @@ INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
// _setjmp on start_thread.  Hence we have to intercept the longjmp on
// pthread_exit so the __hw_jmp_buf order matches.
INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
  if (env[0].__magic != kHwJmpBufMagic)
    return REAL(__libc_longjmp)(env, val);
  InternalLongjmp(env[0].__jmpbuf, val);
}

INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
  if (env[0].__magic != kHwJmpBufMagic) {
    Printf(
        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
        "there is a bug in HWASan.");
    return REAL(longjmp)(env, val);
  }
  InternalLongjmp(env[0].__jmpbuf, val);
}
#undef SIG_BLOCK
@@ -142,6 +158,11 @@ void InitializeInterceptors() {

#if HWASAN_WITH_INTERCEPTORS
#if defined(__linux__)
#      if defined(__aarch64__)
  INTERCEPT_FUNCTION(__libc_longjmp);
  INTERCEPT_FUNCTION(longjmp);
  INTERCEPT_FUNCTION(siglongjmp);
#      endif  // __aarch64__
  INTERCEPT_FUNCTION(vfork);
#endif  // __linux__
  INTERCEPT_FUNCTION(pthread_create);