Cap number of NetworkRequests a UID can make to 100
This prevents DoSing ConnectivityService with too many requests.
Fixes: 27253080
Change-Id: Id0480d220b2f01b9ef1146bef8ead2fc8287e28d
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 40d44b4..6856d3f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2321,6 +2321,18 @@
if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request);
nri.unlinkDeathRecipient();
mNetworkRequests.remove(request);
+ synchronized (mUidToNetworkRequestCount) {
+ int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
+ if (requests < 1) {
+ Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " +
+ nri.mUid);
+ } else if (requests == 1) {
+ mUidToNetworkRequestCount.removeAt(
+ mUidToNetworkRequestCount.indexOfKey(nri.mUid));
+ } else {
+ mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
+ }
+ }
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.isRequest()) {
// Find all networks that are satisfying this request and remove the request
@@ -3668,6 +3680,11 @@
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
new HashMap<NetworkRequest, NetworkRequestInfo>();
+ private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
+ // Map from UID to number of NetworkRequests that UID has filed.
+ @GuardedBy("mUidToNetworkRequestCount")
+ private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
+
private static class NetworkFactoryInfo {
public final String name;
public final Messenger messenger;
@@ -3728,6 +3745,7 @@
mPid = getCallingPid();
mUid = getCallingUid();
mType = type;
+ enforceRequestCountLimit();
}
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, NetworkRequestType type) {
@@ -3739,6 +3757,7 @@
mUid = getCallingUid();
mType = type;
mPendingIntent = null;
+ enforceRequestCountLimit();
try {
mBinder.linkToDeath(this, 0);
@@ -3747,6 +3766,16 @@
}
}
+ private void enforceRequestCountLimit() {
+ synchronized (mUidToNetworkRequestCount) {
+ int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
+ if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) {
+ throw new IllegalArgumentException("Too many NetworkRequests filed");
+ }
+ mUidToNetworkRequestCount.put(mUid, networkRequests);
+ }
+ }
+
private String typeString() {
switch (mType) {
case LISTEN: return "Listen";
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 23f186c..7017d81 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -42,6 +42,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 26eed24..4fae4a7 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -74,6 +74,7 @@
import com.android.server.net.NetworkPinner;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -1788,4 +1789,96 @@
waitFor(cv);
assertPinnedToWifiWithCellDefault();
}
+
+ @SmallTest
+ public void testNetworkRequestMaximum() {
+ final int MAX_REQUESTS = 100;
+ // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
+ NetworkRequest networkRequest = new NetworkRequest.Builder().build();
+ ArrayList<NetworkCallback> networkCallbacks = new ArrayList<NetworkCallback>();
+ try {
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ NetworkCallback networkCallback = new NetworkCallback();
+ mCm.requestNetwork(networkRequest, networkCallback);
+ networkCallbacks.add(networkCallback);
+ }
+ fail("Registering " + MAX_REQUESTS + " NetworkRequests did not throw exception");
+ } catch (IllegalArgumentException expected) {}
+ for (NetworkCallback networkCallback : networkCallbacks) {
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+ networkCallbacks.clear();
+
+ try {
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ NetworkCallback networkCallback = new NetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+ networkCallbacks.add(networkCallback);
+ }
+ fail("Registering " + MAX_REQUESTS + " NetworkCallbacks did not throw exception");
+ } catch (IllegalArgumentException expected) {}
+ for (NetworkCallback networkCallback : networkCallbacks) {
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+ networkCallbacks.clear();
+
+ ArrayList<PendingIntent> pendingIntents = new ArrayList<PendingIntent>();
+ try {
+ for (int i = 0; i < MAX_REQUESTS + 1; i++) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
+ mCm.requestNetwork(networkRequest, pendingIntent);
+ pendingIntents.add(pendingIntent);
+ }
+ fail("Registering " + MAX_REQUESTS +
+ " PendingIntent NetworkRequests did not throw exception");
+ } catch (IllegalArgumentException expected) {}
+ for (PendingIntent pendingIntent : pendingIntents) {
+ mCm.unregisterNetworkCallback(pendingIntent);
+ }
+ pendingIntents.clear();
+
+ try {
+ for (int i = 0; i < MAX_REQUESTS + 1; i++) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0);
+ mCm.registerNetworkCallback(networkRequest, pendingIntent);
+ pendingIntents.add(pendingIntent);
+ }
+ fail("Registering " + MAX_REQUESTS +
+ " PendingIntent NetworkCallbacks did not throw exception");
+ } catch (IllegalArgumentException expected) {}
+ for (PendingIntent pendingIntent : pendingIntents) {
+ mCm.unregisterNetworkCallback(pendingIntent);
+ }
+ pendingIntents.clear();
+ mService.waitForIdle(5000);
+
+ // Test that the limit is not hit when MAX_REQUESTS requests are added and removed.
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ NetworkCallback networkCallback = new NetworkCallback();
+ mCm.requestNetwork(networkRequest, networkCallback);
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+ mService.waitForIdle();
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ NetworkCallback networkCallback = new NetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+ mService.waitForIdle();
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent("b" + i), 0);
+ mCm.requestNetwork(networkRequest, pendingIntent);
+ mCm.unregisterNetworkCallback(pendingIntent);
+ }
+ mService.waitForIdle();
+ for (int i = 0; i < MAX_REQUESTS; i++) {
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, new Intent("c" + i), 0);
+ mCm.registerNetworkCallback(networkRequest, pendingIntent);
+ mCm.unregisterNetworkCallback(pendingIntent);
+ }
+ }
}