diff options
author | Duy Truong <duytruong@google.com> | 2021-10-27 22:22:20 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-10-27 22:22:20 +0000 |
commit | e48ac12d35320e912a07b19147ba9a24984d4351 (patch) | |
tree | cc29982bbc39ddf62052ae614174e74f28047992 | |
parent | ced436b3eeb11c59ca12e7832c4c21bea0d237ea (diff) | |
parent | e03111efb55a5ae2323b3a84d0e7798d5dce0e71 (diff) | |
download | platform_testing-e48ac12d35320e912a07b19147ba9a24984d4351.tar.gz |
CP from aosp/1854060 am: e03111efb5
Original change: https://googleplex-android-review.googlesource.com/c/platform/platform_testing/+/16125754
Change-Id: I471b775f4988bc66bdcee040b826ad1c28f29787
2 files changed, 274 insertions, 14 deletions
diff --git a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/CrashUtils.java b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/CrashUtils.java index 785139595..5e0d12718 100644 --- a/libraries/compatibility-common-util/src/com/android/compatibility/common/util/CrashUtils.java +++ b/libraries/compatibility-common-util/src/com/android/compatibility/common/util/CrashUtils.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import java.math.BigInteger; import org.json.JSONArray; @@ -68,8 +69,7 @@ public class CrashUtils { "\\w+? \\d+? \\((.*?)\\), code -*?\\d+? \\(.*?\\), fault addr " + "(?:0x(\\p{XDigit}+)|-+)"); // Matches the abort message line - private static Pattern sAbortMessagePattern = - Pattern.compile("(?i)Abort message: (.*)"); + private static Pattern sAbortMessagePattern = Pattern.compile("(?i)Abort message: (.*)"); // Matches one backtrace NOTE line, exactly as tombstone_proto_to_text's print_thread_backtrace private static Pattern sBacktraceNotePattern = Pattern.compile("[0-9\\-\\s:.]+[A-Z] DEBUG\\s+:\\s+NOTE: .*"); @@ -172,6 +172,37 @@ public class CrashUtils { continue; } } + + JSONArray backtrace = crash.getJSONArray(BACKTRACE); + + /* if backtrace "includes" patterns are present, ignore this crash if there is no + * frame that matches any of the patterns + */ + List<Config.BacktraceFilterPattern> backtraceIncludes = + config.getBacktraceIncludes(); + if (!backtraceIncludes.isEmpty()) { + if (!IntStream.range(0, backtrace.length()) + .mapToObj(j -> backtrace.optJSONObject(j)) + .flatMap(frame -> backtraceIncludes.stream().map(p -> p.match(frame))) + .anyMatch(matched -> matched)) { + continue; + } + } + + /* if backtrace "excludes" patterns are present, ignore this crash if there is any + * frame that matches any of the patterns + */ + List<Config.BacktraceFilterPattern> backtraceExcludes = + config.getBacktraceExcludes(); + if (!backtraceExcludes.isEmpty()) { + if (IntStream.range(0, backtrace.length()) + .mapToObj(j -> backtrace.optJSONObject(j)) + .flatMap(frame -> backtraceExcludes.stream().map(p -> p.match(frame))) + .anyMatch(matched -> matched)) { + continue; + } + } + securityCrashes.put(crash); } catch (JSONException | NullPointerException e) {} } @@ -208,10 +239,12 @@ public class CrashUtils { if (pidtidNameMatcher.find()) { try { pid = Integer.parseInt(pidtidNameMatcher.group(1)); - } catch (NumberFormatException e) {} + } catch (NumberFormatException e) { + } try { tid = Integer.parseInt(pidtidNameMatcher.group(2)); - } catch (NumberFormatException e) {} + } catch (NumberFormatException e) { + } name = pidtidNameMatcher.group(3).trim(); process = pidtidNameMatcher.group(4).trim(); } @@ -223,7 +256,8 @@ public class CrashUtils { if (faultAddrMatch != null) { try { faultAddress = new BigInteger(faultAddrMatch, 16); - } catch (NumberFormatException e) {} + } catch (NumberFormatException e) { + } } } @@ -278,8 +312,7 @@ public class CrashUtils { crash.put(TID, tid); crash.put(NAME, name); crash.put(PROCESS, process); - crash.put(FAULT_ADDRESS, - faultAddress == null ? null : faultAddress.toString(16)); + crash.put(FAULT_ADDRESS, faultAddress == null ? null : faultAddress.toString(16)); crash.put(SIGNAL, signal); crash.put(ABORT_MESSAGE, abortMessage); JSONArray backtrace = new JSONArray(); @@ -321,6 +354,8 @@ public class CrashUtils { private List<Pattern> processPatterns; private List<Pattern> abortMessageIncludes; private List<Pattern> abortMessageExcludes; + private List<BacktraceFilterPattern> backtraceIncludes; + private List<BacktraceFilterPattern> backtraceExcludes; public Config() { checkMinAddress = true; @@ -329,6 +364,8 @@ public class CrashUtils { abortMessageIncludes = new ArrayList<>(); setAbortMessageExcludes("CHECK_", "CANNOT LINK EXECUTABLE"); processPatterns = new ArrayList(); + backtraceIncludes = new ArrayList(); + backtraceExcludes = new ArrayList(); } public Config setMinAddress(BigInteger minCrashAddress) { @@ -415,6 +452,87 @@ public class CrashUtils { Collections.addAll(this.processPatterns, processPatterns); return this; } + + public Config setBacktraceIncludes(BacktraceFilterPattern... patterns) { + this.backtraceIncludes = new ArrayList<>(Arrays.asList(patterns)); + return this; + } + + public List<BacktraceFilterPattern> getBacktraceIncludes() { + return Collections.unmodifiableList(this.backtraceIncludes); + } + + public Config appendBacktraceIncludes(BacktraceFilterPattern... patterns) { + Collections.addAll(this.backtraceIncludes, patterns); + return this; + } + + public Config setBacktraceExcludes(BacktraceFilterPattern... patterns) { + this.backtraceExcludes = new ArrayList<>(Arrays.asList(patterns)); + return this; + } + + public List<BacktraceFilterPattern> getBacktraceExcludes() { + return Collections.unmodifiableList(this.backtraceExcludes); + } + + public Config appendBacktraceExcludes(BacktraceFilterPattern... patterns) { + Collections.addAll(this.backtraceExcludes, patterns); + return this; + } + + /** + * A utility class that contains patterns to filter backtraces on. + * + * <p>A filter matches if any of the backtrace frame matches any of the patterns. + * + * <p>Either filenamePattern or methodPattern can be null, in which case it will act like a + * wildcard pattern and matches anything. + * + * <p>A null filename or method name will not match any non-null pattern. + */ + public static class BacktraceFilterPattern { + private final Pattern filenamePattern; + private final Pattern methodPattern; + + /** + * Constructs a BacktraceFilterPattern with the given file and method name patterns. + * + * <p>Null patterns are interpreted as wildcards and match anything. + * + * @param filenamePattern Regex string for the filename pattern. Can be null. + * @param methodPattern Regex string for the method name pattern. Can be null. + */ + public BacktraceFilterPattern(String filenamePattern, String methodPattern) { + if (filenamePattern == null) { + this.filenamePattern = null; + } else { + this.filenamePattern = Pattern.compile(filenamePattern); + } + + if (methodPattern == null) { + this.methodPattern = null; + } else { + this.methodPattern = Pattern.compile(methodPattern); + } + } + + /** Returns true if the current patterns match a backtrace frame. */ + public boolean match(JSONObject frame) { + if (frame == null) return false; + + String filename = frame.optString(FILENAME); + String method = frame.optString(METHOD); + + boolean filenameMatches = + (filenamePattern == null + || (filename != null && filenamePattern.matcher(filename).find())); + boolean methodMatches = + (methodPattern == null + || (method != null && methodPattern.matcher(method).find())); + return filenameMatches && methodMatches; + } + } } private static List<Pattern> toPatterns(String... patternStrings) { diff --git a/libraries/compatibility-common-util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java b/libraries/compatibility-common-util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java index 78e78be64..92be36639 100644 --- a/libraries/compatibility-common-util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java +++ b/libraries/compatibility-common-util/tests/src/com/android/compatibility/common/util/CrashUtilsTest.java @@ -16,6 +16,7 @@ package com.android.compatibility.common.util; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; import com.google.common.collect.ImmutableList; import java.io.BufferedReader; import java.io.IOException; @@ -501,13 +502,13 @@ public class CrashUtilsTest { } public JSONObject createCrashJson( - int pid, - int tid, - String name, - String process, - String faultaddress, - String signal, - String abortMessage) { + int pid, + int tid, + String name, + String process, + String faultaddress, + String signal, + String abortMessage) { return createCrashJson( pid, tid, name, process, faultaddress, signal, abortMessage, ImmutableList.of()); } @@ -656,4 +657,145 @@ public class CrashUtilsTest { .setProcessPatterns(Pattern.compile("CVE-2015-6616-2")))); } + + @Test + public void testBacktraceFilterIncludeFilename() throws Exception { + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern("libaudioutils", null)))); + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern("libstagefright", null), + new BacktraceFilterPattern("libaudioflinger\\.so", null)))); + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern("libstagefright", null)))); + } + + @Test + public void testBacktraceFilterExcludeFilename() throws Exception { + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes( + new BacktraceFilterPattern("libaudioutils", null)))); + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes( + new BacktraceFilterPattern("libstagefright", null), + new BacktraceFilterPattern("libaudioflinger\\.so", null)))); + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes( + new BacktraceFilterPattern("libstagefright", null)))); + } + + @Test + public void testBacktraceFilterIncludeMethodName() throws Exception { + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern(null, "memcpy_to_float")))); + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern(null, "strlen"), + new BacktraceFilterPattern(null, "memcpy_[^_]+_float")))); + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes(new BacktraceFilterPattern(null, "strlen")))); + } + + @Test + public void testBacktraceFilterExcludeMethodName() throws Exception { + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes( + new BacktraceFilterPattern(null, "memcpy_to_float")))); + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes( + new BacktraceFilterPattern(null, "strlen"), + new BacktraceFilterPattern(null, "memcpy_[^_]+_float")))); + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceExcludes(new BacktraceFilterPattern(null, "strlen")))); + } + + @Test + public void testBacktraceFilterCombinations() throws Exception { + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes(new BacktraceFilterPattern(null, null)))); + Assert.assertTrue( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern("libaudioutils", "memcpy")))); + Assert.assertFalse( + CrashUtils.securityCrashDetected( + mCrashes, + new CrashUtils.Config() + .checkMinAddress(true) + .setProcessPatterns(Pattern.compile("synthetic_process_0")) + .setBacktraceIncludes( + new BacktraceFilterPattern("libaudioutils", "strlen")))); + } } |