Android 12 S 系统开机流程分析 - SetupSelinux(二)

Android 12 S 系统开机流程分析-FirstStageMain(一)

本文接着上文开始讲解,上文中最后一步执行后会执行init启动过程中的第二步SetupSelinux(Selinux配置阶段),这样又会走到main.cpp中的main方法。

目录

1. SetupSelinux

1.1 初始化kernel log系统

1.2 ReadPolicy

1.3 加载策略

1.4 使能/关闭Selinux并写入节点

1.5 selinux_android_restorecon

1.6  重新执行init进程并携带参数second_stage


1. SetupSelinux

由于上一篇中最后一步在重新执行init的时候携带了参数selinux_setup,所以此处会走入SetupSelinux方法,加载selinux的策略。

SetupSelinux主要功能:

1)open sepolicy

2)load sepolicy

3)使能/关闭selinux

int main(int argc, char** argv) {
...
        if (!strcmp(argv[1], "selinux_setup")) {
//执行此处
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

1.1 初始化kernel log系统

1)初始化kernel log

2)若系统发生了panic,则走InstallRebootSignalHandlers

InstallRebootSignalHandlers中主要做了以下几件事:

    1. 初始化了一个自定义信号集,将其所有信号都填充满,即将信号集中的所有的标志位都置为1,使得这个集合包含所有可接受的信号,也就是阻塞所有信号。这个函数可以用于快速创建一个包含所有信号的信号集,然后可以根据需要删除其中的某些信号。

    2. init创建出来的子进程不做处理,直接exit;如果不是子进程,则代表是init进程,则执行InitFatalReboot

    3. 通过syscall向内核发送重启命令

    4. 捕获一些信号

int SetupSelinux(char** argv) {
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);

    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }

    boot_clock::time_point start_time = boot_clock::now();
//这是R上为了R system.img/system_ext.img作为system_ext.img工作在old vendor.img上。
//我们在init第二阶段挂载system_ext,因为在system-only OTA场景中boot.img的init第一阶段是不会被更新的。
    MountMissingSystemPartitions();

    SelinuxSetupKernelLogging();

    LOG(INFO) << "Opening SELinux policy";

 SelinuxGetVendorAndroidVersion主要作用是读/vendor/etc/selinux/plat_sepolicy_vers.txt中的第一行,获取Selinux vendor version。

int SelinuxGetVendorAndroidVersion() {
    static int vendor_android_version = [] {
        if (!IsSplitPolicyDevice()) {
//如果这个设备不拆分sepolicy文件,它就不是一个Treble设备
            return __ANDROID_API_FUTURE__;
        }

        std::string version;
//获取vendor Selinux version
//读/vendor/etc/selinux/plat_sepolicy_vers.txt中的第一行
//可以adb看下vendor版本:
//adb shell cat /vendor/etc/selinux/plat_sepolicy_vers.txt
//32.0
        if (!GetVendorMappingVersion(&version)) {
            LOG(FATAL) << "Could not read vendor SELinux version";
        }
//解析version值,version一般是带小数点的,比如32.0。
        int major_version;
        std::string major_version_str(version, 0, version.find('.'));
        if (!ParseInt(major_version_str, &major_version)) {
            PLOG(FATAL) << "Failed to parse the vendor sepolicy major version "
                        << major_version_str;
        }
//返回version中整数
        return major_version;
    }();
    return vendor_android_version;
}

1.2 ReadPolicy

ReadPolicy主要做了两件事

1)open split policy

2)读sepolicy

这个是SetupSelinux的主要功能之一。

    // Read the policy before potentially killing snapuserd.
    std::string policy;
    ReadPolicy(&policy);

    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
    if (snapuserd_helper) {
        // Kill the old snapused to avoid audit messages. After this we cannot
        // read from /system (or other dynamic partitions) until we call
        // FinishTransition().
        snapuserd_helper->StartTransition();
    }

ReadPolicy的主要功能是:

    如果设备中存在/system/etc/selinux/plat_sepolicy.cil文件,并可以访问,则IsSplitPolicyDevice为true,会走OpenSplitPolicy流程,这个设备中默认都有这个文件的。

void ReadPolicy(std::string* policy) {
    PolicyFile policy_file;
//constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";

//bool IsSplitPolicyDevice() {
//    return access(plat_policy_cil_file, R_OK) != -1;
//}
//IsSplitPolicyDevice如上,如果能访问设备中的/system/etc/selinux/plat_sepolicy.cil文件,
//则IsSplitPolicyDevice为true,会走OpenSplitPolicy
    bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
                                    : OpenMonolithicPolicy(&policy_file);
    if (!ok) {
        LOG(FATAL) << "Unable to open SELinux policy";
    }

    if (!android::base::ReadFdToString(policy_file.fd, policy)) {
        PLOG(FATAL) << "Failed to read policy file: " << policy_file.path;
    }
}

OpenSplitPolicy的主要功能是:

1)如果设备是userdebug版本+设备unlock+存在/debug_ramdisk/adb_debug.prop并且可以访问,则加载userdebug system sepolicy

 2)从代码看是access system,vendor,system_ext和vendor下etc/selinux/mapping/下的.cil文件
