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:
committed by
Lorenzo Colitti
parent
e9e251fcfb
commit
17586f7f1f
@@ -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.
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user