跳转到主要内容

Aosp GSI烧录无法开机

问题描述

a78项目在烧录GSI之后无法开机,init部分不断重启(正常可以开机),log类似如下:

[    4.688635][T1600001] init: init: Opening SELinux policy
[    4.690264][T1600001] init: init: Falling back to standard signature check. TODO implement support for fsverity SEPolicy.
[    4.691655][T1600001] init: init: Error: Apex SEPolicy failed signature check
[    4.692858][T1600001] init: init: Loading APEX Sepolicy from /system/etc/selinux/apex/SEPolicy.zip
[    4.693995][T1600001] init: init: Failed to open package /system/etc/selinux/apex/SEPolicy.zip: No such file or directory
# 此处后续没有sepolicy编译加载的log,
[    5.380006][T1400305] secilc: [name:exit&]secilc (305) used greatest stack depth: 10624 bytes left

问题分析

上述log初步怀疑是selinux加载出现异常,查看selinux加载代码(alps/system/core/init/selinux.cpp):

int SetupSelinux(char** argv) {
	// ...
    PrepareApexSepolicy();

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

void ReadPolicy(std::string* policy) {
    PolicyFile policy_file;
    bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)
                                    : OpenMonolithicPolicy(&policy_file);
}

bool OpenSplitPolicy(PolicyFile* policy_file) {
	// ...
    if (!use_userdebug_policy) {
	    // 查找预编译sepolicy,正常会走,烧录gsi之后不会执行(hash值判定失败,会重新compile)
        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;
            }
        }
    }
    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";
    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;
    }

    // Determine which mapping file to include
    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();
    }

    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
        LOG(ERROR) << "Missing " << vendor_policy_cil_file;
        return false;
    }

    std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
    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();
    }

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

    // clang-format off
    std::vector<const char*> compile_args {
        "/system/bin/secilc",
        use_userdebug_policy ? *userdebug_plat_sepolicy : 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

    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());
    }
    if (!apex_policy_cil_file.empty()) {
        compile_args.push_back(apex_policy_cil_file.c_str());
    }
    compile_args.push_back(nullptr);

    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
        unlink(compiled_sepolicy);
        return false;
    }
    unlink(compiled_sepolicy);

    policy_file->fd = std::move(compiled_sepolicy_fd);
    policy_file->path = compiled_sepolicy;
    return true;
}

正常开机selinux初始化时,会对比,system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256与vendor/etc/selinux/precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256中的哈希值(包括其他分区的,这里写system_ext是因为我们客制化的权限都添加在了system_ext中),如果发现哈希值不一致,那么就会调用seclic命令在线编译一次(烧录GSI之后,一定会走编译,因为哈希值一定不一样),此哈希值是在编译时期,根据添加的所有的权限文件内容,生成的,命令类似(cat $(in) | sha256sum | cut -d' ' -f1 > $(out),所以如果system,vendor的编译内容不一致,那么生成的哈希值一定会不一样。

在烧录GSI之后,会使用secilc命令重新编译sepolicy,编译成功之后继续加载政策, 因此怀疑是否是此处编译出现异常。因为ForkExecveAndWaitForCompletion此函数回去执行编译命令,如果编译失败,就会直接退出进程,会导致init进程异常。

// fork succeeded -- this is executing in the child process

        // Close the pipe FD not used by this process
        close(pipe_fds[0]);

        // Redirect stderr to the pipe FD provided by the parent
        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
            PLOG(ERROR) << "Failed to redirect stderr of " << filename;
            _exit(127);
            return false;
        }
        close(pipe_fds[1]);

        if (execv(filename, argv) == -1) {
            PLOG(ERROR) << "Failed to execve " << filename;
            return false;
        }
        // Unreachable because execve will have succeeded and replaced this code
        // with child process's code.
        _exit(127);
        return false;

因此准备验证是否是编译问题: 首先,将gsi中的system.img挂载在电脑上(直接双击打开就行,使用mount挂载反而会失败,可能是命令参数问题),然后根据代码中需要的参数准备文件:

std::vector<const char*> compile_args {
        "/system/bin/secilc",
        use_userdebug_policy ? *userdebug_plat_sepolicy : 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
    };
  • plat_policy_cil_file:/system/etc/selinux/plat_sepolicy.cil,使用GSI中的文件
  • version_as_string:30,来自于SEPOLICY_VERSION,在system/core/init/Android.bp中指定:-DSEPOLICY_VERSION=30
  • plat_mapping_file: "/system/etc/selinux/mapping/" + vend_plat_vers + ".cil,使用GSI文件,vend_plat_vers在/vendor/etc/selinux/plat_sepolicy_vers.txt指定,一般为平台版本,此处是34,所以这里使用34.0.cil文件
  • 后面再接vendor部分的参数 完整命令如下:
/home5/develop/u0/alps/out/full_g78v78c2k_dfl_eea-userdebug/out_hal/host/linux-x86/bin/secilc ./plat_sepolicy.cil -m -M true -G -N -c 30 ./34.0.cil -o ./output  ./plat_pub_versioned.cil ./vendor_sepolicy.cil 

执行结果出现错误:

Type proc_agold is invalid for role object_r
Invalid context
Invalid genfscon at ./vendor_sepolicy.cil:45
Failed to verify cil database
Failed to verify cil database
Post process failed
Failed to compile cildb: -1

提示proc_agold找不到,但是在vendor的plat_pub_versioned是有定义的。 这个类型代码中是定义在common中的,表示为private与vendor公用,说明此处这样定义会找不到。

修改方案

调整原先存在vendor中genfs_context文件位置,放在private中,type定义还是在common中。 原先存放vendor中,在烧录了GSI之后,在线编译sepolicy时, 会找不到定义的类型(即使放在了common中,但是无法识别到)导致sepolicy编译报错,从而无法开机。 修改之后,将 vendor 中的 plat_pub_versioned.cil,vendor_sepolicy.cil替换上述命令中的对应的文件,再次执行验证,无错误信息输出,编译成功,说明修改符合预期。

FAQ

由于部分文件,设备需要在开机才创建,无法提标记权限,因此会有很多文件,设备使用genfscon命令标记,在使用此命令的时候需要注意:

  1. 如果文件,设备要绑定的type,要在system,vendor端使用,那么type要在common中定义,即system,vendor都能读到,但是genfscon一定要在private绑定,不要在vendor绑定,
  2. 如果文件,设备要绑定的type,只在system端使用,那么type可以在private定义,同时genfscon也在private中绑定
  3. 如果文件,设备要绑定的type,只在vendor端使用,那么type必须在vendor定义,同时genfscon也在vendor中绑定