Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would a thread created by an app be considered a different app from the app's ContentProvider?

I have an app that, when notified by a ContentObserver of a change to a ContentProvider, attempts to query the provider on a background thread. This causes an SecurityException to be thrown:

8-10 15:54:29.577    3057-3200/com.xxxx.mobile.android.xxx W/Binder﹕ Caught a RuntimeException from the binder stub implementation.
  java.lang.SecurityException: Permission Denial: reading com.xxx.mobile.android.mdk.model.customer.ContentProvider uri content://com.xxx.mobile.android.consumer.xxx/vehicle from pid=0, uid=1000 requires the provider be exported, or grantUriPermission()
at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:539)
           at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:452)
           at android.content.ContentProvider$Transport.query(ContentProvider.java:205)
           at android.content.ContentResolver.query(ContentResolver.java:478)
           at android.content.ContentResolver.query(ContentResolver.java:422)

How would a thread created by an app end up with a different UID from the app's ContentProvider?

By placing an exception breakpoint in android.content.ContentProvider I see that UserHandle.isSameApp(uid, mMyUid) is false and UserHandle.isSameUser(uid, mMyUid) is true. I also see that the providers UID is 10087.

like image 650
Julian A. Avatar asked Aug 10 '15 22:08

Julian A.


1 Answers

The uid value of 1000 belongs to the Android system. Many features of Android involve proxying requests to the system thread for processing. If an exception is thrown during this, the error will include the uid of the system, rather than the original requestor.

For the other points:

UserHandle.isSameApp(uid, mMyUid) is false

UserHandle.isSameUser(uid, mMyUid) is true

These are easiest to explain by looking at the source. On an Android device with multi-user support, each user is defined by a range of UIDs. isSameApp is false because the modulus of the ids do not match:

 public static final boolean isSameApp(int uid1, int uid2) {
        return getAppId(uid1) == getAppId(uid2);
}

 public static final int getAppId(int uid) {
        return uid % PER_USER_RANGE;
}

Similarly, the two ids belong to the same user because they live in the same range:

 public static final boolean isSameUser(int uid1, int uid2) {
        return getUserId(uid1) == getUserId(uid2);
 }

public static final int getUserId(int uid) {
        if (MU_ENABLED) {
            return uid / PER_USER_RANGE;
        } else {
            return 0;
        }
}

Note that this logic is flawed because it means that all Android system uids (< 10000) will be assumed to "belong" to the first user.

Also note that if a second user installs more than 1000 apps(!), there's the possibility that an App will be mistaken for a system app (both uid % PER_USER_RANGE will return 1000). It won't really matter though, because the strong sandboxing would prevent anything too bad from happening.

like image 95
adelphus Avatar answered Nov 15 '22 18:11

adelphus