Enforcing a maximum depth on nested RemoteViews calls

An unchecked read from parcel can lead to StackOverflowError

Test: am instrument -w -e class android.widget.RemoteViewsTest com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Bug: 26755067
Change-Id: I36ea253846d5c823899097821270dcfca2472b65
This commit is contained in:
Sunny Goyal
2016-11-11 09:19:14 -08:00
parent ac4a8b7f05
commit 692f8c9324
2 changed files with 64 additions and 15 deletions

View File

@@ -42,11 +42,13 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -91,6 +93,12 @@ public class RemoteViews implements Parcelable, Filter {
*/
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
/**
* Maximum depth of nested views calls from {@link #addView(int, RemoteViews)} and
* {@link #RemoteViews(RemoteViews, RemoteViews)}.
*/
private static final int MAX_NESTED_VIEWS = 10;
/**
* Application that hosts the remote views.
*
@@ -1538,11 +1546,11 @@ public class RemoteViews implements Parcelable, Filter {
}
}
ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) {
ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth) {
viewId = parcel.readInt();
boolean nestedViewsNull = parcel.readInt() == 0;
if (!nestedViewsNull) {
nestedViews = new RemoteViews(parcel, bitmapCache, info);
nestedViews = new RemoteViews(parcel, bitmapCache, info, depth);
} else {
nestedViews = null;
}
@@ -2209,10 +2217,16 @@ public class RemoteViews implements Parcelable, Filter {
* @param parcel
*/
public RemoteViews(Parcel parcel) {
this(parcel, null, null);
this(parcel, null, null, 0);
}
private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info) {
private RemoteViews(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth) {
if (depth > MAX_NESTED_VIEWS
&& (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID)) {
throw new IllegalArgumentException("Too many nested views.");
}
depth++;
int mode = parcel.readInt();
// We only store a bitmap cache in the root of the RemoteViews.
@@ -2245,7 +2259,8 @@ public class RemoteViews implements Parcelable, Filter {
mActions.add(new ReflectionAction(parcel));
break;
case ViewGroupAction.TAG:
mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication));
mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication,
depth));
break;
case ReflectionActionWithoutParams.TAG:
mActions.add(new ReflectionActionWithoutParams(parcel));
@@ -2293,8 +2308,8 @@ public class RemoteViews implements Parcelable, Filter {
}
} else {
// MODE_HAS_LANDSCAPE_AND_PORTRAIT
mLandscape = new RemoteViews(parcel, mBitmapCache, info);
mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication);
mLandscape = new RemoteViews(parcel, mBitmapCache, info, depth);
mPortrait = new RemoteViews(parcel, mBitmapCache, mLandscape.mApplication, depth);
mApplication = mPortrait.mApplication;
mLayoutId = mPortrait.getLayoutId();
}
@@ -2318,7 +2333,7 @@ public class RemoteViews implements Parcelable, Filter {
p.setDataPosition(0);
mIsRoot = true;
RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication);
RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication, 0);
rv.mIsRoot = true;
p.recycle();

View File

@@ -179,13 +179,8 @@ public class RemoteViewsTest {
ViewAppliedListener listener = new ViewAppliedListener();
views.applyAsync(mContext, mContainer, AsyncTask.THREAD_POOL_EXECUTOR, listener);
boolean exceptionThrown = false;
try {
listener.waitAndGetView();
} catch (Exception e) {
exceptionThrown = true;
}
assertTrue(exceptionThrown);
exception.expect(Exception.class);
listener.waitAndGetView();
}
@Test
@@ -332,4 +327,43 @@ public class RemoteViewsTest {
return mView;
}
}
@Test
public void nestedAddViews() {
RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
for (int i = 0; i < 10; i++) {
RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_views_test);
parent.addView(R.id.layout, views);
views = parent;
}
views.clone();
views = new RemoteViews(mPackage, R.layout.remote_views_test);
for (int i = 0; i < 11; i++) {
RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_views_test);
parent.addView(R.id.layout, views);
views = parent;
}
exception.expect(IllegalArgumentException.class);
views.clone();
}
@Test
public void nestedLandscapeViews() {
RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test);
for (int i = 0; i < 10; i++) {
views = new RemoteViews(views,
new RemoteViews(mPackage, R.layout.remote_views_test));
}
views.clone();
views = new RemoteViews(mPackage, R.layout.remote_views_test);
for (int i = 0; i < 11; i++) {
RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_views_test);
parent.addView(R.id.layout, views);
views = parent;
}
exception.expect(IllegalArgumentException.class);
views.clone();
}
}