Merge "More StrictMode work, keeping Binder & BlockGuard's thread-locals in-sync." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
c4b204bdc4
@ -102,6 +102,29 @@ public class Binder implements IBinder {
|
|||||||
*/
|
*/
|
||||||
public static final native void restoreCallingIdentity(long token);
|
public static final native void restoreCallingIdentity(long token);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the native thread-local StrictMode policy mask.
|
||||||
|
*
|
||||||
|
* <p>The StrictMode settings are kept in two places: a Java-level
|
||||||
|
* threadlocal for libcore/Dalvik, and a native threadlocal (set
|
||||||
|
* here) for propagation via Binder calls. This is a little
|
||||||
|
* unfortunate, but necessary to break otherwise more unfortunate
|
||||||
|
* dependencies either of Dalvik on Android, or Android
|
||||||
|
* native-only code on Dalvik.
|
||||||
|
*
|
||||||
|
* @see StrictMode
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final native void setThreadStrictModePolicy(int policyMask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current native thread-local StrictMode policy mask.
|
||||||
|
*
|
||||||
|
* @see #setThreadStrictModePolicy
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final native int getThreadStrictModePolicy();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush any Binder commands pending in the current thread to the kernel
|
* Flush any Binder commands pending in the current thread to the kernel
|
||||||
* driver. This can be
|
* driver. This can be
|
||||||
|
@ -69,6 +69,17 @@ public final class StrictMode {
|
|||||||
*/
|
*/
|
||||||
public static final int PENALTY_DROPBOX = 0x80;
|
public static final int PENALTY_DROPBOX = 0x80;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non-public penalty mode which overrides all the other penalty
|
||||||
|
* bits and signals that we're in a Binder call and we should
|
||||||
|
* ignore the other penalty bits and instead serialize back all
|
||||||
|
* our offending stack traces to the caller to ultimately handle
|
||||||
|
* in the originating process.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public static final int PENALTY_GATHER = 0x100;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public static final int PENALTY_MASK =
|
public static final int PENALTY_MASK =
|
||||||
PENALTY_LOG | PENALTY_DIALOG |
|
PENALTY_LOG | PENALTY_DIALOG |
|
||||||
@ -81,6 +92,19 @@ public final class StrictMode {
|
|||||||
* @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
|
* @param policyMask a bitmask of DISALLOW_* and PENALTY_* values.
|
||||||
*/
|
*/
|
||||||
public static void setThreadBlockingPolicy(final int policyMask) {
|
public static void setThreadBlockingPolicy(final int policyMask) {
|
||||||
|
// In addition to the Java-level thread-local in Dalvik's
|
||||||
|
// BlockGuard, we also need to keep a native thread-local in
|
||||||
|
// Binder in order to propagate the value across Binder calls,
|
||||||
|
// even across native-only processes. The two are kept in
|
||||||
|
// sync via the callback to onStrictModePolicyChange, below.
|
||||||
|
setBlockGuardPolicy(policyMask);
|
||||||
|
|
||||||
|
// And set the Android native version...
|
||||||
|
Binder.setThreadStrictModePolicy(policyMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the policy in Dalvik/libcore (BlockGuard)
|
||||||
|
private static void setBlockGuardPolicy(final int policyMask) {
|
||||||
if (policyMask == 0) {
|
if (policyMask == 0) {
|
||||||
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
|
BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
|
||||||
return;
|
return;
|
||||||
@ -189,6 +213,11 @@ public final class StrictMode {
|
|||||||
new ApplicationErrorReport.CrashInfo(violation);
|
new ApplicationErrorReport.CrashInfo(violation);
|
||||||
crashInfo.durationMillis = durationMillis;
|
crashInfo.durationMillis = durationMillis;
|
||||||
|
|
||||||
|
if ((policy & PENALTY_GATHER) != 0) {
|
||||||
|
Log.d(TAG, "StrictMode violation via Binder call; ignoring for now.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Not perfect, but fast and good enough for dup suppression.
|
// Not perfect, but fast and good enough for dup suppression.
|
||||||
Integer crashFingerprint = crashInfo.stackTrace.hashCode();
|
Integer crashFingerprint = crashInfo.stackTrace.hashCode();
|
||||||
long lastViolationTime = 0;
|
long lastViolationTime = 0;
|
||||||
@ -227,13 +256,23 @@ public final class StrictMode {
|
|||||||
|
|
||||||
if (violationMask != 0) {
|
if (violationMask != 0) {
|
||||||
violationMask |= violation.getPolicyViolation();
|
violationMask |= violation.getPolicyViolation();
|
||||||
|
final int savedPolicy = getThreadBlockingPolicy();
|
||||||
try {
|
try {
|
||||||
|
// First, remove any policy before we call into the Activity Manager,
|
||||||
|
// otherwise we'll infinite recurse as we try to log policy violations
|
||||||
|
// to disk, thus violating policy, thus requiring logging, etc...
|
||||||
|
// We restore the current policy below, in the finally block.
|
||||||
|
setThreadBlockingPolicy(0);
|
||||||
|
|
||||||
ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
|
ActivityManagerNative.getDefault().handleApplicationStrictModeViolation(
|
||||||
RuntimeInit.getApplicationObject(),
|
RuntimeInit.getApplicationObject(),
|
||||||
violationMask,
|
violationMask,
|
||||||
new ApplicationErrorReport.CrashInfo(violation));
|
new ApplicationErrorReport.CrashInfo(violation));
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
|
Log.e(TAG, "RemoteException trying to handle StrictMode violation", e);
|
||||||
|
} finally {
|
||||||
|
// Restore the policy.
|
||||||
|
setThreadBlockingPolicy(savedPolicy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,4 +283,16 @@ public final class StrictMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from android_util_Binder.cpp's
|
||||||
|
* android_os_Parcel_enforceInterface when an incoming Binder call
|
||||||
|
* requires changing the StrictMode policy mask. The role of this
|
||||||
|
* function is to ask Binder for its current (native) thread-local
|
||||||
|
* policy value and synchronize it to libcore's (Java)
|
||||||
|
* thread-local policy value.
|
||||||
|
*/
|
||||||
|
private static void onBinderStrictModePolicyChange(int newPolicy) {
|
||||||
|
setBlockGuardPolicy(newPolicy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,12 @@ static struct parcel_file_descriptor_offsets_t
|
|||||||
jmethodID mConstructor;
|
jmethodID mConstructor;
|
||||||
} gParcelFileDescriptorOffsets;
|
} gParcelFileDescriptorOffsets;
|
||||||
|
|
||||||
|
static struct strict_mode_callback_offsets_t
|
||||||
|
{
|
||||||
|
jclass mClass;
|
||||||
|
jmethodID mCallback;
|
||||||
|
} gStrictModeCallbackOffsets;
|
||||||
|
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
@ -214,6 +220,15 @@ bail:
|
|||||||
env->DeleteLocalRef(msgstr);
|
env->DeleteLocalRef(msgstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void set_dalvik_blockguard_policy(JNIEnv* env, jint strict_policy)
|
||||||
|
{
|
||||||
|
// Call back into android.os.StrictMode#onBinderStrictModePolicyChange
|
||||||
|
// to sync our state back to it. See the comments in StrictMode.java.
|
||||||
|
env->CallStaticVoidMethod(gStrictModeCallbackOffsets.mClass,
|
||||||
|
gStrictModeCallbackOffsets.mCallback,
|
||||||
|
strict_policy);
|
||||||
|
}
|
||||||
|
|
||||||
class JavaBBinderHolder;
|
class JavaBBinderHolder;
|
||||||
|
|
||||||
class JavaBBinder : public BBinder
|
class JavaBBinder : public BBinder
|
||||||
@ -253,12 +268,28 @@ protected:
|
|||||||
|
|
||||||
LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
|
LOGV("onTransact() on %p calling object %p in env %p vm %p\n", this, mObject, env, mVM);
|
||||||
|
|
||||||
|
IPCThreadState* thread_state = IPCThreadState::self();
|
||||||
|
const int strict_policy_before = thread_state->getStrictModePolicy();
|
||||||
|
|
||||||
//printf("Transact from %p to Java code sending: ", this);
|
//printf("Transact from %p to Java code sending: ", this);
|
||||||
//data.print();
|
//data.print();
|
||||||
//printf("\n");
|
//printf("\n");
|
||||||
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
|
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
|
||||||
code, (int32_t)&data, (int32_t)reply, flags);
|
code, (int32_t)&data, (int32_t)reply, flags);
|
||||||
jthrowable excep = env->ExceptionOccurred();
|
jthrowable excep = env->ExceptionOccurred();
|
||||||
|
|
||||||
|
// Restore the Java binder thread's state if it changed while
|
||||||
|
// processing a call (as it would if the Parcel's header had a
|
||||||
|
// new policy mask and Parcel.enforceInterface() changed
|
||||||
|
// it...)
|
||||||
|
const int strict_policy_after = thread_state->getStrictModePolicy();
|
||||||
|
if (strict_policy_after != strict_policy_before) {
|
||||||
|
// Our thread-local...
|
||||||
|
thread_state->setStrictModePolicy(strict_policy_before);
|
||||||
|
// And the Java-level thread-local...
|
||||||
|
set_dalvik_blockguard_policy(env, strict_policy_before);
|
||||||
|
}
|
||||||
|
|
||||||
if (excep) {
|
if (excep) {
|
||||||
report_exception(env, excep,
|
report_exception(env, excep,
|
||||||
"*** Uncaught remote exception! "
|
"*** Uncaught remote exception! "
|
||||||
@ -574,6 +605,16 @@ static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz,
|
|||||||
IPCThreadState::self()->restoreCallingIdentity(token);
|
IPCThreadState::self()->restoreCallingIdentity(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void android_os_Binder_setThreadStrictModePolicy(JNIEnv* env, jobject clazz, jint policyMask)
|
||||||
|
{
|
||||||
|
IPCThreadState::self()->setStrictModePolicy(policyMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jint android_os_Binder_getThreadStrictModePolicy(JNIEnv* env, jobject clazz)
|
||||||
|
{
|
||||||
|
return IPCThreadState::self()->getStrictModePolicy();
|
||||||
|
}
|
||||||
|
|
||||||
static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
|
static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
|
||||||
{
|
{
|
||||||
IPCThreadState::self()->flushCommands();
|
IPCThreadState::self()->flushCommands();
|
||||||
@ -618,6 +659,8 @@ static const JNINativeMethod gBinderMethods[] = {
|
|||||||
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
|
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
|
||||||
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
|
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
|
||||||
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
|
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
|
||||||
|
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
|
||||||
|
{ "getThreadStrictModePolicy", "()I", (void*)android_os_Binder_getThreadStrictModePolicy },
|
||||||
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
|
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
|
||||||
{ "init", "()V", (void*)android_os_Binder_init },
|
{ "init", "()V", (void*)android_os_Binder_init },
|
||||||
{ "destroy", "()V", (void*)android_os_Binder_destroy }
|
{ "destroy", "()V", (void*)android_os_Binder_destroy }
|
||||||
@ -1523,9 +1566,24 @@ static void android_os_Parcel_enforceInterface(JNIEnv* env, jobject clazz, jstri
|
|||||||
if (parcel != NULL) {
|
if (parcel != NULL) {
|
||||||
const jchar* str = env->GetStringCritical(name, 0);
|
const jchar* str = env->GetStringCritical(name, 0);
|
||||||
if (str) {
|
if (str) {
|
||||||
bool isValid = parcel->enforceInterface(String16(str, env->GetStringLength(name)));
|
const int32_t old_strict_policy =
|
||||||
|
IPCThreadState::self()->getStrictModePolicy();
|
||||||
|
int32_t strict_policy;
|
||||||
|
bool isValid = parcel->enforceInterface(
|
||||||
|
String16(str, env->GetStringLength(name)),
|
||||||
|
&strict_policy);
|
||||||
env->ReleaseStringCritical(name, str);
|
env->ReleaseStringCritical(name, str);
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
|
if (old_strict_policy != strict_policy) {
|
||||||
|
// Need to keep the Java-level thread-local strict
|
||||||
|
// mode policy in sync for the libcore
|
||||||
|
// enforcements, which involves an upcall back
|
||||||
|
// into Java. (We can't modify the
|
||||||
|
// Parcel.enforceInterface signature, as it's
|
||||||
|
// pseudo-public, and used via AIDL
|
||||||
|
// auto-generation...)
|
||||||
|
set_dalvik_blockguard_policy(env, strict_policy);
|
||||||
|
}
|
||||||
return; // everything was correct -> return silently
|
return; // everything was correct -> return silently
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1611,6 +1669,14 @@ static int int_register_android_os_Parcel(JNIEnv* env)
|
|||||||
gParcelOffsets.mOwnObject
|
gParcelOffsets.mOwnObject
|
||||||
= env->GetFieldID(clazz, "mOwnObject", "I");
|
= env->GetFieldID(clazz, "mOwnObject", "I");
|
||||||
|
|
||||||
|
clazz = env->FindClass("android/os/StrictMode");
|
||||||
|
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.StrictMode");
|
||||||
|
gStrictModeCallbackOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
|
||||||
|
gStrictModeCallbackOffsets.mCallback = env->GetStaticMethodID(
|
||||||
|
clazz, "onBinderStrictModePolicyChange", "(I)V");
|
||||||
|
LOG_FATAL_IF(gStrictModeCallbackOffsets.mCallback == NULL,
|
||||||
|
"Unable to find strict mode callback.");
|
||||||
|
|
||||||
return AndroidRuntime::registerNativeMethods(
|
return AndroidRuntime::registerNativeMethods(
|
||||||
env, kParcelPathName,
|
env, kParcelPathName,
|
||||||
gParcelMethods, NELEM(gParcelMethods));
|
gParcelMethods, NELEM(gParcelMethods));
|
||||||
|
@ -58,9 +58,13 @@ public:
|
|||||||
|
|
||||||
// Writes the RPC header.
|
// Writes the RPC header.
|
||||||
status_t writeInterfaceToken(const String16& interface);
|
status_t writeInterfaceToken(const String16& interface);
|
||||||
|
|
||||||
// Parses the RPC header, returning true if the interface name
|
// Parses the RPC header, returning true if the interface name
|
||||||
// in the header matches the expected interface from the caller.
|
// in the header matches the expected interface from the caller.
|
||||||
bool enforceInterface(const String16& interface) const;
|
// If strict_policy_out is non-NULL, the RPC header's StrictMode policy
|
||||||
|
// mask is returned.
|
||||||
|
bool enforceInterface(const String16& interface,
|
||||||
|
int32_t* strict_policy_out = NULL) const;
|
||||||
bool checkInterface(IBinder*) const;
|
bool checkInterface(IBinder*) const;
|
||||||
|
|
||||||
void freeData();
|
void freeData();
|
||||||
|
@ -372,8 +372,8 @@ void IPCThreadState::setStrictModePolicy(int32_t policy)
|
|||||||
mStrictModePolicy = policy;
|
mStrictModePolicy = policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t IPCThreadState::getStrictModePolicy() const
|
||||||
int32_t IPCThreadState::getStrictModePolicy() const {
|
{
|
||||||
return mStrictModePolicy;
|
return mStrictModePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,9 @@
|
|||||||
|
|
||||||
#define PAD_SIZE(s) (((s)+3)&~3)
|
#define PAD_SIZE(s) (((s)+3)&~3)
|
||||||
|
|
||||||
|
// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
|
||||||
|
#define STRICT_MODE_PENALTY_GATHER 0x100
|
||||||
|
|
||||||
// XXX This can be made public if we want to provide
|
// XXX This can be made public if we want to provide
|
||||||
// support for typed data.
|
// support for typed data.
|
||||||
struct small_flat_data
|
struct small_flat_data
|
||||||
@ -440,7 +443,8 @@ bool Parcel::hasFileDescriptors() const
|
|||||||
// Write RPC headers. (previously just the interface token)
|
// Write RPC headers. (previously just the interface token)
|
||||||
status_t Parcel::writeInterfaceToken(const String16& interface)
|
status_t Parcel::writeInterfaceToken(const String16& interface)
|
||||||
{
|
{
|
||||||
writeInt32(IPCThreadState::self()->getStrictModePolicy());
|
writeInt32(IPCThreadState::self()->getStrictModePolicy() |
|
||||||
|
STRICT_MODE_PENALTY_GATHER);
|
||||||
// currently the interface identification token is just its name as a string
|
// currently the interface identification token is just its name as a string
|
||||||
return writeString16(interface);
|
return writeString16(interface);
|
||||||
}
|
}
|
||||||
@ -450,9 +454,14 @@ bool Parcel::checkInterface(IBinder* binder) const
|
|||||||
return enforceInterface(binder->getInterfaceDescriptor());
|
return enforceInterface(binder->getInterfaceDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parcel::enforceInterface(const String16& interface) const
|
bool Parcel::enforceInterface(const String16& interface,
|
||||||
|
int32_t* strict_policy_out) const
|
||||||
{
|
{
|
||||||
int32_t strict_policy = readInt32();
|
int32_t strict_policy = readInt32();
|
||||||
|
IPCThreadState::self()->setStrictModePolicy(strict_policy);
|
||||||
|
if (strict_policy_out != NULL) {
|
||||||
|
*strict_policy_out = strict_policy;
|
||||||
|
}
|
||||||
const String16 str(readString16());
|
const String16 str(readString16());
|
||||||
if (str == interface) {
|
if (str == interface) {
|
||||||
return true;
|
return true;
|
||||||
|
Reference in New Issue
Block a user