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