aboutsummaryrefslogtreecommitdiff
path: root/libsanitizer/lsan/lsan_common_fuchsia.cpp
blob: cb3fe1f859f798d4623baac7d3b29e60436c7f9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//=-- lsan_common_fuchsia.cpp --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
// This file is a part of LeakSanitizer.
// Implementation of common leak checking functionality. Fuchsia-specific code.
//
//===---------------------------------------------------------------------===//

#include "lsan_common.h"
#include "lsan_thread.h"
#include "sanitizer_common/sanitizer_platform.h"

#if CAN_SANITIZE_LEAKS && SANITIZER_FUCHSIA
#include <zircon/sanitizer.h>

#include "lsan_allocator.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stoptheworld_fuchsia.h"
#include "sanitizer_common/sanitizer_thread_registry.h"

// Ensure that the Zircon system ABI is linked in.
#pragma comment(lib, "zircon")

namespace __lsan {

void InitializePlatformSpecificModules() {}

LoadedModule *GetLinker() { return nullptr; }

__attribute__((tls_model("initial-exec"))) THREADLOCAL int disable_counter;
bool DisabledInThisThread() { return disable_counter > 0; }
void DisableInThisThread() { disable_counter++; }
void EnableInThisThread() {
  if (disable_counter == 0) {
    DisableCounterUnderflow();
  }
  disable_counter--;
}

// There is nothing left to do after the globals callbacks.
void ProcessGlobalRegions(Frontier *frontier) {}

// Nothing to do here.
void ProcessPlatformSpecificAllocations(Frontier *frontier) {}

// On Fuchsia, we can intercept _Exit gracefully, and return a failing exit
// code if required at that point.  Calling Die() here is undefined
// behavior and causes rare race conditions.
void HandleLeaks() {}

// This is defined differently in asan_fuchsia.cpp and lsan_fuchsia.cpp.
bool UseExitcodeOnLeak();

int ExitHook(int status) {
  if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
    if (UseExitcodeOnLeak())
      DoLeakCheck();
    else
      DoRecoverableLeakCheckVoid();
  }
  return status == 0 && HasReportedLeaks() ? common_flags()->exitcode : status;
}

void LockStuffAndStopTheWorld(StopTheWorldCallback callback,
                              CheckForLeaksParam *argument) {
  ScopedStopTheWorldLock lock;

  struct Params {
    InternalMmapVector<uptr> allocator_caches;
    StopTheWorldCallback callback;
    CheckForLeaksParam *argument;
  } params = {{}, callback, argument};

  // Callback from libc for globals (data/bss modulo relro), when enabled.
  auto globals = +[](void *chunk, size_t size, void *data) {
    auto params = static_cast<const Params *>(data);
    uptr begin = reinterpret_cast<uptr>(chunk);
    uptr end = begin + size;
    ScanGlobalRange(begin, end, &params->argument->frontier);
  };

  // Callback from libc for thread stacks.
  auto stacks = +[](void *chunk, size_t size, void *data) {
    auto params = static_cast<const Params *>(data);
    uptr begin = reinterpret_cast<uptr>(chunk);
    uptr end = begin + size;
    ScanRangeForPointers(begin, end, &params->argument->frontier, "STACK",
                         kReachable);
  };

  // Callback from libc for thread registers.
  auto registers = +[](void *chunk, size_t size, void *data) {
    auto params = static_cast<const Params *>(data);
    uptr begin = reinterpret_cast<uptr>(chunk);
    uptr end = begin + size;
    ScanRangeForPointers(begin, end, &params->argument->frontier, "REGISTERS",
                         kReachable);
  };

  if (flags()->use_tls) {
    // Collect the allocator cache range from each thread so these
    // can all be excluded from the reported TLS ranges.
    GetAllThreadAllocatorCachesLocked(&params.allocator_caches);
    __sanitizer::Sort(params.allocator_caches.data(),
                      params.allocator_caches.size());
  }

  // Callback from libc for TLS regions.  This includes thread_local
  // variables as well as C11 tss_set and POSIX pthread_setspecific.
  auto tls = +[](void *chunk, size_t size, void *data) {
    auto params = static_cast<const Params *>(data);
    uptr begin = reinterpret_cast<uptr>(chunk);
    uptr end = begin + size;
    auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin);
    if (i < params->allocator_caches.size() &&
        params->allocator_caches[i] >= begin &&
        params->allocator_caches[i] <= end &&
        end - params->allocator_caches[i] >= sizeof(AllocatorCache)) {
      // Split the range in two and omit the allocator cache within.
      ScanRangeForPointers(begin, params->allocator_caches[i],
                           &params->argument->frontier, "TLS", kReachable);
      uptr begin2 = params->allocator_caches[i] + sizeof(AllocatorCache);
      ScanRangeForPointers(begin2, end, &params->argument->frontier, "TLS",
                           kReachable);
    } else {
      ScanRangeForPointers(begin, end, &params->argument->frontier, "TLS",
                           kReachable);
    }
  };

  // This stops the world and then makes callbacks for various memory regions.
  // The final callback is the last thing before the world starts up again.
  __sanitizer_memory_snapshot(
      flags()->use_globals ? globals : nullptr,
      flags()->use_stacks ? stacks : nullptr,
      flags()->use_registers ? registers : nullptr,
      flags()->use_tls ? tls : nullptr,
      [](zx_status_t, void *data) {
        auto params = static_cast<const Params *>(data);

        // We don't use the thread registry at all for enumerating the threads
        // and their stacks, registers, and TLS regions.  So use it separately
        // just for the allocator cache, and to call ScanExtraStackRanges,
        // which ASan needs.
        if (flags()->use_stacks) {
          InternalMmapVector<Range> ranges;
          GetThreadExtraStackRangesLocked(&ranges);
          ScanExtraStackRanges(ranges, &params->argument->frontier);
        }
        params->callback(SuspendedThreadsListFuchsia(), params->argument);
      },
      &params);
}

}  // namespace __lsan

// This is declared (in extern "C") by <zircon/sanitizer.h>.
// _Exit calls this directly to intercept and change the status value.
int __sanitizer_process_exit_hook(int status) {
  return __lsan::ExitHook(status);
}

#endif