SoFunction
Updated on 2025-03-10

Compilation and loading of Android selinux policy files

introduction

Note: This article was written in 2020. The code has changed slightly after Android11 ​​and Android12, but the code logic has not changed.

This article analyzes the loading process of selinux policy files when booting. There are many selinux articles, but not many people who write this.

Compilation

Usage 1:

Compile selinux

make sepolicy -j48 or make selinux_policy -j48

Put the generated file \out\target\product\XXXX\obj\ETC\vendor_sepolicy.cil_intermediates\vendor_sepolicy.cil push (out\target\product\XXXX\vendor\etc\selinux\vendor_sepolicy.cil) into the /vendor/etc/selinux directory

Note: Currently, take the rule file vendor_sepolicy.cil as an example.

  • Delete /odm/etc/selinux/precompiled_sepolicy file after backup
  • Take effect after restarting the phone

Usage 2 (recommended):

Compile selinux

make selinux_policy -j48

Make compiled for about 3~17 minutes. If make is compiled, you can use ninja to compile, and the compilation can be completed in less than a minute.

time prebuilts/build-tools/linux-x86/bin/ninja -f out/ -j48 selinux_policy

time prebuilts/build-tools/linux-x86/bin/ninja -f out/ -j48

  • Backup /odm/etc/selinux/precompiled_sepolicy file
  • Put the generated file out\target\product\klein\odm\etc\selinux\precompiled_sepolicy and push to /odm/etc/selinux/ directory
  • Take effect after restarting the phone

Note: App context seapp_contexts, file context file_contexts, property context property_contexts, see the end of the article

Introduction to the principle:

After the phone starts, enter the loading selinux policy process. The values ​​in the two sha256 files in the /odm/etc/selinux/ directory will be compared with the values ​​in the sha256 files in the /system/etc/selinux and /product/etc/selinux. If they are all equal, the precompiled selinux binary policy file in the /odm/etc/selinux/ directory will be loaded. If it is different, recompile the selinux binary policy file using files under directories such as /system/etc/selinux and /vendor/etc/selinux, and then load the new sepolicy file.

1. Selinux policy loading process (loading sepolicy binary file process)

Function call flow:

system/core/init/

​ ---> ---> int SetupSelinux(char** argv)

​ ---> SelinuxInitialize();

​ ---> LoadPolicy()

​ ---> LoadSplitPolicy()

---> FindPrecompiledSplitPolicy(std::string* file) // Key functions

Function call process explanation:

The selinux policy will be loaded when booting. Call the int SetupSelinux(char** argv) function in, SetupSelinux calls SelinuxInitialize(), SelinuxInitialize() calls the LoadPolicy() function

int SetupSelinux(char** argv) {
	···
    // Set up SELinux, loading the SELinux policy.
    SelinuxSetupKernelLogging();
    SelinuxInitialize();
	···
    return 1;
}
void SelinuxInitialize() {
    ···
    LOG(INFO) << "Loading SELinux policy";
    if (!LoadPolicy()) {
        LOG(FATAL) << "Unable to load SELinux policy";
    }
	···
}

The LoadPolicy method will determine whether the /system/etc/selinux/plat_sepolicy.cil file exists, and there is a call to LoadSplitPolicy, and there is no call to the LoadMonolithicPolicy method to load the selinux policy from the root directory /sepolicy (this path is written in /external/selinux/libselinux/src/android/android_platform.c sepolicy_file variable) (this is the previous version)

[According to the code logic here, after making sepolicy, push the generated file out\target\product\klein\root\sepolicy to the root directory, and delete the /system/etc/selinux/plat_sepolicy.cil file

It should also be possible to make the new selinux policy effective (it was tested that it cannot push to the root directory: Read-only file system)]

