1b47455b5SNico Weber //===-- asan_noinst_test.cpp ----------------------------------------------===// 2b47455b5SNico Weber // 3b47455b5SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4b47455b5SNico Weber // See https://llvm.org/LICENSE.txt for license information. 5b47455b5SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6b47455b5SNico Weber // 7b47455b5SNico Weber //===----------------------------------------------------------------------===// 8b47455b5SNico Weber // 9b47455b5SNico Weber // This file is a part of AddressSanitizer, an address sanity checker. 10b47455b5SNico Weber // 11b47455b5SNico Weber // This test file should be compiled w/o asan instrumentation. 12b47455b5SNico Weber //===----------------------------------------------------------------------===// 13b47455b5SNico Weber 14b6f3c8deSKirill Stoimenov #include <assert.h> 15b6f3c8deSKirill Stoimenov #include <sanitizer/allocator_interface.h> 16b6f3c8deSKirill Stoimenov #include <stdio.h> 17b6f3c8deSKirill Stoimenov #include <stdlib.h> 18b6f3c8deSKirill Stoimenov #include <string.h> // for memset() 19b6f3c8deSKirill Stoimenov 20b6f3c8deSKirill Stoimenov #include <algorithm> 21b6f3c8deSKirill Stoimenov #include <limits> 22b6f3c8deSKirill Stoimenov #include <vector> 23b6f3c8deSKirill Stoimenov 24b47455b5SNico Weber #include "asan_allocator.h" 25b47455b5SNico Weber #include "asan_internal.h" 26b47455b5SNico Weber #include "asan_mapping.h" 27b47455b5SNico Weber #include "asan_test_utils.h" 28b47455b5SNico Weber 29b47455b5SNico Weber using namespace __sanitizer; 30b47455b5SNico Weber 31b47455b5SNico Weber // ATTENTION! 32b47455b5SNico Weber // Please don't call intercepted functions (including malloc() and friends) 33b47455b5SNico Weber // in this test. The static runtime library is linked explicitly (without 34b47455b5SNico Weber // -fsanitize=address), thus the interceptors do not work correctly on OS X. 35b47455b5SNico Weber 36b47455b5SNico Weber // Make sure __asan_init is called before any test case is run. 37b47455b5SNico Weber struct AsanInitCaller { 38b47455b5SNico Weber AsanInitCaller() { 39b47455b5SNico Weber __asan_init(); 40b47455b5SNico Weber } 41b47455b5SNico Weber }; 42b47455b5SNico Weber static AsanInitCaller asan_init_caller; 43b47455b5SNico Weber 44b47455b5SNico Weber TEST(AddressSanitizer, InternalSimpleDeathTest) { 45b47455b5SNico Weber EXPECT_DEATH(exit(1), ""); 46b47455b5SNico Weber } 47b47455b5SNico Weber 487269570eSAbhin P Jose static void *MallocStress(void *NumOfItrPtr) { 497269570eSAbhin P Jose size_t n = *((size_t *)NumOfItrPtr); 50b47455b5SNico Weber u32 seed = my_rand(); 51b47455b5SNico Weber BufferedStackTrace stack1; 52b47455b5SNico Weber stack1.trace_buffer[0] = 0xa123; 53b47455b5SNico Weber stack1.trace_buffer[1] = 0xa456; 54b47455b5SNico Weber stack1.size = 2; 55b47455b5SNico Weber 56b47455b5SNico Weber BufferedStackTrace stack2; 57b47455b5SNico Weber stack2.trace_buffer[0] = 0xb123; 58b47455b5SNico Weber stack2.trace_buffer[1] = 0xb456; 59b47455b5SNico Weber stack2.size = 2; 60b47455b5SNico Weber 61b47455b5SNico Weber BufferedStackTrace stack3; 62b47455b5SNico Weber stack3.trace_buffer[0] = 0xc123; 63b47455b5SNico Weber stack3.trace_buffer[1] = 0xc456; 64b47455b5SNico Weber stack3.size = 2; 65b47455b5SNico Weber 66b47455b5SNico Weber std::vector<void *> vec; 67b47455b5SNico Weber for (size_t i = 0; i < n; i++) { 68b47455b5SNico Weber if ((i % 3) == 0) { 69b47455b5SNico Weber if (vec.empty()) continue; 70b47455b5SNico Weber size_t idx = my_rand_r(&seed) % vec.size(); 71b47455b5SNico Weber void *ptr = vec[idx]; 72b47455b5SNico Weber vec[idx] = vec.back(); 73b47455b5SNico Weber vec.pop_back(); 74b47455b5SNico Weber __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC); 75b47455b5SNico Weber } else { 76b47455b5SNico Weber size_t size = my_rand_r(&seed) % 1000 + 1; 77b47455b5SNico Weber switch ((my_rand_r(&seed) % 128)) { 78b47455b5SNico Weber case 0: size += 1024; break; 79b47455b5SNico Weber case 1: size += 2048; break; 80b47455b5SNico Weber case 2: size += 4096; break; 81b47455b5SNico Weber } 82b47455b5SNico Weber size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1); 83b47455b5SNico Weber char *ptr = (char*)__asan::asan_memalign(alignment, size, 84b47455b5SNico Weber &stack2, __asan::FROM_MALLOC); 85b47455b5SNico Weber EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0)); 86b47455b5SNico Weber vec.push_back(ptr); 87b47455b5SNico Weber ptr[0] = 0; 88b47455b5SNico Weber ptr[size-1] = 0; 89b47455b5SNico Weber ptr[size/2] = 0; 90b47455b5SNico Weber } 91b47455b5SNico Weber } 92b47455b5SNico Weber for (size_t i = 0; i < vec.size(); i++) 93b47455b5SNico Weber __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC); 947269570eSAbhin P Jose return nullptr; 95b47455b5SNico Weber } 96b47455b5SNico Weber 97b47455b5SNico Weber TEST(AddressSanitizer, NoInstMallocTest) { 987269570eSAbhin P Jose const size_t kNumIterations = (ASAN_LOW_MEMORY) ? 300000 : 1000000; 997269570eSAbhin P Jose MallocStress((void *)&kNumIterations); 100b47455b5SNico Weber } 101b47455b5SNico Weber 102b47455b5SNico Weber TEST(AddressSanitizer, ThreadedMallocStressTest) { 103b47455b5SNico Weber const int kNumThreads = 4; 1047269570eSAbhin P Jose const size_t kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000; 105b47455b5SNico Weber pthread_t t[kNumThreads]; 106b47455b5SNico Weber for (int i = 0; i < kNumThreads; i++) { 107b47455b5SNico Weber PTHREAD_CREATE(&t[i], 0, (void *(*)(void *x))MallocStress, 1087269570eSAbhin P Jose (void *)&kNumIterations); 109b47455b5SNico Weber } 110b47455b5SNico Weber for (int i = 0; i < kNumThreads; i++) { 111b47455b5SNico Weber PTHREAD_JOIN(t[i], 0); 112b47455b5SNico Weber } 113b47455b5SNico Weber } 114b47455b5SNico Weber 115b47455b5SNico Weber static void PrintShadow(const char *tag, uptr ptr, size_t size) { 116b47455b5SNico Weber fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size); 117b47455b5SNico Weber uptr prev_shadow = 0; 118b47455b5SNico Weber for (sptr i = -32; i < (sptr)size + 32; i++) { 119b47455b5SNico Weber uptr shadow = __asan::MemToShadow(ptr + i); 120b47455b5SNico Weber if (i == 0 || i == (sptr)size) 121b47455b5SNico Weber fprintf(stderr, "."); 122b47455b5SNico Weber if (shadow != prev_shadow) { 123b47455b5SNico Weber prev_shadow = shadow; 124b47455b5SNico Weber fprintf(stderr, "%02x", (int)*(u8*)shadow); 125b47455b5SNico Weber } 126b47455b5SNico Weber } 127b47455b5SNico Weber fprintf(stderr, "\n"); 128b47455b5SNico Weber } 129b47455b5SNico Weber 130b47455b5SNico Weber TEST(AddressSanitizer, DISABLED_InternalPrintShadow) { 131b47455b5SNico Weber for (size_t size = 1; size <= 513; size++) { 132b47455b5SNico Weber char *ptr = new char[size]; 133b47455b5SNico Weber PrintShadow("m", (uptr)ptr, size); 134b47455b5SNico Weber delete [] ptr; 135b47455b5SNico Weber PrintShadow("f", (uptr)ptr, size); 136b47455b5SNico Weber } 137b47455b5SNico Weber } 138b47455b5SNico Weber 139b47455b5SNico Weber TEST(AddressSanitizer, QuarantineTest) { 140*5e5cce56SFlorian Mayer UNINITIALIZED BufferedStackTrace stack; 141b47455b5SNico Weber stack.trace_buffer[0] = 0x890; 142b47455b5SNico Weber stack.size = 1; 143b47455b5SNico Weber 144b47455b5SNico Weber const int size = 1024; 145b47455b5SNico Weber void *p = __asan::asan_malloc(size, &stack); 146b47455b5SNico Weber __asan::asan_free(p, &stack, __asan::FROM_MALLOC); 147b47455b5SNico Weber size_t i; 148b47455b5SNico Weber size_t max_i = 1 << 30; 149b47455b5SNico Weber for (i = 0; i < max_i; i++) { 150b47455b5SNico Weber void *p1 = __asan::asan_malloc(size, &stack); 151b47455b5SNico Weber __asan::asan_free(p1, &stack, __asan::FROM_MALLOC); 152b47455b5SNico Weber if (p1 == p) break; 153b47455b5SNico Weber } 154b47455b5SNico Weber EXPECT_GE(i, 10000U); 155b47455b5SNico Weber EXPECT_LT(i, max_i); 156b47455b5SNico Weber } 157b47455b5SNico Weber 158b47455b5SNico Weber #if !defined(__NetBSD__) 159b47455b5SNico Weber void *ThreadedQuarantineTestWorker(void *unused) { 160b47455b5SNico Weber (void)unused; 161b47455b5SNico Weber u32 seed = my_rand(); 162*5e5cce56SFlorian Mayer UNINITIALIZED BufferedStackTrace stack; 163b47455b5SNico Weber stack.trace_buffer[0] = 0x890; 164b47455b5SNico Weber stack.size = 1; 165b47455b5SNico Weber 166b47455b5SNico Weber for (size_t i = 0; i < 1000; i++) { 167b47455b5SNico Weber void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack); 168b47455b5SNico Weber __asan::asan_free(p, &stack, __asan::FROM_MALLOC); 169b47455b5SNico Weber } 170b47455b5SNico Weber return NULL; 171b47455b5SNico Weber } 172b47455b5SNico Weber 173b47455b5SNico Weber // Check that the thread local allocators are flushed when threads are 174b47455b5SNico Weber // destroyed. 175b47455b5SNico Weber TEST(AddressSanitizer, ThreadedQuarantineTest) { 176b47455b5SNico Weber // Run the routine once to warm up ASAN internal structures to get more 177b47455b5SNico Weber // predictable incremental memory changes. 178b47455b5SNico Weber pthread_t t; 179b47455b5SNico Weber PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); 180b47455b5SNico Weber PTHREAD_JOIN(t, 0); 181b47455b5SNico Weber 182b47455b5SNico Weber const int n_threads = 3000; 183b47455b5SNico Weber size_t mmaped1 = __sanitizer_get_heap_size(); 184b47455b5SNico Weber for (int i = 0; i < n_threads; i++) { 185b47455b5SNico Weber pthread_t t; 186b47455b5SNico Weber PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0); 187b47455b5SNico Weber PTHREAD_JOIN(t, 0); 188b47455b5SNico Weber size_t mmaped2 = __sanitizer_get_heap_size(); 189b47455b5SNico Weber // Figure out why this much memory is required. 190b47455b5SNico Weber EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20)); 191b47455b5SNico Weber } 192b47455b5SNico Weber } 193b47455b5SNico Weber #endif 194b47455b5SNico Weber 195b47455b5SNico Weber void *ThreadedOneSizeMallocStress(void *unused) { 196b47455b5SNico Weber (void)unused; 197*5e5cce56SFlorian Mayer UNINITIALIZED BufferedStackTrace stack; 198b47455b5SNico Weber stack.trace_buffer[0] = 0x890; 199b47455b5SNico Weber stack.size = 1; 200b47455b5SNico Weber const size_t kNumMallocs = 1000; 201b47455b5SNico Weber for (int iter = 0; iter < 1000; iter++) { 202b47455b5SNico Weber void *p[kNumMallocs]; 203b47455b5SNico Weber for (size_t i = 0; i < kNumMallocs; i++) { 204b47455b5SNico Weber p[i] = __asan::asan_malloc(32, &stack); 205b47455b5SNico Weber } 206b47455b5SNico Weber for (size_t i = 0; i < kNumMallocs; i++) { 207b47455b5SNico Weber __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC); 208b47455b5SNico Weber } 209b47455b5SNico Weber } 210b47455b5SNico Weber return NULL; 211b47455b5SNico Weber } 212b47455b5SNico Weber 213b47455b5SNico Weber TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) { 214b47455b5SNico Weber const int kNumThreads = 4; 215b47455b5SNico Weber pthread_t t[kNumThreads]; 216b47455b5SNico Weber for (int i = 0; i < kNumThreads; i++) { 217b47455b5SNico Weber PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0); 218b47455b5SNico Weber } 219b47455b5SNico Weber for (int i = 0; i < kNumThreads; i++) { 220b47455b5SNico Weber PTHREAD_JOIN(t[i], 0); 221b47455b5SNico Weber } 222b47455b5SNico Weber } 223b47455b5SNico Weber 224b47455b5SNico Weber TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) { 225b47455b5SNico Weber using __asan::kHighMemEnd; 226b47455b5SNico Weber // Check that __asan_region_is_poisoned works for shadow regions. 227b47455b5SNico Weber uptr ptr = kLowShadowBeg + 200; 228b47455b5SNico Weber EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); 229b47455b5SNico Weber ptr = kShadowGapBeg + 200; 230b47455b5SNico Weber EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); 231b47455b5SNico Weber ptr = kHighShadowBeg + 200; 232b47455b5SNico Weber EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100)); 233b47455b5SNico Weber } 234b47455b5SNico Weber 235b47455b5SNico Weber // Test __asan_load1 & friends. 236b47455b5SNico Weber typedef void (*CB)(uptr p); 237c1352485SKirill Stoimenov static void TestLoadStoreCallbacks(CB cb[2][5]) { 238b47455b5SNico Weber uptr buggy_ptr; 239b47455b5SNico Weber 240b47455b5SNico Weber __asan_test_only_reported_buggy_pointer = &buggy_ptr; 241*5e5cce56SFlorian Mayer UNINITIALIZED BufferedStackTrace stack; 242b47455b5SNico Weber stack.trace_buffer[0] = 0x890; 243b47455b5SNico Weber stack.size = 1; 244b47455b5SNico Weber 245b47455b5SNico Weber for (uptr len = 16; len <= 32; len++) { 246b47455b5SNico Weber char *ptr = (char*) __asan::asan_malloc(len, &stack); 247b47455b5SNico Weber uptr p = reinterpret_cast<uptr>(ptr); 248b47455b5SNico Weber for (uptr is_write = 0; is_write <= 1; is_write++) { 249b47455b5SNico Weber for (uptr size_log = 0; size_log <= 4; size_log++) { 250b47455b5SNico Weber uptr size = 1 << size_log; 251b47455b5SNico Weber CB call = cb[is_write][size_log]; 252b47455b5SNico Weber // Iterate only size-aligned offsets. 253b47455b5SNico Weber for (uptr offset = 0; offset <= len; offset += size) { 254b47455b5SNico Weber buggy_ptr = 0; 255b47455b5SNico Weber call(p + offset); 256b47455b5SNico Weber if (offset + size <= len) 257b47455b5SNico Weber EXPECT_EQ(buggy_ptr, 0U); 258b47455b5SNico Weber else 259b47455b5SNico Weber EXPECT_EQ(buggy_ptr, p + offset); 260b47455b5SNico Weber } 261b47455b5SNico Weber } 262b47455b5SNico Weber } 263b47455b5SNico Weber __asan::asan_free(ptr, &stack, __asan::FROM_MALLOC); 264b47455b5SNico Weber } 265b47455b5SNico Weber __asan_test_only_reported_buggy_pointer = 0; 266b47455b5SNico Weber } 267c1352485SKirill Stoimenov 268c1352485SKirill Stoimenov TEST(AddressSanitizer, LoadStoreCallbacks) { 269c1352485SKirill Stoimenov CB cb[2][5] = {{ 270c1352485SKirill Stoimenov __asan_load1, 271c1352485SKirill Stoimenov __asan_load2, 272c1352485SKirill Stoimenov __asan_load4, 273c1352485SKirill Stoimenov __asan_load8, 274c1352485SKirill Stoimenov __asan_load16, 275c1352485SKirill Stoimenov }, 276c1352485SKirill Stoimenov { 277c1352485SKirill Stoimenov __asan_store1, 278c1352485SKirill Stoimenov __asan_store2, 279c1352485SKirill Stoimenov __asan_store4, 280c1352485SKirill Stoimenov __asan_store8, 281c1352485SKirill Stoimenov __asan_store16, 282c1352485SKirill Stoimenov }}; 283c1352485SKirill Stoimenov TestLoadStoreCallbacks(cb); 284c1352485SKirill Stoimenov } 285c1352485SKirill Stoimenov 286c1352485SKirill Stoimenov #if defined(__x86_64__) && \ 2878246b2e1SMariusz Borsa !(defined(SANITIZER_APPLE) || defined(SANITIZER_WINDOWS)) 288c1352485SKirill Stoimenov // clang-format off 289c1352485SKirill Stoimenov 290c1352485SKirill Stoimenov #define CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(s, reg, op) \ 291c1352485SKirill Stoimenov void CallAsanMemoryAccessAdd##reg##op##s(uptr address) { \ 292c1352485SKirill Stoimenov asm("push %%" #reg " \n" \ 293c1352485SKirill Stoimenov "mov %[x], %%" #reg " \n" \ 294c1352485SKirill Stoimenov "call __asan_check_" #op "_add_" #s "_" #reg "\n" \ 295c1352485SKirill Stoimenov "pop %%" #reg " \n" \ 296c1352485SKirill Stoimenov : \ 297c1352485SKirill Stoimenov : [x] "r"(address) \ 298c1352485SKirill Stoimenov : "r8", "rdi"); \ 299c1352485SKirill Stoimenov } 300c1352485SKirill Stoimenov 301c1352485SKirill Stoimenov #define TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(reg) \ 302c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(1, reg, load) \ 303c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(1, reg, store) \ 304c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(2, reg, load) \ 305c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(2, reg, store) \ 306c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(4, reg, load) \ 307c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(4, reg, store) \ 308c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(8, reg, load) \ 309c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(8, reg, store) \ 310c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(16, reg, load) \ 311c1352485SKirill Stoimenov CALL_ASAN_MEMORY_ACCESS_CALLBACK_ADD(16, reg, store) \ 312c1352485SKirill Stoimenov \ 313c1352485SKirill Stoimenov TEST(AddressSanitizer, LoadStoreCallbacksAddX86##reg) { \ 314c1352485SKirill Stoimenov CB cb[2][5] = {{ \ 315c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##load1, \ 316c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##load2, \ 317c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##load4, \ 318c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##load8, \ 319c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##load16, \ 320c1352485SKirill Stoimenov }, \ 321c1352485SKirill Stoimenov { \ 322c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##store1, \ 323c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##store2, \ 324c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##store4, \ 325c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##store8, \ 326c1352485SKirill Stoimenov CallAsanMemoryAccessAdd##reg##store16, \ 327c1352485SKirill Stoimenov }}; \ 328c1352485SKirill Stoimenov TestLoadStoreCallbacks(cb); \ 329c1352485SKirill Stoimenov } 330c1352485SKirill Stoimenov 331c1352485SKirill Stoimenov // Instantiate all but R10 and R11 callbacks. We are using PLTSafe class with 332c1352485SKirill Stoimenov // the intrinsic, which guarantees that the code generation will never emit 333c1352485SKirill Stoimenov // R10 or R11 callbacks. 334c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RAX) 335c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RBX) 336c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RCX) 337c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RDX) 338c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RSI) 339c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RDI) 340c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(RBP) 341c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R8) 342c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R9) 343c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R12) 344c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R13) 345c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R14) 346c1352485SKirill Stoimenov TEST_ASAN_MEMORY_ACCESS_CALLBACKS_ADD(R15) 347c1352485SKirill Stoimenov 348c1352485SKirill Stoimenov // clang-format on 349c1352485SKirill Stoimenov #endif 350