3)再查看对应目录下的.cil文件内容是否为空,不为空则将对应目录下的.cil文件内容push进compile_args
4)执行compile_args并等待,决定include哪个mapping下的.cil文件

5)将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。

bool OpenSplitPolicy(PolicyFile* policy_file) {
//若存在/force_debuggable,则在init启动第一阶段,force_debuggable_env 环境变量已经设置好了
//setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
//是userdebug版本+设备unlock+存在/debug_ramdisk/adb_debug.prop并且可以访问,
//则加载userdebug system sepolicy
    bool use_userdebug_policy =
            ((force_debuggable_env && "true"s == force_debuggable_env) &&
             AvbHandle::IsDeviceUnlocked() && access(kDebugRamdiskSEPolicy, F_OK) == 0);
    if (use_userdebug_policy) {
        LOG(WARNING) << "Using userdebug system sepolicy";
    }

    if (!use_userdebug_policy) {
        if (auto res = FindPrecompiledSplitPolicy(); res.ok()) {
            unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
            if (fd != -1) {
                policy_file->fd = std::move(fd);
                policy_file->path = std::move(*res);
                return true;
            }
        } else {
            LOG(INFO) << res.error();
        }
    }
    // No suitable precompiled policy could be loaded
//没有合适的策略可以加载
    LOG(INFO) << "Compiling SELinux policy";

    // We store the output of the compilation on /dev because this is the most convenient tmpfs
    // storage mount available this early in the boot sequence.
    char compiled_sepolicy[] = "/dev/sepolicy.XXXXXX";
//mkostemp:创建临时文件
//创建临时文件/dev/sepolicy.XXXXXX,其fd为compiled_sepolicy_fd
    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));
    if (compiled_sepolicy_fd < 0) {
        PLOG(ERROR) << "Failed to create temporary file " << compiled_sepolicy;
        return false;
    }
...
//从代码看是access system,vendor,system_ext和vendor下etc/selinux/mapping/下的.cil文件
//再查看对应目录下的.cil文件内容是否为空,不为空则将对应目录下的.cil文件内容push进compile_args
//执行compile_args并等待
//将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。
//决定include哪个mapping下的.cil文件
    std::string vend_plat_vers;
    if (!GetVendorMappingVersion(&vend_plat_vers)) {
        return false;
    }
    std::string plat_mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");

    std::string plat_compat_cil_file("/system/etc/selinux/mapping/" + vend_plat_vers +
                                     ".compat.cil");
    if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {
        plat_compat_cil_file.clear();
    }

    std::string system_ext_policy_cil_file("/system_ext/etc/selinux/system_ext_sepolicy.cil");
    if (access(system_ext_policy_cil_file.c_str(), F_OK) == -1) {
        system_ext_policy_cil_file.clear();
    }

    std::string system_ext_mapping_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +
                                        ".cil");
    if (access(system_ext_mapping_file.c_str(), F_OK) == -1) {
        system_ext_mapping_file.clear();
    }

    std::string system_ext_compat_cil_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +
                                           ".compat.cil");
    if (access(system_ext_compat_cil_file.c_str(), F_OK) == -1) {
        system_ext_compat_cil_file.clear();
    }

    std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
    if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
        product_policy_cil_file.clear();
    }

    std::string product_mapping_file("/product/etc/selinux/mapping/" + vend_plat_vers + ".cil");
    if (access(product_mapping_file.c_str(), F_OK) == -1) {
        product_mapping_file.clear();
    }

    // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
    // nonplat_sepolicy.cil.
    std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");

    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
        // For backward compatibility.
        // TODO: remove this after no device is using nonplat_sepolicy.cil.
        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
        plat_pub_versioned_cil_file.clear();
    } else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
        LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;
        return false;
    }

    // odm_sepolicy.cil is default but optional.
    std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
        odm_policy_cil_file.clear();
    }
    const std::string version_as_string = std::to_string(SEPOLICY_VERSION);

//有一些代码,我们并不想让clang-format调整格式,这时可以使用注释临时禁用clang-format
    // clang-format off
    std::vector compile_args {
        "/system/bin/secilc",
        use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
        "-m", "-M", "true", "-G", "-N",
        "-c", version_as_string.c_str(),
        plat_mapping_file.c_str(),
        "-o", compiled_sepolicy,
        // We don't care about file_contexts output by the compiler
        "-f", "/sys/fs/selinux/null",  // /dev/null is not yet available
    };
    // clang-format on

