Android Binder communication principle (8): IPC permission control

Source code based on: Android R

0. Preface

In several previous blog posts, we have conducted an in-depth analysis of the communication principles of Android binder. These blog posts include: binder introduction , servicemanager startup , service registration , service acquisition , service registration and acquisition , CS under native , java CS under .

This article further analyzes permission control under IPC.

path: 

frameworks/base/core/java/android/os/Binder.java

frameworks/base/core/jin/android_util_Binder.cpp

frameworks/native/libs/binder/IPCThreadState.cpp

1. java code

frameworks/base/core/java/android/os/Binder.java

public static final native long clearCallingIdentity();

public static final native void restoreCallingIdentity(long token);

2. JNI code

frameworks/base/core/jin/android_util_Binder.cpp

static jlong android_os_Binder_clearCallingIdentity()
{
    return IPCThreadState::self()->clearCallingIdentity();
}

static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token)
{
    // XXX temporary sanity check to debug crashes.
    int uid = (int)(token>>32);
    if (uid > 0 && uid < 999) {
        // In Android currently there are no uids in this range.
        char buf[128];
        sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token);
        jniThrowException(env, "java/lang/IllegalStateException", buf);
        return;
    }
    IPCThreadState::self()->restoreCallingIdentity(token);
}

The restore parameter token is the return value of the clear interface.

3. Code in IPCThreadState

frameworks/native/libs/binder/IPCThreadState.cpp

int64_t IPCThreadState::clearCallingIdentity()
{
    // ignore mCallingSid for legacy reasons
    int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
    clearCaller();
    return token;
}

void IPCThreadState::restoreCallingIdentity(int64_t token)
{
    mCallingUid = (int)(token>>32);
    mCallingSid = nullptr;  // not enough data to restore
    mCallingPid = (int)token;
}

clearCallingIdentity mainly does two things:

  • The high 32 bits are mCallingUid and the low 32 bits are mCallingPid . They are stored in the token and will be restored later through restoreCallingIdentity() .
  • Call clearCaller() to temporarily obtain the exact PID and UID of the process where IPCThreadState is located;
void IPCThreadState::clearCaller()
{
    mCallingPid = getpid();
    mCallingSid = nullptr;  // expensive to lookup
    mCallingUid = getuid();
}

4. Usage scenarios

  •  Thread A calls thread B through binder. It is also possible that thread A and thread B are in different processes;
  • Thread B has this logic:
    •  Once the component receives a call from thread A, mCallingPid and mCallingUid in the IPCThreadState where thread B is located may be modified at this time, see 4.2;
    • When component one calls the function of component two, it wants to display the PID and UID of component one itself, see 4.1;
    • When component two is completed and needs to return to component one, mCallingPid and mCallingUid in IPCThreadState need to be restored; 

 4.1 Get the PID and UID of the current Bidner thread

JAVA:
public static final native int getCallingPid();
public static final native int getCallingUid();

JNI:
static jint android_os_Binder_getCallingPid()
{
    return IPCThreadState::self()->getCallingPid();
}
static jint android_os_Binder_getCallingUid()
{
    return IPCThreadState::self()->getCallingUid();
}

The calling pid and calling uid can be obtained in the thread through the Binder interface. Of course, the native can also be obtained through IPCThreadState:

pid_t IPCThreadState::getCallingPid() const
{
    return mCallingPid;
}
uid_t IPCThreadState::getCallingUid() const
{
    return mCallingUid;
}

So where did the initial mCallingPid and mCallingUid come from?

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      ...
{
    ...
    clearCaller();
    ...
}

clearCaller() was analyzed in Section 3. Here, when each Binder thread is created, the pid and uid of the current thread will be obtained.

4.2 IPCThreadState, as the receiving end, will modify the calling pid and uid

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    ...
    case BR_TRANSACTION:
        const pid_t origPid = mCallingPid;   //备份mCallingPid 和mCallingUid
        const char* origSid = mCallingSid;
        const uid_t origUid = mCallingUid;
        ...
        mCallingPid = tr.sender_pid;    //这里会修改成sender 的pid 和uid
        mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
        mCallingUid = tr.sender_euid;
        ...
        if (tr.target.ptr) {
            // We only have a weak reference on the target object, so we must first try to
            // safely acquire a strong reference before doing anything else with it.
            if (reinterpret_cast<RefBase::weakref_type*>(
                    tr.target.ptr)->attemptIncStrong(this)) {
                error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, //进入接收端,此时的pid和uid都为sender的
                        &reply, tr.flags);
                reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
            } else {
                error = UNKNOWN_TRANSACTION;
            }
        } else {
            error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
        }
        ...
        mCallingPid = origPid; //从接收端返回后,pid和uid 需要改为原始的
        mCallingSid = origSid;
        mCallingUid = origUid;
        ...
}

See the comments in the code. In transact() , both are on the receiving end. The pid and uid at this time are both on the sender side. If you need to obtain the original mCallingPid and mCallingUid of Binder during this process, you need to call clear and restore.

For example, in AMS:

    public ComponentName startService(IApplicationThread caller, Intent service,
            ...
            String callingFeatureId, int userId)
            throws TransactionTooLargeException {
        ...
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, callingFeatureId, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

When calling ActiveServices.startServiceLocked(), you need to know the current thread PID and UID. You need to clear before the call and restore after the call returns.

At this point, the IPC permission control analysis is completed.

Guess you like

Origin blog.csdn.net/jingerppp/article/details/131529505