1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <android/log.h>
18 #include <jni.h>
19 #include <malloc.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include <fstream>
24 #include <memory>
25 #include <shared_mutex>
26 #include <string>
27 #include <vector>
28
29 // We can get all the GWP-ASan ranges ahead of time. GWP-ASan doesn't do an mmap() for each
30 // allocation, it reserves the entire pool up front (with the name "[anon:GWP-ASan Guard Page]") and
31 // then mprotect()s and renames pages in that pool as necessary. At the point where we're observing
32 // the range, it's normal to have a couple of slots already in use. Technically, the metadata region
33 // also exists at startup ("[anon:GWP-ASan Metadata]"), but no malloc() will ever be allocated
34 // there, and it's not necessary to special case this single range.
get_gwp_asan_ranges()35 const std::vector<std::pair<uintptr_t, uintptr_t>>& get_gwp_asan_ranges() {
36 static std::mutex gwp_asan_ranges_mutex;
37 static std::vector<std::pair<uintptr_t, uintptr_t>> gwp_asan_ranges;
38
39 std::lock_guard<std::mutex> l(gwp_asan_ranges_mutex);
40 if (gwp_asan_ranges.size() != 0) return gwp_asan_ranges;
41
42 std::ifstream mappings("/proc/self/maps");
43 if (!mappings.good()) {
44 __android_log_print(ANDROID_LOG_FATAL, getprogname(), "Failed to open /proc/self/maps");
45 }
46
47 std::string line;
48 while (std::getline(mappings, line)) {
49 uintptr_t map_start, map_end;
50 if (line.find("[anon:GWP-ASan") != std::string::npos &&
51 sscanf(line.c_str(), "%zx-%zx", &map_start, &map_end) == 2) {
52 gwp_asan_ranges.emplace_back(map_start, map_end);
53 __android_log_print(ANDROID_LOG_INFO, getprogname(),
54 "Found 0x%zx-byte GWP-ASan mapping: \"%s\"", map_end - map_start,
55 line.c_str());
56 }
57 }
58 return gwp_asan_ranges;
59 }
60
is_gwp_asan_pointer(void * ptr)61 bool is_gwp_asan_pointer(void* ptr) {
62 static const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges = get_gwp_asan_ranges();
63 uintptr_t untagged_ptr = reinterpret_cast<uintptr_t>(ptr);
64 #if defined(__aarch64__)
65 // Untag the heap pointer: https://source.android.com/docs/security/test/tagged-pointers
66 untagged_ptr &= ~(0xffull << 56);
67 #endif // defined(__aarch64__)
68 return std::any_of(ranges.cbegin(), ranges.cend(), [&](const auto& range) {
69 return untagged_ptr >= range.first && untagged_ptr < range.second;
70 });
71 }
72
73 constexpr size_t kMallocsToGuaranteeAGwpAsanPointer = 0x10000;
74
get_gwp_asan_pointer()75 std::unique_ptr<char[]> get_gwp_asan_pointer() {
76 for (size_t i = 0; i < kMallocsToGuaranteeAGwpAsanPointer; ++i) {
77 auto p = std::make_unique<char[]>(4096);
78 if (is_gwp_asan_pointer(p.get())) {
79 __android_log_print(ANDROID_LOG_INFO, getprogname(), "Found GWP-ASan pointer: %p",
80 p.get());
81 return p;
82 }
83 }
84 return std::unique_ptr<char[]>();
85 }
86
87 // Note: The '_1' in the function signature here is the JNI literalization of the underscore in the
88 // 'gwp_asan' part of the package name. See "Table 2-1 Unicode Character Translation" in
89 // https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html
Java_android_cts_gwp_1asan_Utils_isGwpAsanEnabled(JNIEnv *)90 extern "C" JNIEXPORT jboolean JNICALL Java_android_cts_gwp_1asan_Utils_isGwpAsanEnabled(JNIEnv*) {
91 std::unique_ptr<char[]> gwp_asan_ptr = get_gwp_asan_pointer();
92 return gwp_asan_ptr.get() == nullptr ? JNI_FALSE : JNI_TRUE;
93 }
94
95 extern "C" JNIEXPORT void JNICALL
Java_android_cts_gwp_1asan_Utils_instrumentedUseAfterFree(JNIEnv *)96 Java_android_cts_gwp_1asan_Utils_instrumentedUseAfterFree(JNIEnv*) {
97 char* volatile p = nullptr;
98 {
99 std::unique_ptr<char[]> gwp_asan_ptr = get_gwp_asan_pointer();
100 p = gwp_asan_ptr.get();
101 }
102 if (!p) return;
103 __attribute__((unused)) volatile char c = *p;
104 }
105