//将对应目录下的.cil文件内容push进compile_args
    if (!plat_compat_cil_file.empty()) {
        compile_args.push_back(plat_compat_cil_file.c_str());
    }
    if (!system_ext_policy_cil_file.empty()) {
        compile_args.push_back(system_ext_policy_cil_file.c_str());
    }
    if (!system_ext_mapping_file.empty()) {
        compile_args.push_back(system_ext_mapping_file.c_str());
    }
    if (!system_ext_compat_cil_file.empty()) {
        compile_args.push_back(system_ext_compat_cil_file.c_str());
    }
    if (!product_policy_cil_file.empty()) {
        compile_args.push_back(product_policy_cil_file.c_str());
    }
    if (!product_mapping_file.empty()) {
        compile_args.push_back(product_mapping_file.c_str());
    }
    if (!plat_pub_versioned_cil_file.empty()) {
        compile_args.push_back(plat_pub_versioned_cil_file.c_str());
    }
    if (!vendor_policy_cil_file.empty()) {
        compile_args.push_back(vendor_policy_cil_file.c_str());
    }
    if (!odm_policy_cil_file.empty()) {
        compile_args.push_back(odm_policy_cil_file.c_str());
    }
    compile_args.push_back(nullptr);
//执行compile_args并等待
    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
        unlink(compiled_sepolicy);
        return false;
    }
    unlink(compiled_sepolicy);
//将临时文件/dev/sepolicy.XXXXXX的fd和path保存进policy_file->fd和 policy_file->path。
    policy_file->fd = std::move(compiled_sepolicy_fd);
    policy_file->path = compiled_sepolicy;
    return true;
}

1.3 加载策略

这也是SetupSelinux中最重要的功能之一。

   LoadSelinuxPolicy(policy);

 上一节中对policy进行了赋值。

static void LoadSelinuxPolicy(std::string& policy) {
    LOG(INFO) << "Loading SELinux policy";

    set_selinuxmnt("/sys/fs/selinux");
    if (security_load_policy(policy.data(), policy.size()) < 0) {
        PLOG(FATAL) << "SELinux:  Could not load policy";
    }
}

 security_load_policy主要功能是:

1)open $selinux_mnt/load,获取其对应fd

2)根据fd将/dev/sepolicy.XXXXXX中的内容写入$selinux_mnt/load中。selinux_mnt如下#define SELINUXMNT "/sys/fs/selinux"

external/selinux/libselinux/src/load_policy.c
int security_load_policy(void *data, size_t len)
{
	char path[PATH_MAX];
	int fd, ret;

	if (!selinux_mnt) {
		errno = ENOENT;
		return -1;
	}

	snprintf(path, sizeof path, "%s/load", selinux_mnt);
	fd = open(path, O_RDWR | O_CLOEXEC);
	if (fd < 0)
		return -1;

	ret = write(fd, data, len);
	close(fd);
	if (ret < 0)
		return -1;
	return 0;
}

1.4 使能/关闭Selinux并写入节点

获取selinux状态是否是enforce,然后将selinux状态is_enforcing写入 $selinux_mnt/enforce中。selinux_mnt如下

#define SELINUXMNT "/sys/fs/selinux"

    if (snapuserd_helper) {
        // Before enforcing, finish the pending snapuserd transition.
        snapuserd_helper->FinishTransition();
        snapuserd_helper = nullptr;
    }

    SelinuxSetEnforcement();
void SelinuxSetEnforcement() {
    bool kernel_enforcing = (security_getenforce() == 1);
//check selinux是否是enforce状态(开启selinux)
    bool is_enforcing = IsEnforcing();
    if (kernel_enforcing != is_enforcing) {
        if (security_setenforce(is_enforcing)) {
            PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
                        << ") failed";
        }
    }

    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
    }
}
bool IsEnforcing() {
//如果关闭系统的selinux,即将selinux置为permissive,此处直接返回false即可
//return false;
    if (ALLOW_PERMISSIVE_SELINUX) {
        return StatusFromProperty() == SELINUX_ENFORCING;
    }
    return true;
}
/external/selinux/libselinux/src/setenforce.c
int security_setenforce(int value)
{
	int fd, ret;
	char path[PATH_MAX];
	char buf[20];

	if (!selinux_mnt) {
		errno = ENOENT;
		return -1;
	}

	snprintf(path, sizeof path, "%s/enforce", selinux_mnt);
	fd = open(path, O_RDWR | O_CLOEXEC);
	if (fd < 0)
		return -1;

	snprintf(buf, sizeof buf, "%d", value);
	ret = write(fd, buf, strlen(buf));
	close(fd);
	if (ret < 0)
		return -1;

	return 0;
}

1.5 selinux_android_restorecon

从kernel domain域过渡到init domain域
文件系统在xattrs中存储了SELabels,比如ext4不需要restorecon,但其他文件系统需要

//从kernel domain域过渡到init domain域
//文件系统在xattrs中存储了SELabels,比如ext4不需要restorecon,但其他文件系统需要
    if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
        PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
    }

1.6  重新执行init进程并携带参数second_stage

    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);

    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast(args));

    // execv() only returns if an error happened, in which case we
    // panic and never return from this function.
    PLOG(FATAL) << "execv(\"" << path << "\") failed";

    return 1;
}

到了SetupSelinux的最后一步,这一步还是重新执行/system/bin/init,这样就会再次走init bin程序的main函数,由代码可以看到,此次携带了参数second_stage。

后面接着讲启动流程

你可能感兴趣的:(Android,Android系统,android)