OVERVIEW Sending a private serializable subclass as an Intent extra can crash many receivers. STEPS TO REPRODUCE 1. Send a private serializable subclass as an Intent extra to a receiver that inspects the extras. For example, try the following sample code: public class TestActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS).putExtra("serializable", new CustomSerializable())); } private static final class CustomSerializable implements Serializable { private static final long serialVersionUID = 1L; } } RESULTS Actual behavior: The receiver crashes as soon as it attempts to look at the Intent's extras with a RuntimeException. This is the trace generated on the Honeycomb emulator E/AndroidRuntime( 464): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.Settings$WifiSettingsActivity}: java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.test.TestActivity$CustomSerializable) E/AndroidRuntime( 464): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1736) E/AndroidRuntime( 464): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1752) E/AndroidRuntime( 464): at android.app.ActivityThread.access$1500(ActivityThread.java:123) E/AndroidRuntime( 464): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:993) E/AndroidRuntime( 464): at android.os.Handler.dispatchMessage(Handler.java:99) E/AndroidRuntime( 464): at android.os.Looper.loop(Looper.java:126) E/AndroidRuntime( 464): at android.app.ActivityThread.main(ActivityThread.java:3997) E/AndroidRuntime( 464): at java.lang.reflect.Method.invokeNative(Native Method) E/AndroidRuntime( 464): at java.lang.reflect.Method.invoke(Method.java:491) E/AndroidRuntime( 464): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) E/AndroidRuntime( 464): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) E/AndroidRuntime( 464): at dalvik.system.NativeStart.main(Native Method) E/AndroidRuntime( 464): Caused by: java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.fragment.test.FragmentTestActivity$CustomSerializable) E/AndroidRuntime( 464): at android.os.Parcel.readSerializable(Parcel.java:2026) E/AndroidRuntime( 464): at android.os.Parcel.readValue(Parcel.java:1897) E/AndroidRuntime( 464): at android.os.Parcel.readMapInternal(Parcel.java:2083) E/AndroidRuntime( 464): at android.os.Bundle.unparcel(Bundle.java:215) E/AndroidRuntime( 464): at android.os.Bundle.getBoolean(Bundle.java:787) E/AndroidRuntime( 464): at android.content.Intent.getBooleanExtra(Intent.java:3444) E/AndroidRuntime( 464): at android.preference.PreferenceActivity.onIsHidingHeaders(PreferenceActivity.java:665) E/AndroidRuntime( 464): at android.preference.PreferenceActivity.onCreate(PreferenceActivity.java:495) E/AndroidRuntime( 464): at com.android.settings.Settings.onCreate(Settings.java:66) E/AndroidRuntime( 464): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048) E/AndroidRuntime( 464): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1700) E/AndroidRuntime( 464): ... 11 more E/AndroidRuntime( 464): Caused by: java.lang.ClassNotFoundException: com.fragment.test.FragmentTestActivity$CustomSerializable E/AndroidRuntime( 464): at java.lang.Class.classForName(Native Method) E/AndroidRuntime( 464): at java.lang.Class.forName(Class.java:234) E/AndroidRuntime( 464): at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2392) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1742) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:755) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1888) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:859) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2090) E/AndroidRuntime( 464): at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2045) E/AndroidRuntime( 464): at android.os.Parcel.readSerializable(Parcel.java:2020) E/AndroidRuntime( 464): ... 21 more E/AndroidRuntime( 464): Caused by: java.lang.NoClassDefFoundError: com.fragment.test.FragmentTestActivity$CustomSerializable E/AndroidRuntime( 464): ... 31 more E/AndroidRuntime( 464): Caused by: java.lang.ClassNotFoundException: com.fragment.test.FragmentTestActivity$CustomSerializable in loader dalvik.system.PathClassLoader[/system/app/Settings.apk] E/AndroidRuntime( 464): at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:251) E/AndroidRuntime( 464): at java.lang.ClassLoader.loadClass(ClassLoader.java:548) E/AndroidRuntime( 464): at java.lang.ClassLoader.loadClass(ClassLoader.java:508) E/AndroidRuntime( 464): ... 31 more Expected behavior: The expected behavior requires additional design discussion. At a minimum, the exception should be a ClassNotFoundException rather than a runtime exception. This would allow all receivers to more easily catch this issue. However requiring all receivers to implement such try-catch boilerplate might be non-obvious, and perhaps consideration should be given to other solutions. For example, the extra could simply be suppressed (and logged as a error) similar to how accessing a extras as the wrong type is currently handled (e.g. store a string, try to retrieve a boolean, gets treated as if the boolean simply doesn't exist). WORKAROUND When reading the extras, the following can be done to prevent the receiver from crashing /* * This is a hack to work around a custom serializable classloader attack. This check must come before any of the Intent * extras are examined. */ try { final Bundle extras = intent.getExtras(); if (extras != null) { // if a custom serializable subclass exists, this will throw an exception extras.containsKey(null); } } catch (final Exception e) { Log.e("Test", "Custom serializable attack detected; do not send private Serializable subclasses to this receiver", e); //$NON-NLS-1$ return; } NOTES This issue reproduces on Droid, Nexus One, Nexus S, etc. running Android 2.2, 2.3.3, and 2.3.2 respectively. It also reproduces on the Honeycomb emulator.
友盟反馈中发现的这样的问题,对下文没有考证,最终对2.3版本通过绕过Parserable 和 serializable方式解决的,留此只做备忘
最新推荐文章于 2023-04-10 15:32:50 发布