StrictMode to detect untagged network traffic.

Network usage is tracked by the kernel at the UID level, which is
granular enough for normal apps, but large components (such as the
system server) are impossible to debug without adding additional
socket tagging to help identify subsystems within a UID.

To help ensure that system components tag all their network traffic,
this change offers a new StrictMode option to detect and report
untagged sockets.

Test: builds, boots, all common traffic tagged
Bug: 30943431, 30414041
Change-Id: I825c7941076054732264690247de2863342638e2
This commit is contained in:
Jeff Sharkey
2017-01-19 11:55:54 -07:00
parent 063c454b07
commit 619a511d4f
13 changed files with 115 additions and 12 deletions

View File

@@ -24481,6 +24481,7 @@ package android.net {
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
method public static int getAndSetThreadStatsTag(int);
method public static long getMobileRxBytes();
method public static long getMobileRxPackets();
method public static long getMobileTxBytes();
@@ -30062,6 +30063,7 @@ package android.os {
method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();

View File

@@ -26460,6 +26460,7 @@ package android.net {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
method public static void clearThreadStatsUid();
method public static int getAndSetThreadStatsTag(int);
method public static long getMobileRxBytes();
method public static long getMobileRxPackets();
method public static long getMobileTxBytes();
@@ -32706,6 +32707,7 @@ package android.os {
method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();

View File

@@ -24571,6 +24571,7 @@ package android.net {
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
method public static int getAndSetThreadStatsTag(int);
method public static long getMobileRxBytes();
method public static long getMobileRxPackets();
method public static long getMobileTxBytes();
@@ -30174,6 +30175,7 @@ package android.os {
method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();

View File

@@ -96,6 +96,7 @@ public class SntpClient {
public boolean requestTime(InetAddress address, int port, int timeout) {
DatagramSocket socket = null;
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NTP);
try {
socket = new DatagramSocket();
socket.setSoTimeout(timeout);
@@ -161,6 +162,7 @@ public class SntpClient {
if (socket != null) {
socket.close();
}
TrafficStats.setThreadStatsTag(oldTag);
}
return true;

View File

@@ -166,6 +166,24 @@ public class TrafficStats {
NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
}
/**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. Only one active tag per thread is supported.
* <p>
* Changes only take effect during subsequent calls to
* {@link #tagSocket(Socket)}.
* <p>
* Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
* used internally by system services like {@link DownloadManager} when
* performing traffic on behalf of an application.
*
* @return the current tag for the calling thread, which can be used to
* restore any existing values after a nested operation is finished
*/
public static int getAndSetThreadStatsTag(int tag) {
return NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
}
/**
* Set active tag to use when accounting {@link Socket} traffic originating
* from the current thread. The tag used internally is well-defined to

View File

@@ -24,6 +24,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.TrafficStats;
import android.net.Uri;
import android.util.ArrayMap;
import android.util.Log;
@@ -245,11 +246,17 @@ public final class StrictMode {
*/
private static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 0x80 << 8; // for VmPolicy
/**
* @hide
*/
private static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
private static final int ALL_VM_DETECT_BITS =
DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS |
DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS |
DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE |
DETECT_VM_CLEARTEXT_NETWORK | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
DETECT_VM_CLEARTEXT_NETWORK | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION |
DETECT_VM_UNTAGGED_SOCKET;
// Byte 3: Penalty
@@ -300,6 +307,8 @@ public final class StrictMode {
*/
public static final int PENALTY_DEATH_ON_FILE_URI_EXPOSURE = 0x04 << 24;
// CAUTION: we started stealing the top bits of Byte 4 for VM above
/**
* Mask of all the penalty bits valid for thread policies.
*/
@@ -715,7 +724,8 @@ public final class StrictMode {
public Builder detectAll() {
int flags = DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS
| DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS
| DETECT_VM_FILE_URI_EXPOSURE | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
| DETECT_VM_FILE_URI_EXPOSURE | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
| DETECT_VM_UNTAGGED_SOCKET;
// TODO: always add DETECT_VM_CLEARTEXT_NETWORK once we have facility
// for apps to mark sockets that should be ignored
@@ -819,6 +829,22 @@ public final class StrictMode {
return enable(DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION);
}
/**
* Detect any sockets in the calling app which have not been tagged
* using {@link TrafficStats}. Tagging sockets can help you
* investigate network usage inside your app, such as a narrowing
* down heavy usage to a specific library or component.
* <p>
* This currently does not detect sockets created in native code.
*
* @see TrafficStats#setThreadStatsTag(int)
* @see TrafficStats#tagSocket(java.net.Socket)
* @see TrafficStats#tagDatagramSocket(java.net.DatagramSocket)
*/
public Builder detectUntaggedSockets() {
return enable(DETECT_VM_UNTAGGED_SOCKET);
}
/**
* Crashes the whole process on violation. This penalty runs at the
* end of all enabled penalties so you'll still get your logging or
@@ -1152,6 +1178,11 @@ public final class StrictMode {
if (IS_ENG_BUILD) {
policyBuilder.penaltyLog();
}
// All core system components need to tag their sockets to aid
// system health investigations
if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) {
policyBuilder.detectUntaggedSockets();
}
setVmPolicy(policyBuilder.build());
setCloseGuardEnabled(vmClosableObjectLeaksEnabled());
}
@@ -1829,6 +1860,13 @@ public final class StrictMode {
return (sVmPolicyMask & DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION) != 0;
}
/**
* @hide
*/
public static boolean vmUntaggedSocketEnabled() {
return (sVmPolicyMask & DETECT_VM_UNTAGGED_SOCKET) != 0;
}
/**
* @hide
*/
@@ -1911,6 +1949,14 @@ public final class StrictMode {
forceDeath);
}
/**
* @hide
*/
public static void onUntaggedSocket() {
onVmPolicyViolation(null, new Throwable("Untagged socket detected; use"
+ " TrafficStats.setThreadSocketTag() to track all network usage"));
}
// Map from VM violation fingerprint to uptime millis.
private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();

View File

@@ -16,6 +16,7 @@
package com.android.server;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
@@ -50,16 +51,20 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
SocketTagger.set(new NetworkManagementSocketTagger());
}
public static void setThreadSocketStatsTag(int tag) {
public static int setThreadSocketStatsTag(int tag) {
final int old = threadSocketTags.get().statsTag;
threadSocketTags.get().statsTag = tag;
return old;
}
public static int getThreadSocketStatsTag() {
return threadSocketTags.get().statsTag;
}
public static void setThreadSocketStatsUid(int uid) {
public static int setThreadSocketStatsUid(int uid) {
final int old = threadSocketTags.get().statsUid;
threadSocketTags.get().statsUid = uid;
return old;
}
@Override
@@ -69,6 +74,9 @@ public final class NetworkManagementSocketTagger extends SocketTagger {
Log.d(TAG, "tagSocket(" + fd.getInt$() + ") with statsTag=0x"
+ Integer.toHexString(options.statsTag) + ", statsUid=" + options.statsUid);
}
if (options.statsTag == -1 && StrictMode.vmUntaggedSocketEnabled()) {
StrictMode.onUntaggedSocket();
}
// TODO: skip tagging when options would be no-op
tagSocketFd(fd, options.statsTag, options.statsUid);
}

View File

@@ -23,6 +23,7 @@ import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.TrafficStats;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
@@ -381,7 +382,12 @@ public class NetworkDiagnostics {
protected void setupSocket(
int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
throws ErrnoException, IOException {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
} finally {
TrafficStats.setThreadStatsTag(oldTag);
}
// Setting SNDTIMEO is purely for defensive purposes.
Os.setsockoptTimeval(mFileDescriptor,
SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));

View File

@@ -779,6 +779,7 @@ public class NetworkMonitor extends StateMachine {
int httpResponseCode = 599;
String redirectUrl = null;
final Stopwatch probeTimer = new Stopwatch().start();
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
@@ -839,6 +840,7 @@ public class NetworkMonitor extends StateMachine {
if (urlConnection != null) {
urlConnection.disconnect();
}
TrafficStats.setThreadStatsTag(oldTag);
}
logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());

View File

@@ -25,6 +25,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -103,11 +104,15 @@ public class PacManager {
String file;
synchronized (mProxyLock) {
if (Uri.EMPTY.equals(mPacUrl)) return;
final int oldTag = TrafficStats
.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PAC);
try {
file = get(mPacUrl);
} catch (IOException ioe) {
file = null;
Log.w(TAG, "Failed to load PAC file: " + ioe);
} finally {
TrafficStats.setThreadStatsTag(oldTag);
}
}
if (file != null) {

View File

@@ -16,22 +16,19 @@
package com.android.server.location;
import android.net.TrafficStats;
import android.text.TextUtils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import libcore.io.Streams;
/**
* A class for downloading GPS XTRA data.
*
@@ -94,7 +91,12 @@ public class GpsXtraDownloader {
// load balance our requests among the available servers
while (result == null) {
result = doDownload(mXtraServers[mNextServerIndex]);
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_GPS);
try {
result = doDownload(mXtraServers[mNextServerIndex]);
} finally {
TrafficStats.setThreadStatsTag(oldTag);
}
// increment mNextServerIndex and wrap around if necessary
mNextServerIndex++;

View File

@@ -30,6 +30,7 @@ import android.net.DhcpResults;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
@@ -303,6 +304,7 @@ public class DhcpClient extends StateMachine {
}
private boolean initUdpSocket() {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_DHCP);
try {
mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
@@ -314,6 +316,8 @@ public class DhcpClient extends StateMachine {
} catch(SocketException|ErrnoException e) {
Log.e(TAG, "Error creating UDP socket", e);
return false;
} finally {
TrafficStats.setThreadStatsTag(oldTag);
}
return true;
}

View File

@@ -22,6 +22,7 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructGroupReq;
@@ -563,6 +564,7 @@ public class RouterAdvertisementDaemon {
private boolean createSocket() {
final int SEND_TIMEOUT_MS = 300;
final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_NEIGHBOR);
try {
mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
// Setting SNDTIMEO is purely for defensive purposes.
@@ -574,6 +576,8 @@ public class RouterAdvertisementDaemon {
} catch (ErrnoException | IOException e) {
Log.e(TAG, "Failed to create RA daemon socket: " + e);
return false;
} finally {
TrafficStats.setThreadStatsTag(oldTag);
}
return true;