Store CRCs as Integer rather than as hex strings.

This allows us to do lookup with fewer memory allocations.

Bug: 142333043
Test: atest services/tests/servicestests/src/com/android/server/net/watchlist/*.java
Change-Id: I8a8ecd488fabfaed6f7627d934c0ae5adbd4ac4c
This commit is contained in:
Alan Stokes
2019-10-14 17:46:13 +01:00
parent 8401f0e44b
commit e287fd2cff
3 changed files with 137 additions and 19 deletions

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.net.watchlist;
import com.android.internal.util.HexDump;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Helper class to store a set of harmful CRC32s in memory.
* TODO: Optimize memory usage using int array with binary search.
*/
class HarmfulCrcs {
private final Set<Integer> mCrcSet;
HarmfulCrcs(List<byte[]> digests) {
final HashSet<Integer> crcSet = new HashSet<>();
final int size = digests.size();
for (int i = 0; i < size; i++) {
byte[] bytes = digests.get(i);
if (bytes.length <= 4) {
int crc = 0;
for (byte b : bytes) {
// Remember byte is signed
crc = (crc << 8) | (b & 0xff);
}
crcSet.add(crc);
}
}
mCrcSet = Collections.unmodifiableSet(crcSet);
}
public boolean contains(int crc) {
return mCrcSet.contains(crc);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
for (int crc : mCrcSet) {
pw.println(HexDump.toHexString(crc));
}
pw.println("");
}
}

View File

@@ -18,7 +18,6 @@ package com.android.server.net.watchlist;
import android.annotation.Nullable;
import android.os.FileUtils;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -66,11 +65,11 @@ class WatchlistConfig {
}
private static class CrcShaDigests {
final HarmfulDigests crc32Digests;
final HarmfulDigests sha256Digests;
public final HarmfulCrcs crc32s;
public final HarmfulDigests sha256Digests;
public CrcShaDigests(HarmfulDigests crc32Digests, HarmfulDigests sha256Digests) {
this.crc32Digests = crc32Digests;
CrcShaDigests(HarmfulCrcs crc32s, HarmfulDigests sha256Digests) {
this.crc32s = crc32s;
this.sha256Digests = sha256Digests;
}
}
@@ -140,9 +139,9 @@ class WatchlistConfig {
}
}
parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_CONFIG);
mDomainDigests = new CrcShaDigests(new HarmfulDigests(crc32DomainList),
mDomainDigests = new CrcShaDigests(new HarmfulCrcs(crc32DomainList),
new HarmfulDigests(sha256DomainList));
mIpDigests = new CrcShaDigests(new HarmfulDigests(crc32IpList),
mIpDigests = new CrcShaDigests(new HarmfulCrcs(crc32IpList),
new HarmfulDigests(sha256IpList));
Log.i(TAG, "Reload watchlist done");
} catch (IllegalStateException | NullPointerException | NumberFormatException |
@@ -171,8 +170,8 @@ class WatchlistConfig {
return false;
}
// First it does a quick CRC32 check.
final byte[] crc32 = getCrc32(domain);
if (!domainDigests.crc32Digests.contains(crc32)) {
final int crc32 = getCrc32(domain);
if (!domainDigests.crc32s.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
@@ -187,8 +186,8 @@ class WatchlistConfig {
return false;
}
// First it does a quick CRC32 check.
final byte[] crc32 = getCrc32(ip);
if (!ipDigests.crc32Digests.contains(crc32)) {
final int crc32 = getCrc32(ip);
if (!ipDigests.crc32s.contains(crc32)) {
return false;
}
// Now we do a slow SHA256 check.
@@ -198,15 +197,11 @@ class WatchlistConfig {
/** Get CRC32 of a string
*
* TODO: Review if we should use CRC32 or other algorithms
*/
private byte[] getCrc32(String str) {
private int getCrc32(String str) {
final CRC32 crc = new CRC32();
crc.update(str.getBytes());
final long tmp = crc.getValue();
return new byte[]{(byte) (tmp >> 24 & 255), (byte) (tmp >> 16 & 255),
(byte) (tmp >> 8 & 255), (byte) (tmp & 255)};
return (int) crc.getValue();
}
/** Get SHA256 of a string */
@@ -279,7 +274,7 @@ class WatchlistConfig {
pw.println("Domain CRC32 digest list:");
// mDomainDigests won't go from non-null to null so it's safe
if (mDomainDigests != null) {
mDomainDigests.crc32Digests.dump(fd, pw, args);
mDomainDigests.crc32s.dump(fd, pw, args);
}
pw.println("Domain SHA256 digest list:");
if (mDomainDigests != null) {
@@ -288,7 +283,7 @@ class WatchlistConfig {
pw.println("Ip CRC32 digest list:");
// mIpDigests won't go from non-null to null so it's safe
if (mIpDigests != null) {
mIpDigests.crc32Digests.dump(fd, pw, args);
mIpDigests.crc32s.dump(fd, pw, args);
}
pw.println("Ip SHA256 digest list:");
if (mIpDigests != null) {

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.net.watchlist;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.HexDump;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
/**
* runtest frameworks-services -c com.android.server.net.watchlist.HarmfulCrcTests
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class HarmfulCrcsTests {
private static final byte[] TEST_DIGEST = HexDump.hexStringToByteArray("AABBCCDD");
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testHarmfulCrcs_setAndContains() throws Exception {
HarmfulCrcs harmfulCrcs = new HarmfulCrcs(
Arrays.asList(new byte[][] {TEST_DIGEST}));
assertTrue(harmfulCrcs.contains(0xaabbccdd));
assertFalse(harmfulCrcs.contains(0xbbbbbbbb));
assertFalse(harmfulCrcs.contains(0x01020304));
assertFalse(harmfulCrcs.contains(0xddccbbaa));
}
}