From 02ca7a7db04b000a8e403d130781fe65b4e0d091 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 24 Jun 2015 18:16:42 -0700 Subject: [PATCH] Don't throw TransactionTooLargeException for small payloads In practice, if we ever see an apparent transaction-too-large result with a modest-sized payload, it means that the remote process died at just the right time (with the binder transaction already in flight so it wasn't detected as a DEAD_OBJECT up front). Don't throw TransactionTooLargeException in this case, because we really do need to distinguish that from dead-remote more accurately. In particular, certain common execution patterns on existing hardware trigger this circumstance, and they wind up crashing the system. This is bad, so now we avoid it unless we're pretty sure that is really what happened. Bug 21801759 Change-Id: Id05f1eecc0d23dc8d0505c402e2cb68396782135 --- core/jni/android_util_Binder.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 70a7805de5360..e2cfa440162e1 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -682,18 +682,28 @@ void signalExceptionForError(JNIEnv* env, jobject obj, status_t err, break; case FAILED_TRANSACTION: { ALOGE("!!! FAILED BINDER TRANSACTION !!! (parcel size = %d)", parcelSize); + const char* exceptionToThrow; char msg[128]; - snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize); // TransactionTooLargeException is a checked exception, only throw from certain methods. // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION // but it is not the only one. The Binder driver can return BR_FAILED_REPLY // for other reasons also, such as if the transaction is malformed or // refers to an FD that has been closed. We should change the driver // to enable us to distinguish these cases in the future. - jniThrowException(env, canThrowRemoteException - ? "android/os/TransactionTooLargeException" - : "java/lang/RuntimeException", - parcelSize > 0 ? msg : NULL); + if (canThrowRemoteException && parcelSize > 200*1024) { + // bona fide large payload + exceptionToThrow = "android/os/TransactionTooLargeException"; + snprintf(msg, sizeof(msg)-1, "data parcel size %d bytes", parcelSize); + } else { + // Heuristic: a payload smaller than this threshold "shouldn't" be too + // big, so it's probably some other, more subtle problem. In practice + // it nearly always means that the remote process died while the binder + // transaction was already in flight. + exceptionToThrow = "java/lang/RuntimeException"; + snprintf(msg, sizeof(msg)-1, + "Transaction failed on small parcel; remote process probably died"); + } + jniThrowException(env, exceptionToThrow, msg); } break; case FDS_NOT_ALLOWED: jniThrowException(env, "java/lang/RuntimeException",