aboutsummaryrefslogtreecommitdiff
path: root/lib/asan/asan_globals.cc
blob: f53bf38db4c2f495e8eff1be1a6c2febb9856a3e (plain) (blame)
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
169
170
171
//===-- asan_globals.cc -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Handle globals.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"

#include <ctype.h>

namespace __asan {

typedef __asan_global Global;

struct ListOfGlobals {
  const Global *g;
  ListOfGlobals *next;
};

static AsanLock mu_for_globals(LINKER_INITIALIZED);
static ListOfGlobals *list_of_globals;
static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);

void PoisonRedZones(const Global &g)  {
  size_t shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
  CHECK(shadow_rz_size == 1 || shadow_rz_size == 2 || shadow_rz_size == 4);
  // full right redzone
  size_t g_aligned_size = kGlobalAndStackRedzone *
      ((g.size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone);
  PoisonShadow(g.beg + g_aligned_size,
               kGlobalAndStackRedzone, kAsanGlobalRedzoneMagic);
  if ((g.size % kGlobalAndStackRedzone) != 0) {
    // partial right redzone
    uint64_t g_aligned_down_size = kGlobalAndStackRedzone *
        (g.size / kGlobalAndStackRedzone);
    CHECK(g_aligned_down_size == g_aligned_size - kGlobalAndStackRedzone);
    PoisonShadowPartialRightRedzone(g.beg + g_aligned_down_size,
                                    g.size % kGlobalAndStackRedzone,
                                    kGlobalAndStackRedzone,
                                    kAsanGlobalRedzoneMagic);
  }
}

static size_t GetAlignedSize(size_t size) {
  return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
      * kGlobalAndStackRedzone;
}

  // Check if the global is a zero-terminated ASCII string. If so, print it.
void PrintIfASCII(const Global &g) {
  for (size_t p = g.beg; p < g.beg + g.size - 1; p++) {
    if (!isascii(*(char*)p)) return;
  }
  if (*(char*)(g.beg + g.size - 1) != 0) return;
  Printf("  '%s' is ascii string '%s'\n", g.name, g.beg);
}

bool DescribeAddrIfMyRedZone(const Global &g, uintptr_t addr) {
  if (addr < g.beg - kGlobalAndStackRedzone) return false;
  if (addr >= g.beg + g.size_with_redzone) return false;
  Printf("%p is located ", addr);
  if (addr < g.beg) {
    Printf("%d bytes to the left", g.beg - addr);
  } else if (addr >= g.beg + g.size) {
    Printf("%d bytes to the right", addr - (g.beg + g.size));
  } else {
    Printf("%d bytes inside", addr - g.beg);  // Can it happen?
  }
  Printf(" of global variable '%s' (0x%lx) of size %ld\n",
         g.name, g.beg, g.size);
  PrintIfASCII(g);
  return true;
}


bool DescribeAddrIfGlobal(uintptr_t addr) {
  if (!FLAG_report_globals) return false;
  ScopedLock lock(&mu_for_globals);
  bool res = false;
  for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
    const Global &g = *l->g;
    if (FLAG_report_globals >= 2)
      Printf("Search Global: beg=%p size=%ld name=%s\n",
             g.beg, g.size, g.name);
    res |= DescribeAddrIfMyRedZone(g, addr);
  }
  return res;
}

// Register a global variable.
// This function may be called more than once for every global
// so we store the globals in a map.
static void RegisterGlobal(const Global *g) {
  CHECK(asan_inited);
  CHECK(FLAG_report_globals);
  CHECK(AddrIsInMem(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
  PoisonRedZones(*g);
  ListOfGlobals *l =
      (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
  l->g = g;
  l->next = list_of_globals;
  list_of_globals = l;
  if (FLAG_report_globals >= 2)
    Report("Added Global: beg=%p size=%ld name=%s\n",
           g->beg, g->size, g->name);
}

static void UnregisterGlobal(const Global *g) {
  CHECK(asan_inited);
  CHECK(FLAG_report_globals);
  CHECK(AddrIsInMem(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->beg));
  CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
  PoisonShadow(g->beg, g->size_with_redzone, 0);
  // We unpoison the shadow memory for the global but we do not remove it from
  // the list because that would require O(n^2) time with the current list
  // implementation. It might not be worth doing anyway.
}

}  // namespace __asan

// ---------------------- Interface ---------------- {{{1
using namespace __asan;  // NOLINT

// Register one global with a default redzone.
void __asan_register_global(uintptr_t addr, size_t size,
                            const char *name) {
  if (!FLAG_report_globals) return;
  ScopedLock lock(&mu_for_globals);
  Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
  g->beg = addr;
  g->size = size;
  g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
  g->name = name;
  RegisterGlobal(g);
}

// Register an array of globals.
void __asan_register_globals(__asan_global *globals, size_t n) {
  if (!FLAG_report_globals) return;
  ScopedLock lock(&mu_for_globals);
  for (size_t i = 0; i < n; i++) {
    RegisterGlobal(&globals[i]);
  }
}

// Unregister an array of globals.
// We must do it when a shared objects gets dlclosed.
void __asan_unregister_globals(__asan_global *globals, size_t n) {
  if (!FLAG_report_globals) return;
  ScopedLock lock(&mu_for_globals);
  for (size_t i = 0; i < n; i++) {
    UnregisterGlobal(&globals[i]);
  }
}