bool LoadPolicy() {
    return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
bool IsSplitPolicyDevice() {
    return access(plat_policy_cil_file, R_OK) != -1;
}
bool LoadMonolithicPolicy() {
    LOG(VERBOSE) << "Loading SELinux policy from monolithic file";
    if (selinux_android_load_policy() < 0) {
        PLOG(ERROR) << "Failed to load monolithic SELinux policy";
        return false;
    }
    return true;
}

/external/selinux/libselinux/src/android/android_platform.c

static const char *const sepolicy_file = "/sepolicy";
int selinux_android_load_policy()
{
	int fd = -1;
	fd = open(sepolicy_file, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
	if (fd < 0) {
		selinux_log(SELINUX_ERROR, "SELinux:  Could not open %s:  %s\n",
				sepolicy_file, strerror(errno));
		return -1;
	}
	int ret = selinux_android_load_policy_from_fd(fd, sepolicy_file);
	close(fd);
	return ret;
}

LoadSplitPolicy() method

use_userdebug_policy is determined by the environment variable INIT_FORCE_DEBUGGABLE, use the adb shell command echo $INIT_FORCE_DEBUGGABLE to see that this variable is empty, so use_userdebug_policy == false

The code enters the FindPrecompiledSplitPolicy method.

bool LoadSplitPolicy() {
    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
    // * platform -- policy needed due to logic contained in the system image,
    // * non-platform -- policy needed due to logic contained in the vendor image,
    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
    //   with newer versions of platform policy.
    //
    // secilc is invoked to compile the above three policy files into a single monolithic policy
    // file. This file is then loaded into the kernel.
    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.
    const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
    // You can use the adb shell command echo $INIT_FORCE_DEBUGGABLE to view the value of this environment variable    // This variable is empty, so use_userdebug_policy == false    bool use_userdebug_policy =
            ((force_debuggable_env &amp;&amp; "true"s == force_debuggable_env) &amp;&amp;
             AvbHandle::IsDeviceUnlocked() &amp;&amp; access(kDebugRamdiskSEPolicy, F_OK) == 0);
    if (use_userdebug_policy) {
        LOG(WARNING) &lt;&lt; "Using userdebug system sepolicy";
    }
    // Load precompiled policy from vendor image, if a matching policy is found there. The policy
    // must match the platform policy on the system image.
    std::string precompiled_sepolicy_file;
    // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
    // Thus it cannot use the precompiled policy from vendor image.
    // Core code !use_userdebug_policy == true , enter the FindPrecompiledSplitPolicy function    if (!use_userdebug_policy &amp;&amp; FindPrecompiledSplitPolicy(&amp;precompiled_sepolicy_file)) {
        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
        if (fd != -1) {
            if (selinux_android_load_policy_from_fd(fd, precompiled_sepolicy_file.c_str()) &lt; 0) {
                LOG(ERROR) &lt;&lt; "Failed to load SELinux policy from " &lt;&lt; precompiled_sepolicy_file;
                return false;
            }
            return true;
        }
    }
    // No suitable precompiled policy could be loaded
    LOG(INFO) &lt;&lt; "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/";
    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));//Create temporary files    if (compiled_sepolicy_fd &lt; 0) {
        PLOG(ERROR) &lt;&lt; "Failed to create temporary file " &lt;&lt; compiled_sepolicy;
        return false;
    }
	···
    unlink(compiled_sepolicy);// If the temporary file is no longer used, the file will be automatically deleted    LOG(INFO) &lt;&lt; "Loading compiled SELinux policy";
    // Compilation is completed, loading the new selinux file    if (selinux_android_load_policy_from_fd(compiled_sepolicy_fd, compiled_sepolicy) &lt; 0) {
        LOG(ERROR) &lt;&lt; "Failed to load SELinux policy from " &lt;&lt; compiled_sepolicy;
        return false;
    }
    return true;
}

FindPrecompiledSplitPolicy This method mainly works:

/odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256

/system/etc/selinux/plat_sepolicy_and_mapping.sha256

Comparison of sha values ​​in two files

/odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256

/product/etc/selinux/product_sepolicy_and_mapping.sha256

And compare the sha values ​​in these two files

If the values ​​in these two pairs of files are consistent, FindPrecompiledSplitPolicy returns true, and inconsistent returns false.

