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