aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hwasan/hwasan_tag_mismatch_aarch64.S
blob: 4c060a61e98ed6f0a407cec04dc7f581bb7fdb46 (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
#include "sanitizer_common/sanitizer_asm.h"

// The content of this file is AArch64-only:
#if defined(__aarch64__)

// The responsibility of the HWASan entry point in compiler-rt is to primarily
// readjust the stack from the callee and save the current register values to
// the stack.
// This entry point function should be called from a __hwasan_check_* symbol.
// These are generated during a lowering pass in the backend, and are found in
// AArch64AsmPrinter::EmitHwasanMemaccessSymbols(). Please look there for
// further information.
// The __hwasan_check_* caller of this function should have expanded the stack
// and saved the previous values of x0, x1, x29, and x30. This function will
// "consume" these saved values and treats it as part of its own stack frame.
// In this sense, the __hwasan_check_* callee and this function "share" a stack
// frame. This allows us to omit having unwinding information (.cfi_*) present
// in every __hwasan_check_* function, therefore reducing binary size. This is
// particularly important as hwasan_check_* instances are duplicated in every
// translation unit where HWASan is enabled.
// This function calls HwasanTagMismatch to step back into the C++ code that
// completes the stack unwinding and error printing. This function is is not
// permitted to return.


// Frame from __hwasan_check_:
// |              ...                |
// |              ...                |
// | Previous stack frames...        |
// +=================================+
// | Unused 8-bytes for maintaining  |
// | 16-byte SP alignment.           |
// +---------------------------------+
// | Return address (x30) for caller |
// | of __hwasan_check_*.            |
// +---------------------------------+
// | Frame address (x29) for caller  |
// | of __hwasan_check_*             |
// +---------------------------------+ <-- [SP + 232]
// |              ...                |
// |                                 |
// | Stack frame space for x2 - x28. |
// |                                 |
// |              ...                |
// +---------------------------------+ <-- [SP + 16]
// |                                 |
// | Saved x1, as __hwasan_check_*   |
// | clobbers it.                    |
// +---------------------------------+
// | Saved x0, likewise above.       |
// +---------------------------------+ <-- [x30 / SP]

// This function takes two arguments:
//   * x0: The data address.
//   * x1: The encoded access info for the failing access.

// This function has two entry points. The first, __hwasan_tag_mismatch, is used
// by clients that were compiled without short tag checks (i.e. binaries built
// by older compilers and binaries targeting older runtimes). In this case the
// outlined tag check will be missing the code handling short tags (which won't
// be used in the binary's own stack variables but may be used on the heap
// or stack variables in other binaries), so the check needs to be done here.
//
// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer
// runtimes. This entry point bypasses the short tag check since it will have
// already been done as part of the outlined tag check. Since tag mismatches are
// uncommon, there isn't a significant performance benefit to being able to
// bypass the check; the main benefits are that we can sometimes avoid
// clobbering the x17 register in error reports, and that the program will have
// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will
// fail to start up given an older (i.e. incompatible) runtime.
.section .text
.file "hwasan_tag_mismatch_aarch64.S"
.global __hwasan_tag_mismatch
.type __hwasan_tag_mismatch, %function
__hwasan_tag_mismatch:
  // Compute the granule position one past the end of the access.
  mov x16, #1
  and x17, x1, #0xf
  lsl x16, x16, x17
  and x17, x0, #0xf
  add x17, x16, x17

  // Load the shadow byte again and check whether it is a short tag within the
  // range of the granule position computed above.
  ubfx x16, x0, #4, #52
  ldrb w16, [x9, x16]
  cmp w16, #0xf
  b.hi __hwasan_tag_mismatch_v2
  cmp w16, w17
  b.lo __hwasan_tag_mismatch_v2

  // Load the real tag from the last byte of the granule and compare against
  // the pointer tag.
  orr x16, x0, #0xf
  ldrb w16, [x16]
  cmp x16, x0, lsr #56
  b.ne __hwasan_tag_mismatch_v2

  // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch
  // call and resume execution.
  ldp x0, x1, [sp], #256
  ret

.global __hwasan_tag_mismatch_v2
.type __hwasan_tag_mismatch_v2, %function
__hwasan_tag_mismatch_v2:
  CFI_STARTPROC

  // Set the CFA to be the return address for caller of __hwasan_check_*. Note
  // that we do not emit CFI predicates to describe the contents of this stack
  // frame, as this proxy entry point should never be debugged. The contents
  // are static and are handled by the unwinder after calling
  // __hwasan_tag_mismatch. The frame pointer is already correctly setup
  // by __hwasan_check_*.
  add x29, sp, #232
  CFI_DEF_CFA(w29, 24)
  CFI_OFFSET(w30, -16)
  CFI_OFFSET(w29, -24)

  // Save the rest of the registers into the preallocated space left by
  // __hwasan_check.
  str     x28,      [sp, #224]
  stp     x26, x27, [sp, #208]
  stp     x24, x25, [sp, #192]
  stp     x22, x23, [sp, #176]
  stp     x20, x21, [sp, #160]
  stp     x18, x19, [sp, #144]
  stp     x16, x17, [sp, #128]
  stp     x14, x15, [sp, #112]
  stp     x12, x13, [sp, #96]
  stp     x10, x11, [sp, #80]
  stp     x8,  x9,  [sp, #64]
  stp     x6,  x7,  [sp, #48]
  stp     x4,  x5,  [sp, #32]
  stp     x2,  x3,  [sp, #16]

  // Pass the address of the frame to __hwasan_tag_mismatch_stub, so that it can
  // extract the saved registers from this frame without having to worry about
  // finding this frame.
  mov x2, sp

  bl __hwasan_tag_mismatch_stub
  CFI_ENDPROC

.Lfunc_end0:
  .size __hwasan_tag_mismatch, .Lfunc_end0-__hwasan_tag_mismatch

#endif  // defined(__aarch64__)

// We do not need executable stack.
NO_EXEC_STACK_DIRECTIVE