If there is no precompiled_sepolicy file in the /odm/etc/selinux/ directory,

Then, you will go to /vendor/etc/selinux/ directory to find the relevant mapping.sha256 to compare with the files in system product.

(Our phones do not have /vendor/etc/selinux/precompiled_sepolicy file)

If there is no precompiled_sepolicy file in the /vendor/etc/selinux/ directory,

Then FindPrecompiledSplitPolicy returns false.

If FindPrecompiledSplitPolicyReturn true, load the precompiled policy

FindPrecompiledSplitPolicyReturn false,butRecompile selinux policy and load the new policy after completion

[From this, it can be inferred that the value in the /odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 file is modified.

Or, modify the value of /odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256

Or, modify the value of /system/etc/selinux/plat_sepolicy_and_mapping.sha256

Or, modify the value of /product/etc/selinux/product_sepolicy_and_mapping.sha256

Or, delete the /odm/etc/selinux/precompiled_sepolicy file

All can cause the new selinux policy to be recompiled after restarting, so that the new selinux policy takes effect]

bool FindPrecompiledSplitPolicy(std::string* file) {
    file-&gt;clear();
    // If there is an odm partition, precompiled_sepolicy will be in
    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
    static constexpr const char vendor_precompiled_sepolicy[] =
        "/vendor/etc/selinux/precompiled_sepolicy";
    static constexpr const char odm_precompiled_sepolicy[] =
        "/odm/etc/selinux/precompiled_sepolicy";
    if (access(odm_precompiled_sepolicy, R_OK) == 0) {
        *file = odm_precompiled_sepolicy;
    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
        *file = vendor_precompiled_sepolicy;
    } else {
        PLOG(INFO) &lt;&lt; "No precompiled sepolicy";
        return false;
    }
    std::string actual_plat_id;
    if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &amp;actual_plat_id)) {
        PLOG(INFO) &lt;&lt; "Failed to read "
                      "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
        return false;
    }
    std::string actual_product_id;
    if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
                       &amp;actual_product_id)) {
        PLOG(INFO) &lt;&lt; "Failed to read "
                      "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
        return false;
    }
    std::string precompiled_plat_id;
    std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
    if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &amp;precompiled_plat_id)) {
        PLOG(INFO) &lt;&lt; "Failed to read " &lt;&lt; precompiled_plat_sha256;
        file-&gt;clear();
        return false;
    }
    std::string precompiled_product_id;
    std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
    if (!ReadFirstLine(precompiled_product_sha256.c_str(), &amp;precompiled_product_id)) {
        PLOG(INFO) &lt;&lt; "Failed to read " &lt;&lt; precompiled_product_sha256;
        file-&gt;clear();
        return false;
    }
    // Core code    if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
        actual_product_id.empty() || actual_product_id != precompiled_product_id) {
        file-&gt;clear();
        return false;
    }
    return true;
}

2. In summary, the methods to make the new selinux policy take effect are:

  • Delete the /system/etc/selinux/plat_sepolicy.cil file, make sepolicy, push the generated file out\target\product\klein\root\sepolicy to the root directory [Unable to push: Read-only file system]
  • After making selinux_policy, push the generated file out\target\product\klein\odm\etc\selinux\precompiled_sepolicy to /odm/etc/selinux/ directory [verified]
  • After making selinux_policy, push the generated file out\target\product\klein\odm\etc\selinux\precompiled_sepolicy, push to /vendor/etc/selinux/ directory, and copy the two sha files in /odm/etc/selinux/ directory to /vendor/etc/selinux/ directory [not verified]
  • Delete or modify /odm/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 [Verified Delete Method]
  • Delete or modify /odm/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256 [Not verified]
  • Delete or modify **/system/etc/selinux/plat_sepolicy_and_mapping.sha256** [Verified Modification Method]
  • Delete or modify /product/etc/selinux/product_sepolicy_and_mapping.sha256 [Verified Delete Method]
  • Delete /odm/etc/selinux/precompiled_sepolicy file [Verified]

