From 053ee29ff0e100b427662e0ec7d2902febab366e Mon Sep 17 00:00:00 2001 From: Omer Nebil Yaveroglu Date: Thu, 9 Jan 2020 16:24:28 +0000 Subject: [PATCH] Modify the RuleBinaryParser to only parse the ranges that are within the index ranges. Bug: 145493956 Test: atest FrameworksServicesTests:RuleBinaryParserTest Change-Id: I5a453cf49b937fa4516ae9939ec6d7bba02e7ac0 --- .../integrity/IntegrityFileManager.java | 16 ++- .../integrity/parser/RuleBinaryParser.java | 44 +++++++- .../server/integrity/parser/RuleParser.java | 3 +- .../integrity/parser/RuleXmlParser.java | 3 +- .../parser/RuleBinaryParserTest.java | 102 ++++++++++++++---- .../integrity/parser/RuleXmlParserTest.java | 4 +- 6 files changed, 139 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java index 31d4816e5d665..17a4b9c6c1706 100644 --- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java +++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java @@ -39,6 +39,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -153,14 +154,19 @@ public class IntegrityFileManager { throws IOException, RuleParseException { synchronized (RULES_LOCK) { // Try to identify indexes from the index file. - List ruleReadingIndexes = - mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); + List ruleReadingIndexes; + try { + ruleReadingIndexes = + mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata); + } catch (Exception e) { + Slog.w(TAG, "Error identifying the rule indexes. Trying unindexed.", e); + ruleReadingIndexes = Collections.emptyList(); + } - // Read the rules based on the index information. - // TODO(b/145493956): Provide the identified indexes to the rule reader. + // Read the rules based on the index information when available. try (FileInputStream inputStream = new FileInputStream(new File(mRulesDir, RULES_FILE))) { - List rules = mRuleParser.parse(inputStream); + List rules = mRuleParser.parse(inputStream, ruleReadingIndexes); return rules; } } diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index 1d426fe51a9c7..e744326c49dbc 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -42,6 +42,7 @@ import com.android.server.integrity.model.BitTrackedInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** A helper class to parse rules into the {@link Rule} model from Binary representation. */ @@ -51,28 +52,40 @@ public class RuleBinaryParser implements RuleParser { public List parse(byte[] ruleBytes) throws RuleParseException { try { BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(ruleBytes); - return parseRules(bitTrackedInputStream); + return parseRules(bitTrackedInputStream, /* indexRanges= */ Collections.emptyList()); } catch (Exception e) { throw new RuleParseException(e.getMessage(), e); } } @Override - public List parse(InputStream inputStream) throws RuleParseException { + public List parse(InputStream inputStream, List indexRanges) + throws RuleParseException { try { BitTrackedInputStream bitTrackedInputStream = new BitTrackedInputStream(inputStream); - return parseRules(bitTrackedInputStream); + return parseRules(bitTrackedInputStream, indexRanges); } catch (Exception e) { throw new RuleParseException(e.getMessage(), e); } } - private List parseRules(BitTrackedInputStream bitTrackedInputStream) throws IOException { - List parsedRules = new ArrayList<>(); + private List parseRules( + BitTrackedInputStream bitTrackedInputStream, + List indexRanges) + throws IOException { // Read the rule binary file format version. bitTrackedInputStream.getNext(FORMAT_VERSION_BITS); + return indexRanges.isEmpty() + ? parseAllRules(bitTrackedInputStream) + : parseIndexedRules(bitTrackedInputStream, indexRanges); + } + + private List parseAllRules(BitTrackedInputStream bitTrackedInputStream) + throws IOException { + List parsedRules = new ArrayList<>(); + while (bitTrackedInputStream.hasNext()) { if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) { parsedRules.add(parseRule(bitTrackedInputStream)); @@ -82,6 +95,27 @@ public class RuleBinaryParser implements RuleParser { return parsedRules; } + private List parseIndexedRules( + BitTrackedInputStream bitTrackedInputStream, List indexRanges) + throws IOException { + List parsedRules = new ArrayList<>(); + + for (RuleIndexRange range : indexRanges) { + // Skip the rules that are not in the range. + bitTrackedInputStream.setCursorToByteLocation(range.getStartIndex()); + + // Read the rules until we reach the end index. + while (bitTrackedInputStream.hasNext() + && bitTrackedInputStream.getReadBitsCount() < range.getEndIndex()) { + if (bitTrackedInputStream.getNext(SIGNAL_BIT) == 1) { + parsedRules.add(parseRule(bitTrackedInputStream)); + } + } + } + + return parsedRules; + } + private Rule parseRule(BitTrackedInputStream bitTrackedInputStream) throws IOException { Formula formula = parseFormula(bitTrackedInputStream); int effect = bitTrackedInputStream.getNext(EFFECT_BITS); diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java index 81783d5c73245..a8e9f61347594 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java @@ -28,5 +28,6 @@ public interface RuleParser { List parse(byte[] ruleBytes) throws RuleParseException; /** Parse rules from an input stream. */ - List parse(InputStream inputStream) throws RuleParseException; + List parse(InputStream inputStream, List ruleIndexRanges) + throws RuleParseException; } diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java index d405583442bd5..497be84242868 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java @@ -62,7 +62,8 @@ public final class RuleXmlParser implements RuleParser { } @Override - public List parse(InputStream inputStream) throws RuleParseException { + public List parse(InputStream inputStream, List indexRanges) + throws RuleParseException { try { XmlPullParser xmlPullParser = Xml.newPullParser(); xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name()); diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index 9cc0ed85a0448..51f5c755754c1 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -48,6 +48,7 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -97,8 +98,10 @@ public class RuleBinaryParserTest { private static final byte[] DEFAULT_FORMAT_VERSION_BYTES = getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS)); + private static final List NO_INDEXING = Collections.emptyList(); + @Test - public void testBinaryStream_validCompoundFormula() throws Exception { + public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -131,13 +134,13 @@ public class RuleBinaryParserTest { /* isHashedValue= */ false))), Rule.DENY); - List rules = binaryParser.parse(inputStream); + List rules = binaryParser.parse(inputStream, NO_INDEXING); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @Test - public void testBinaryString_validCompoundFormula_notConnector() throws Exception { + public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -175,7 +178,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validCompoundFormula_andConnector() throws Exception { + public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -223,7 +226,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validCompoundFormula_orConnector() throws Exception { + public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -272,7 +275,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_stringValue() throws Exception { + public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -304,7 +307,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_hashedValue() throws Exception { + public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception { String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; String ruleBits = START_BIT @@ -337,7 +340,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_integerValue() throws Exception { + public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception { int versionCode = 1; String ruleBits = START_BIT @@ -365,7 +368,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_validAtomicFormula_booleanValue() throws Exception { + public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception { String isPreInstalled = "1"; String ruleBits = START_BIT @@ -392,7 +395,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidAtomicFormula() throws Exception { + public void testBinaryString_invalidAtomicFormula_noIndexing() { int versionCode = 1; String ruleBits = START_BIT @@ -415,7 +418,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_withNoRuleList() throws RuleParseException { + public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException { ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length); rule.put(DEFAULT_FORMAT_VERSION_BYTES); RuleParser binaryParser = new RuleBinaryParser(); @@ -426,7 +429,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_withEmptyRule() throws RuleParseException { + public void testBinaryString_withEmptyRule_noIndexing() { String ruleBits = START_BIT; byte[] ruleBytes = getBytes(ruleBits); ByteBuffer rule = @@ -442,7 +445,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception { + public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() { String packageName = "com.test.app"; String appCertificate = "test_cert"; String ruleBits = @@ -478,7 +481,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidOperator() throws Exception { + public void testBinaryString_invalidRule_invalidOperator_noIndexing() { int versionCode = 1; String ruleBits = START_BIT @@ -506,7 +509,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidEffect() throws Exception { + public void testBinaryString_invalidRule_invalidEffect_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -536,7 +539,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidConnector() throws Exception { + public void testBinaryString_invalidRule_invalidConnector_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -566,7 +569,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidKey() throws Exception { + public void testBinaryString_invalidRule_invalidKey_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -596,7 +599,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidSeparator() throws Exception { + public void testBinaryString_invalidRule_invalidSeparator_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -626,7 +629,7 @@ public class RuleBinaryParserTest { } @Test - public void testBinaryString_invalidRule_invalidEndMarker() throws Exception { + public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() { String packageName = "com.test.app"; String ruleBits = START_BIT @@ -653,4 +656,65 @@ public class RuleBinaryParserTest { /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit", () -> binaryParser.parse(rule.array())); } + + @Test + public void testBinaryStream_multipleRules_indexingIdentifiesParsesIndexRangeCorrectly() + throws Exception { + String packageName2 = "com.test.2"; + + byte[] ruleBytes1 = getBytes(getRulesWithPackageName("com.test.1")); + byte[] ruleBytes2 = getBytes(getRulesWithPackageName(packageName2)); + byte[] ruleBytes3 = getBytes(getRulesWithPackageName("com.test.3")); + + ByteBuffer rule = + ByteBuffer.allocate( + DEFAULT_FORMAT_VERSION_BYTES.length + + ruleBytes1.length + + ruleBytes2.length + + ruleBytes3.length); + rule.put(DEFAULT_FORMAT_VERSION_BYTES); + rule.put(ruleBytes1); + rule.put(ruleBytes2); + rule.put(ruleBytes3); + InputStream inputStream = new ByteArrayInputStream(rule.array()); + + RuleParser binaryParser = new RuleBinaryParser(); + + List indexRanges = new ArrayList<>(); + indexRanges.add( + new RuleIndexRange( + DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length, + DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes1.length + + ruleBytes2.length)); + List rules = binaryParser.parse(inputStream, indexRanges); + + Rule expectedRule = + new Rule( + new CompoundFormula( + CompoundFormula.NOT, + Collections.singletonList( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.PACKAGE_NAME, + packageName2, + /* isHashedValue= */ false))), + Rule.DENY); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + private static String getRulesWithPackageName(String packageName) { + return START_BIT + + COMPOUND_FORMULA_START_BITS + + NOT + + ATOMIC_FORMULA_START_BITS + + PACKAGE_NAME + + EQ + + IS_NOT_HASHED + + getBits(packageName.length(), VALUE_SIZE_BITS) + + getValueBits(packageName) + + COMPOUND_FORMULA_END_BITS + + DENY + + END_BIT; + + } } diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java index a14197b17529a..6944aee7fcb91 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java @@ -73,7 +73,7 @@ public class RuleXmlParserTest { /* isHashedValue= */ false))), Rule.DENY); - List rules = xmlParser.parse(inputStream); + List rules = xmlParser.parse(inputStream, Collections.emptyList()); assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); } @@ -623,7 +623,7 @@ public class RuleXmlParserTest { assertExpectException( RuleParseException.class, /* expectedExceptionMessageRegex */ "Rules must start with RuleList tag", - () -> xmlParser.parse(inputStream)); + () -> xmlParser.parse(inputStream, Collections.emptyList())); } private String generateTagWithAttribute(