1351282Sdim#include "sanitizer_common/sanitizer_asm.h"
2351282Sdim
3351282Sdim// The content of this file is AArch64-only:
4351282Sdim#if defined(__aarch64__)
5351282Sdim
6351282Sdim// The responsibility of the HWASan entry point in compiler-rt is to primarily
7351282Sdim// readjust the stack from the callee and save the current register values to
8351282Sdim// the stack.
9351282Sdim// This entry point function should be called from a __hwasan_check_* symbol.
10351282Sdim// These are generated during a lowering pass in the backend, and are found in
11351282Sdim// AArch64AsmPrinter::EmitHwasanMemaccessSymbols(). Please look there for
12351282Sdim// further information.
13351282Sdim// The __hwasan_check_* caller of this function should have expanded the stack
14351282Sdim// and saved the previous values of x0, x1, x29, and x30. This function will
15351282Sdim// "consume" these saved values and treats it as part of its own stack frame.
16351282Sdim// In this sense, the __hwasan_check_* callee and this function "share" a stack
17351282Sdim// frame. This allows us to omit having unwinding information (.cfi_*) present
18351282Sdim// in every __hwasan_check_* function, therefore reducing binary size. This is
19351282Sdim// particularly important as hwasan_check_* instances are duplicated in every
20351282Sdim// translation unit where HWASan is enabled.
21351282Sdim// This function calls HwasanTagMismatch to step back into the C++ code that
22351282Sdim// completes the stack unwinding and error printing. This function is is not
23351282Sdim// permitted to return.
24351282Sdim
25351282Sdim
26351282Sdim// Frame from __hwasan_check_:
27351282Sdim// |              ...                |
28351282Sdim// |              ...                |
29351282Sdim// | Previous stack frames...        |
30351282Sdim// +=================================+
31351282Sdim// | Unused 8-bytes for maintaining  |
32351282Sdim// | 16-byte SP alignment.           |
33351282Sdim// +---------------------------------+
34351282Sdim// | Return address (x30) for caller |
35351282Sdim// | of __hwasan_check_*.            |
36351282Sdim// +---------------------------------+
37351282Sdim// | Frame address (x29) for caller  |
38351282Sdim// | of __hwasan_check_*             |
39351282Sdim// +---------------------------------+ <-- [SP + 232]
40351282Sdim// |              ...                |
41351282Sdim// |                                 |
42351282Sdim// | Stack frame space for x2 - x28. |
43351282Sdim// |                                 |
44351282Sdim// |              ...                |
45351282Sdim// +---------------------------------+ <-- [SP + 16]
46351282Sdim// |                                 |
47351282Sdim// | Saved x1, as __hwasan_check_*   |
48351282Sdim// | clobbers it.                    |
49351282Sdim// +---------------------------------+
50351282Sdim// | Saved x0, likewise above.       |
51351282Sdim// +---------------------------------+ <-- [x30 / SP]
52351282Sdim
53351282Sdim// This function takes two arguments:
54360784Sdim//   * x0: The data address.
55360784Sdim//   * x1: The encoded access info for the failing access.
56351282Sdim
57360784Sdim// This function has two entry points. The first, __hwasan_tag_mismatch, is used
58360784Sdim// by clients that were compiled without short tag checks (i.e. binaries built
59360784Sdim// by older compilers and binaries targeting older runtimes). In this case the
60360784Sdim// outlined tag check will be missing the code handling short tags (which won't
61360784Sdim// be used in the binary's own stack variables but may be used on the heap
62360784Sdim// or stack variables in other binaries), so the check needs to be done here.
63360784Sdim//
64360784Sdim// The second, __hwasan_tag_mismatch_v2, is used by binaries targeting newer
65360784Sdim// runtimes. This entry point bypasses the short tag check since it will have
66360784Sdim// already been done as part of the outlined tag check. Since tag mismatches are
67360784Sdim// uncommon, there isn't a significant performance benefit to being able to
68360784Sdim// bypass the check; the main benefits are that we can sometimes avoid
69360784Sdim// clobbering the x17 register in error reports, and that the program will have
70360784Sdim// a runtime dependency on the __hwasan_tag_mismatch_v2 symbol therefore it will
71360784Sdim// fail to start up given an older (i.e. incompatible) runtime.
72351282Sdim.section .text
73351282Sdim.file "hwasan_tag_mismatch_aarch64.S"
74351282Sdim.global __hwasan_tag_mismatch
75351282Sdim.type __hwasan_tag_mismatch, %function
76351282Sdim__hwasan_tag_mismatch:
77360784Sdim  // Compute the granule position one past the end of the access.
78360784Sdim  mov x16, #1
79360784Sdim  and x17, x1, #0xf
80360784Sdim  lsl x16, x16, x17
81360784Sdim  and x17, x0, #0xf
82360784Sdim  add x17, x16, x17
83360784Sdim
84360784Sdim  // Load the shadow byte again and check whether it is a short tag within the
85360784Sdim  // range of the granule position computed above.
86360784Sdim  ubfx x16, x0, #4, #52
87360784Sdim  ldrb w16, [x9, x16]
88360784Sdim  cmp w16, #0xf
89360784Sdim  b.hi __hwasan_tag_mismatch_v2
90360784Sdim  cmp w16, w17
91360784Sdim  b.lo __hwasan_tag_mismatch_v2
92360784Sdim
93360784Sdim  // Load the real tag from the last byte of the granule and compare against
94360784Sdim  // the pointer tag.
95360784Sdim  orr x16, x0, #0xf
96360784Sdim  ldrb w16, [x16]
97360784Sdim  cmp x16, x0, lsr #56
98360784Sdim  b.ne __hwasan_tag_mismatch_v2
99360784Sdim
100360784Sdim  // Restore x0, x1 and sp to their values from before the __hwasan_tag_mismatch
101360784Sdim  // call and resume execution.
102360784Sdim  ldp x0, x1, [sp], #256
103360784Sdim  ret
104360784Sdim
105360784Sdim.global __hwasan_tag_mismatch_v2
106360784Sdim.type __hwasan_tag_mismatch_v2, %function
107360784Sdim__hwasan_tag_mismatch_v2:
108351282Sdim  CFI_STARTPROC
109351282Sdim
110351282Sdim  // Set the CFA to be the return address for caller of __hwasan_check_*. Note
111351282Sdim  // that we do not emit CFI predicates to describe the contents of this stack
112351282Sdim  // frame, as this proxy entry point should never be debugged. The contents
113351282Sdim  // are static and are handled by the unwinder after calling
114351282Sdim  // __hwasan_tag_mismatch. The frame pointer is already correctly setup
115351282Sdim  // by __hwasan_check_*.
116351282Sdim  add x29, sp, #232
117351282Sdim  CFI_DEF_CFA(w29, 24)
118351282Sdim  CFI_OFFSET(w30, -16)
119351282Sdim  CFI_OFFSET(w29, -24)
120351282Sdim
121351282Sdim  // Save the rest of the registers into the preallocated space left by
122351282Sdim  // __hwasan_check.
123351282Sdim  str     x28,      [sp, #224]
124351282Sdim  stp     x26, x27, [sp, #208]
125351282Sdim  stp     x24, x25, [sp, #192]
126351282Sdim  stp     x22, x23, [sp, #176]
127351282Sdim  stp     x20, x21, [sp, #160]
128351282Sdim  stp     x18, x19, [sp, #144]
129351282Sdim  stp     x16, x17, [sp, #128]
130351282Sdim  stp     x14, x15, [sp, #112]
131351282Sdim  stp     x12, x13, [sp, #96]
132351282Sdim  stp     x10, x11, [sp, #80]
133351282Sdim  stp     x8,  x9,  [sp, #64]
134351282Sdim  stp     x6,  x7,  [sp, #48]
135351282Sdim  stp     x4,  x5,  [sp, #32]
136351282Sdim  stp     x2,  x3,  [sp, #16]
137351282Sdim
138360784Sdim  // Pass the address of the frame to __hwasan_tag_mismatch4, so that it can
139351282Sdim  // extract the saved registers from this frame without having to worry about
140351282Sdim  // finding this frame.
141351282Sdim  mov x2, sp
142351282Sdim
143360784Sdim  bl __hwasan_tag_mismatch4
144351282Sdim  CFI_ENDPROC
145351282Sdim
146351282Sdim.Lfunc_end0:
147351282Sdim  .size __hwasan_tag_mismatch, .Lfunc_end0-__hwasan_tag_mismatch
148351282Sdim
149351282Sdim#endif  // defined(__aarch64__)
150351282Sdim
151351282Sdim// We do not need executable stack.
152351282SdimNO_EXEC_STACK_DIRECTIVE
153