3. About starting the computer to compile sepolicy files

A temporary file /dev/ will be compiled when the new selinux takes effect. This file will be deleted.

Current tests have found that compiling sepolicy files at boot time will cause a longer boot time and will be compiled once every time you boot.

Are there any other side effects? Not found yet.

4. Start SELinux related log

1970-01-01 11:56:20.738 0-0/? I/SELinux: Initializing. 1970-01-01 11:56:31.265 0-0/? I/init: Loading SELinux policy 1970-01-01 11:56:31.271 0-0/? I/init: Compiling SELinux policy// This log appears in the log that indicates that when compiling the new selinux, selinux will take effect 1970-01-01 11:56:32.034 0-0/? I/init:Loading compiled SELinux policy1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability network_peer_controls=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability open_perms=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability extended_socket_class=1 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability always_check_network=0 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability cgroup_seclabel=0 1970-01-01 11:56:32.255 0-0/? I/SELinux: policy capability nnp_nosuid_transition=1 1970-01-01 11:56:32.461 0-0/? I/selinux: SELinux: Loaded policy from /dev/sepolicy.ys2KNm// The new selinux takes effect. If the new selinux is not compiled, the loaded here is /odm/etc/selinux/precompiled_sepolicy 1970-01-01 11:56:32.467 0-0/? W/selinux: SELinux: Skipping /product/etc/selinux/product_file_contexts: empty file 1970-01-01 11:56:32.467 0-0/? I/selinux: SELinux: Loaded file_contexts 1970-01-01 11:56:32.524 0-0/? W/selinux: SELinux: Skipping /product/etc/selinux/product_file_contexts: empty file 1970-01-01 11:56:32.524 0-0/? I/selinux: SELinux: Loaded file_contexts

Other tips

  • The context of the app seapp_contexts file can be modified directly and pushed on the phone. Take effect after restart.
  • The property context property_contexts file can be modified directly and pushed on the phone. Take effect after restart.
  • The service context service_contexts file can be modified directly and pushed on the phone. Take effect after restart.
  • Virtual file context genfs_contexts file, modified by chcon command
  • Let’s talk about the file context file_contexts file modification and testing. The restorecon command enables the file context selinux policy to take effect. restorecon -R

chcon: Ability to modify the selinux lable of a certain file (folder). Ex: chcon u:object_r:system_data_file:s0 /data/app

restorecon: Relable the specified file (folder) according to the rules defined in sepolicy Rule.

Modify /system/bin/toybox context example: Modify the junkserver context to shell_exec

  • First modify the /system/etc/selinux/plat_file_contexts file content
  • Put /system/bin/toybox u:object_r:toolbox_exec:s0
  • Modify to: /system/bin/toybox u:object_r:shell_exec:s0

Enter the mobile phone shell and execute the following command

# restorecon command needs to be followed by parameters, and a single command cannot be executedmobius:/ # restorecon system/bin/toybox -v
SELinux:  Skipping /product/etc/selinux/product_file_contexts:  empty file
SELinux: Loaded file_contexts
SELinux:  Relabeling /system/bin/toybox from u:object_r:toolbox_exec:s0 to u:object_r:shell_exec:s0.
  • You can see that the file context takes effect
mobius:/ # ls system/bin/toybox -lZ
-rwxrwxrwx 1 root shell u:object_r:shell_exec:s0 432976 2009-01-01 08:00 system/bin/toybox

chcon

# android
chcon  &lt;Security context&gt; document
chcon -R  &lt;Security context&gt; Table of contents
# Exampleklein:/ # chcon -v u:object_r:junkserverd_d_file:s0  /data/junk-server/
chcon '/data/junk-server/' to u:object_r:junkserverd_d_file:s0
# 
chcon -R -v u:object_r:system_data_file:s0 ./0
# linux:
chcon -t &lt;Security context&gt; document
chcon -R -t &lt;Security context&gt; Table of contents

The above is the detailed content of compiling and loading of Android selinux policy files. For more information about Android selinux policy compilation, please follow my other related articles!