• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.cts.gwp_asan;
18 
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.app.Application;
22 import android.app.ApplicationExitInfo;
23 import android.content.Context;
24 import android.cts.gwp_asan.Android13Tombstone.Tombstone;
25 import android.util.Log;
26 
27 import com.android.compatibility.common.util.DropBoxReceiver;
28 
29 import java.io.BufferedReader;
30 import java.io.FileReader;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.List;
34 
35 public class Utils {
36     static {
37         System.loadLibrary("gwp_asan_cts_library_jni");
38     }
39 
40     public static final int TEST_SUCCESS = Activity.RESULT_FIRST_USER + 100;
41     public static final int TEST_FAILURE = Activity.RESULT_FIRST_USER + 101;
42 
43     public static final int TEST_IS_GWP_ASAN_ENABLED = 42;
44     public static final int TEST_IS_GWP_ASAN_DISABLED = 43;
45     public static final int TEST_USE_AFTER_FREE = 44;
46 
47     public static final String DROPBOX_NON_RECOVERABLE_TAG = "data_app_native_crash";
48     public static final String DROPBOX_RECOVERABLE_TAG = "data_app_native_recoverable_crash";
49 
50     // Check that GWP-ASan is enabled by allocating a whole bunch of heap pointers and making sure
51     // one of them is a GWP-ASan allocation (by comparing with /proc/self/maps).
isGwpAsanEnabled()52     public static native boolean isGwpAsanEnabled();
53     // Check that GWP-ASan is disabled by ensuring there's no GWP-ASan mappings in /proc/self/maps.
isGwpAsanDisabled()54     public static boolean isGwpAsanDisabled() throws IOException {
55         try (FileReader fr = new FileReader("/proc/self/maps");
56                 BufferedReader reader = new BufferedReader(fr)) {
57             String line;
58             while ((line = reader.readLine()) != null) {
59                 if (line.contains("GWP-ASan Guard Page")) {
60                     Log.e(Application.getProcessName(),
61                             "Line contained guard page: " + line);
62                     return false;
63                 }
64             }
65         }
66         return true;
67     }
68 
69     // Trigger a GWP-ASan instrumented use-after-free. This should always trigger a GWP-ASan report
70     // if it's enabled. In non-recoverable mode (i.e. gwpAsanMode=always), then this will crash the
71     // app with SEGV. In recoverable mode (i.e. gwpAsanMode=default), then this won't crash, but
72     // should still trigger all the dropbox/debuggerd entries.
instrumentedUseAfterFree()73     public static native void instrumentedUseAfterFree();
74 
runTest(int testId)75     public static int runTest(int testId) {
76         try {
77             if (testId == TEST_IS_GWP_ASAN_ENABLED) {
78                 return Utils.isGwpAsanEnabled() ? Utils.TEST_SUCCESS : Utils.TEST_FAILURE;
79             }
80             if (testId == TEST_IS_GWP_ASAN_DISABLED) {
81                 return Utils.isGwpAsanDisabled() ? Utils.TEST_SUCCESS : Utils.TEST_FAILURE;
82             }
83             if (testId == TEST_USE_AFTER_FREE) {
84                 Utils.instrumentedUseAfterFree();
85                 return Utils.TEST_SUCCESS;
86             }
87         } catch (Exception e) {
88             return Utils.TEST_FAILURE;
89         }
90         return Utils.TEST_FAILURE;
91     }
92 
getDropboxReceiver( Context context, String processNameSuffix, String crashTag)93     public static DropBoxReceiver getDropboxReceiver(
94             Context context, String processNameSuffix, String crashTag) {
95         return new DropBoxReceiver(
96                 context,
97                 crashTag,
98                 context.getPackageName() + ":" + processNameSuffix,
99                 "SEGV_ACCERR",
100                 "Cause: [GWP-ASan]: Use After Free",
101                 "deallocated by",
102                 "backtrace:");
103     }
104 
appExitInfoHasReport(Context context, String processNameSuffix)105     public static boolean appExitInfoHasReport(Context context, String processNameSuffix)
106             throws Exception {
107         ActivityManager am = context.getSystemService(ActivityManager.class);
108         List<ApplicationExitInfo> exitReasons =
109                 am.getHistoricalProcessExitReasons(
110                         processNameSuffix == null
111                                 ? null
112                                 : context.getPackageName() + ":" + processNameSuffix,
113                         0,
114                         0);
115         for (ApplicationExitInfo exitReason : exitReasons) {
116             if (exitReason.getReason() != ApplicationExitInfo.REASON_CRASH_NATIVE) continue;
117             InputStream data = exitReason.getTraceInputStream();
118             if (data == null) continue;
119             Tombstone tombstone = Android13Tombstone.Tombstone.parseFrom(data.readAllBytes());
120             String cause = tombstone.getCauses(0).getHumanReadable();
121             if (cause.contains("[GWP-ASan]: Use After Free")) {
122                 return true;
123             }
124         }
125         return false;
126     }
127 }
128