Android Binder通信原理(八):IPC权限控制

源码基于:Android R

0. 前言

在之前的几篇博文中,对Android binder 的通信原理进行的深入的剖析,这些博文包括:binder 简介servicemanager启动service注册service获取Java 端的service 注册和获取、native下的C-Sjava 下的C-S

本文进一步分析 IPC 下的权限控制。

路径: 

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

frameworks/base/core/jin/android_util_Binder.cpp

frameworks/native/libs/binder/IPCThreadState.cpp

1. java 代码

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

public static final native long clearCallingIdentity();

public static final native void restoreCallingIdentity(long token);

2. JNI 代码

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);
}

restore 参数 token 是clear接口的返回值。

3. 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 主要两件事情:

  • 高32位为 mCallingUid,低32位为 mCallingPid,将其存放到 token,后面会通过restoreCallingIdentity() 恢复。
  • 调用 clearCaller(),临时获取 IPCThreadState 所在进程的确切PID 和 UID;
void IPCThreadState::clearCaller()
{
    mCallingPid = getpid();
    mCallingSid = nullptr;  // expensive to lookup
    mCallingUid = getuid();
}

4. 使用场景

  •  线程 A 通过 binder 调用到线程B,也有可能线程A 和线程B 在不同的进程;
  • 而线程B 中有这样的逻辑:
    •  组件一收到线程A 的调用后,这个时候线程B 所在的IPCThreadState 中的mCallingPid 和mCallingUid 可能会被修改,见4.2;
    • 当组件一调用组件二的功能时,想要显示组件一本身的PID 和UID,见4.1;
    • 当组件二完成后需要返回到组件一,需要恢复IPCThreadState 中的mCallingPid 和mCallingUid; 

 4.1 获取当前Bidner 线程的PID 和UID

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();
}

线程中可以通过Binder 接口获取calling pid 和calling uid,当然native 也可以通过IPCThreadState 获取:

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

那么最开始的mCallingPid 和mCallingUid 怎么来的呢?

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

clearCaller() 在第 3 节解析过,这里在每一个Binder 线程创建的时候会获取当前线程所在的pid、uid。

4.2 IPCThreadState 作为接收端,会修改calling pid和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;
        ...
}

见代码中的注释,在 transact() 中都处于接收端,此时的 pid 和uid 都是sender 端的,如果此过程中需要获取Binder 原始的 mCallingPid 和mCallingUid,则需要调用clear和restore。

例如,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;
        }
    }

在调用ActiveServices.startServiceLocked(),需要获悉当前线程PID 和UID,则需要在调用之前clear ,在调用返回后restore。

至此,IPC 权限控制分析完成。

猜你喜欢

转载自blog.csdn.net/jingerppp/article/details/131529505
今日推荐