DO NOT MERGE: Add fuzzing tests to ApfFilter RA processing

Test: added new unit tests

(cherry picked from commit 8acea76a2b)

Change-Id: I4e6633984075d87c4e22466bc881629436359f7f
This commit is contained in:
Hugo Benichi
2016-10-06 15:19:36 +09:00
committed by Lorenzo Colitti
parent e9e251fcfb
commit 17586f7f1f
2 changed files with 60 additions and 14 deletions

View File

@@ -291,8 +291,15 @@ public class ApfFilter {
return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS; return System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS;
} }
public static class InvalidRaException extends Exception {
public InvalidRaException(String m) {
super(m);
}
}
// A class to hold information about an RA. // A class to hold information about an RA.
private class Ra { @VisibleForTesting
class Ra {
// From RFC4861: // From RFC4861:
private static final int ICMP6_RA_HEADER_LEN = 16; private static final int ICMP6_RA_HEADER_LEN = 16;
private static final int ICMP6_RA_CHECKSUM_OFFSET = private static final int ICMP6_RA_CHECKSUM_OFFSET =
@@ -364,7 +371,7 @@ public class ApfFilter {
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// array() failed. Cannot happen, mPacket is array-backed and read-write. // array() failed. Cannot happen, mPacket is array-backed and read-write.
return "???"; return "???";
} catch (ClassCastException | UnknownHostException e) { } catch (ClassCastException|UnknownHostException e) {
// Cannot happen. // Cannot happen.
return "???"; return "???";
} }
@@ -405,7 +412,7 @@ public class ApfFilter {
rdnssOptionToString(sb, i); rdnssOptionToString(sb, i);
} }
return sb.toString(); return sb.toString();
} catch (BufferUnderflowException | IndexOutOfBoundsException e) { } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
return "<Malformed RA>"; return "<Malformed RA>";
} }
} }
@@ -438,7 +445,11 @@ public class ApfFilter {
// Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
// (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
// specifications. // specifications.
Ra(byte[] packet, int length) { Ra(byte[] packet, int length) throws InvalidRaException {
if (length < ICMP6_RA_OPTION_OFFSET) {
throw new InvalidRaException("Not an ICMP6 router advertisement");
}
mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length)); mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
mLastSeen = curTime(); mLastSeen = curTime();
@@ -447,7 +458,7 @@ public class ApfFilter {
if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 || if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
uint8(mPacket.get(IPV6_NEXT_HEADER_OFFSET)) != IPPROTO_ICMPV6 || uint8(mPacket.get(IPV6_NEXT_HEADER_OFFSET)) != IPPROTO_ICMPV6 ||
uint8(mPacket.get(ICMP6_TYPE_OFFSET)) != ICMP6_ROUTER_ADVERTISEMENT) { uint8(mPacket.get(ICMP6_TYPE_OFFSET)) != ICMP6_ROUTER_ADVERTISEMENT) {
throw new IllegalArgumentException("Not an ICMP6 router advertisement"); throw new InvalidRaException("Not an ICMP6 router advertisement");
} }
@@ -513,7 +524,7 @@ public class ApfFilter {
break; break;
} }
if (optionLength <= 0) { if (optionLength <= 0) {
throw new IllegalArgumentException(String.format( throw new InvalidRaException(String.format(
"Invalid option length opt=%d len=%d", optionType, optionLength)); "Invalid option length opt=%d len=%d", optionType, optionLength));
} }
mPacket.position(position + optionLength); mPacket.position(position + optionLength);
@@ -932,8 +943,8 @@ public class ApfFilter {
// Execution will reach the end of the program if no filters match, which will pass the // Execution will reach the end of the program if no filters match, which will pass the
// packet to the AP. // packet to the AP.
program = gen.generate(); program = gen.generate();
} catch (IllegalInstructionException e) { } catch (IllegalInstructionException|IllegalStateException e) {
Log.e(TAG, "Program failed to generate: ", e); Log.e(TAG, "Failed to generate APF program.", e);
return; return;
} }
mLastTimeInstalledProgram = curTime(); mLastTimeInstalledProgram = curTime();
@@ -979,7 +990,8 @@ public class ApfFilter {
* if the current APF program should be updated. * if the current APF program should be updated.
* @return a ProcessRaResult enum describing what action was performed. * @return a ProcessRaResult enum describing what action was performed.
*/ */
private synchronized ProcessRaResult processRa(byte[] packet, int length) { @VisibleForTesting
synchronized ProcessRaResult processRa(byte[] packet, int length) {
if (VDBG) hexDump("Read packet = ", packet, length); if (VDBG) hexDump("Read packet = ", packet, length);
// Have we seen this RA before? // Have we seen this RA before?
@@ -1018,7 +1030,7 @@ public class ApfFilter {
try { try {
ra = new Ra(packet, length); ra = new Ra(packet, length);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Error parsing RA: " + e); Log.e(TAG, "Error parsing RA", e);
return ProcessRaResult.PARSE_ERROR; return ProcessRaResult.PARSE_ERROR;
} }
// Ignore 0 lifetime RAs. // Ignore 0 lifetime RAs.

View File

@@ -16,10 +16,6 @@
package android.net.apf; package android.net.apf;
import static android.system.OsConstants.*;
import com.android.frameworks.servicestests.R;
import android.net.LinkAddress; import android.net.LinkAddress;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.NetworkUtils; import android.net.NetworkUtils;
@@ -37,6 +33,10 @@ import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.LargeTest;
import static android.system.OsConstants.*;
import com.android.frameworks.servicestests.R;
import com.android.internal.util.HexDump;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
@@ -54,6 +54,7 @@ import java.net.InetAddress;
import java.net.NetworkInterface; import java.net.NetworkInterface;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
import java.util.Random;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import libcore.io.Streams; import libcore.io.Streams;
@@ -1156,6 +1157,39 @@ public class ApfTest extends AndroidTestCase {
buffer.position(original); buffer.position(original);
} }
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
try {
apfFilter.new Ra(packet, packet.length);
} catch (ApfFilter.InvalidRaException e) {
} catch (Exception e) {
throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
}
}
}
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
try {
apfFilter.processRa(packet, packet.length);
} catch (Exception e) {
throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
}
}
}
/** /**
* Call the APF interpreter the run {@code program} on {@code packet} pretending the * Call the APF interpreter the run {@code program} on {@code packet} pretending the
* filter was installed {@code filter_age} seconds ago. * filter was installed {@code filter_age} seconds ago.