Add VmPolicy to monitor path access.
An upcoming StrictMode feature is going to help detect app bugs
where they accidentally interact with credential-protected
filesystem paths while a user is encrypted.
Since much of the existing BlockGuard and BlockGuard.Policy
interface is already on the API greylist, we carefully add a new
parallel BlockGuard.VmPolicy object and start dispatching path
access events to it. A future StrictMode CL will inspect paths
reported through this interface to report any violations.
Bug: 110413274
Test: atest cts/tests/tests/os/src/android/os/cts/StrictModeTest.java
Change-Id: I98bb9cb428336710566b3a8c81b6332ef981ecfe
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
index caf0470..e3cf529 100644
--- a/dalvik/src/main/java/dalvik/system/BlockGuard.java
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -16,23 +16,20 @@
package dalvik.system;
-import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.SocketException;
+import libcore.util.NonNull;
+
+import java.util.Objects;
/**
- * Mechanism to let threads set restrictions on what code is allowed
- * to do in their thread.
- *
- * <p>This is meant for applications to prevent certain blocking
- * operations from running on their main event loop (or "UI") threads.
- *
- * <p>Note that this is all best-effort to catch most accidental mistakes
- * and isn't intended to be a perfect mechanism, nor provide any sort of
- * security.
+ * Interface that enables {@code StrictMode} to install callbacks to implement
+ * its policy detection and penalty behavior in {@code libcore} code.
+ * <p>
+ * The framework separately defines {@code StrictMode.ThreadPolicy} and
+ * {@code StrictMode.VmPolicy}, so we mirror that separation here; the former is
+ * designed for per-thread policies, and the latter for process-wide policies.
+ * <p>
+ * Note that this is all best-effort to catch most accidental mistakes and isn't
+ * intended to be a perfect mechanism, nor provide any sort of security.
*
* @hide
*/
@@ -41,14 +38,9 @@
// TODO: refactor class name to something more generic, since its scope is
// growing beyond just blocking/logging.
- public static final int DISALLOW_DISK_WRITE = 0x01;
- public static final int DISALLOW_DISK_READ = 0x02;
- public static final int DISALLOW_NETWORK = 0x04;
- public static final int PASS_RESTRICTIONS_VIA_RPC = 0x08;
- public static final int PENALTY_LOG = 0x10;
- public static final int PENALTY_DIALOG = 0x20;
- public static final int PENALTY_DEATH = 0x40;
-
+ /**
+ * Per-thread interface used to implement {@code StrictMode.ThreadPolicy}.
+ */
public interface Policy {
/**
* Called on disk writes.
@@ -84,6 +76,36 @@
int getPolicyMask();
}
+ /**
+ * Per-process interface used to implement {@code StrictMode.VmPolicy}.
+ */
+ public interface VmPolicy {
+ /**
+ * Called by core libraries code when the given path is accessed. This
+ * allows an implementation to alert developers to unexpected path
+ * access, such as trying to access encrypted files before the
+ * encryption key has been installed.
+ * <p>
+ * This only needs to be called once when a path is first accessed by
+ * the process; it doesn't need to be invoked for each subsequent
+ * read/write. (In contrast, we always need to call the per-thread
+ * policy for every read/write, since ownership of an open file can move
+ * between threads.)
+ * <p>
+ * Note that this is all best-effort to catch most accidental mistakes
+ * and isn't intended to be a perfect mechanism, nor provide any sort of
+ * security.
+ *
+ * @param path The path in the local file system that is being accessed
+ * for reading or writing.
+ */
+ void onPathAccess(String path);
+ }
+
+ /**
+ * @deprecated no longer actively used, but kept intact for greylist.
+ */
+ @Deprecated
public static class BlockGuardPolicyException extends RuntimeException {
// bitmask of DISALLOW_*, PENALTY_*, etc flags
private final int mPolicyState;
@@ -122,18 +144,27 @@
}
/**
- * The default, permissive policy that doesn't prevent any operations.
+ * The default, permissive per-thread policy.
*/
public static final Policy LAX_POLICY = new Policy() {
- public void onWriteToDisk() {}
- public void onReadFromDisk() {}
- public void onNetwork() {}
- public void onUnbufferedIO() {}
- public void onExplicitGc() {}
- public int getPolicyMask() {
- return 0;
- }
- };
+ @Override public void onWriteToDisk() {}
+ @Override public void onReadFromDisk() {}
+ @Override public void onNetwork() {}
+ @Override public void onUnbufferedIO() {}
+ @Override public void onExplicitGc() {}
+
+ @Override
+ public int getPolicyMask() {
+ return 0;
+ }
+ };
+
+ /**
+ * The default, permissive per-process policy.
+ */
+ public static final VmPolicy LAX_VM_POLICY = new VmPolicy() {
+ @Override public void onPathAccess(String path) {}
+ };
private static ThreadLocal<Policy> threadPolicy = new ThreadLocal<Policy>() {
@Override protected Policy initialValue() {
@@ -141,27 +172,52 @@
}
};
+ private static volatile VmPolicy vmPolicy = LAX_VM_POLICY;
+
/**
- * Get the current thread's policy.
+ * Get the per-thread policy for the current thread.
*
- * @return the current thread's policy. Never returns null.
- * Will return the LAX_POLICY instance if nothing else is set.
+ * @return the current thread's policy. Will return the {@link #LAX_POLICY}
+ * instance if nothing else is set.
*/
- public static Policy getThreadPolicy() {
+ public static @NonNull Policy getThreadPolicy() {
return threadPolicy.get();
}
/**
- * Sets the current thread's block guard policy.
+ * Sets the per-thread policy for the current thread.
+ * <p>
+ * This should only be called by {@code StrictMode}, since there can only be
+ * one policy active at any given time.
*
- * @param policy policy to set. May not be null. Use the public LAX_POLICY
- * if you want to unset the active policy.
+ * @param policy policy to set. Use the public {@link #LAX_POLICY} if you
+ * want to unset the active policy.
*/
- public static void setThreadPolicy(Policy policy) {
- if (policy == null) {
- throw new NullPointerException("policy == null");
- }
- threadPolicy.set(policy);
+ public static void setThreadPolicy(@NonNull Policy policy) {
+ threadPolicy.set(Objects.requireNonNull(policy));
+ }
+
+ /**
+ * Get the per-process policy for the current process.
+ *
+ * @return the current process's policy. Will return the
+ * {@link #LAX_VM_POLICY} instance if nothing else is set.
+ */
+ public static @NonNull VmPolicy getVmPolicy() {
+ return vmPolicy;
+ }
+
+ /**
+ * Set the per-process policy for the current process.
+ * <p>
+ * This should only be called by {@code StrictMode}, since there can only be
+ * one policy active at any given time.
+ *
+ * @param policy policy to set. Use the public {@link #LAX_VM_POLICY} if you
+ * want to unset the active policy.
+ */
+ public static void setVmPolicy(@NonNull VmPolicy policy) {
+ vmPolicy = Objects.requireNonNull(policy);
}
private BlockGuard() {}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 9b1ebe9..ac10da6 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -65,16 +65,19 @@
@Override public boolean access(String path, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.access(path, mode);
}
@Override public void chmod(String path, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.chmod(path, mode);
}
@Override public void chown(String path, int uid, int gid) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.chown(path, uid, gid);
}
@@ -174,11 +177,14 @@
@Override public void lchown(String path, int uid, int gid) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.lchown(path, uid, gid);
}
@Override public void link(String oldPath, String newPath) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(oldPath);
+ BlockGuard.getVmPolicy().onPathAccess(newPath);
os.link(oldPath, newPath);
}
@@ -189,21 +195,25 @@
@Override public StructStat lstat(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.lstat(path);
}
@Override public void mkdir(String path, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.mkdir(path, mode);
}
@Override public void mkfifo(String path, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.mkfifo(path, mode);
}
@Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
if ((flags & O_ACCMODE) != O_RDONLY) {
BlockGuard.getThreadPolicy().onWriteToDisk();
}
@@ -256,11 +266,13 @@
@Override public String readlink(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.readlink(path);
}
@Override public String realpath(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.realpath(path);
}
@@ -281,11 +293,14 @@
@Override public void remove(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.remove(path);
}
@Override public void rename(String oldPath, String newPath) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(oldPath);
+ BlockGuard.getVmPolicy().onPathAccess(newPath);
os.rename(oldPath, newPath);
}
@@ -325,16 +340,20 @@
@Override public StructStat stat(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.stat(path);
}
@Override public StructStatVfs statvfs(String path) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.statvfs(path);
}
@Override public void symlink(String oldPath, String newPath) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(oldPath);
+ BlockGuard.getVmPolicy().onPathAccess(newPath);
os.symlink(oldPath, newPath);
}
@@ -355,17 +374,20 @@
@Override public void execv(String filename, String[] argv) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(filename);
os.execv(filename, argv);
}
@Override public void execve(String filename, String[] argv, String[] envp)
throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(filename);
os.execve(filename, argv, envp);
}
@Override public byte[] getxattr(String path, String name) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return os.getxattr(path, name);
}
@@ -378,12 +400,14 @@
@Override public void removexattr(String path, String name) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.removexattr(path, name);
}
@Override public void setxattr(String path, String name, byte[] value, int flags)
throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
os.setxattr(path, name, value, flags);
}
@@ -395,6 +419,7 @@
@Override public void unlink(String pathname) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(pathname);
os.unlink(pathname);
}
diff --git a/ojluni/src/main/java/java/io/FileInputStream.java b/ojluni/src/main/java/java/io/FileInputStream.java
index db7ba65..8df5f4d 100755
--- a/ojluni/src/main/java/java/io/FileInputStream.java
+++ b/ojluni/src/main/java/java/io/FileInputStream.java
@@ -159,8 +159,10 @@
path = name;
- // Android-added: BlockGuard support.
+ // BEGIN Android-added: BlockGuard support.
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
+ // END Android-added: BlockGuard support.
open(name);
diff --git a/ojluni/src/main/java/java/io/UnixFileSystem.java b/ojluni/src/main/java/java/io/UnixFileSystem.java
index 1659b17..223c0fb 100644
--- a/ojluni/src/main/java/java/io/UnixFileSystem.java
+++ b/ojluni/src/main/java/java/io/UnixFileSystem.java
@@ -166,7 +166,10 @@
}
}
if (res == null) {
+ // BEGIN Android-added: BlockGuard support.
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
+ // END Android-added: BlockGuard support.
res = canonicalize0(path);
cache.put(path, res);
if (useCanonPrefixCache &&
@@ -236,9 +239,11 @@
private native int getBooleanAttributes0(String abspath);
- // Android-changed: Added thread policy check
public int getBooleanAttributes(File f) {
+ // BEGIN Android-added: BlockGuard support.
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
+ // END Android-added: BlockGuard support.
int rv = getBooleanAttributes0(f.getPath());
String name = f.getName();
@@ -246,43 +251,47 @@
return rv | (hidden ? BA_HIDDEN : 0);
}
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean checkAccess(File f, int access) {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return checkAccess0(f, access);
}
private native boolean checkAccess0(File f, int access);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public long getLastModifiedTime(File f) {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return getLastModifiedTime0(f);
}
private native long getLastModifiedTime0(File f);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public long getLength(File f) {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return getLength0(f);
}
private native long getLength0(File f);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean setPermission(File f, int access, boolean enable, boolean owneronly) {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return setPermission0(f, access, enable, owneronly);
}
private native boolean setPermission0(File f, int access, boolean enable, boolean owneronly);
/* -- File operations -- */
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean createFileExclusively(String path) throws IOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(path);
return createFileExclusively0(path);
}
private native boolean createFileExclusively0(String path) throws IOException;
- // Android-changed: Added thread policy check
public boolean delete(File f) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
@@ -291,27 +300,31 @@
// anyway.
cache.clear();
javaHomePrefixCache.clear();
+ // BEGIN Android-added: BlockGuard support.
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
+ // END Android-added: BlockGuard support.
return delete0(f);
}
private native boolean delete0(File f);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public String[] list(File f) {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return list0(f);
}
private native String[] list0(File f);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean createDirectory(File f) {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return createDirectory0(f);
}
private native boolean createDirectory0(File f);
- // Android-changed: Added thread policy check
public boolean rename(File f1, File f2) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
@@ -320,22 +333,28 @@
// anyway.
cache.clear();
javaHomePrefixCache.clear();
+ // BEGIN Android-added: BlockGuard support.
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f1.getPath());
+ BlockGuard.getVmPolicy().onPathAccess(f2.getPath());
+ // END Android-added: BlockGuard support.
return rename0(f1, f2);
}
private native boolean rename0(File f1, File f2);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean setLastModifiedTime(File f, long time) {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return setLastModifiedTime0(f, time);
}
private native boolean setLastModifiedTime0(File f, long time);
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public boolean setReadOnly(File f) {
BlockGuard.getThreadPolicy().onWriteToDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return setReadOnly0(f);
}
private native boolean setReadOnly0(File f);
@@ -356,9 +375,10 @@
}
/* -- Disk usage -- */
- // Android-changed: Added thread policy check
+ // Android-changed: BlockGuard support.
public long getSpace(File f, int t) {
BlockGuard.getThreadPolicy().onReadFromDisk();
+ BlockGuard.getVmPolicy().onPathAccess(f.getPath());
return getSpace0(f, t);
}