Vendor Init

The init process has nearly unrestricted permissions and uses input scripts from both the system and vendor partitions to initialize the system during the boot process. This access causes a huge hole in the Treble system/vendor split, as vendor scripts may instruct init to access files, properties, etc. that do not form part of the stable system-vendor application binary interface (ABI).

Vendor init is designed to close this hole by using a separate security-enhanced Linux (SELinux) domain vendor_init to run commands found in /vendor with vendor-specific permissions.


Vendor init forks a subprocess of init early in the boot process with the SELinux context u:r:vendor_init:s0. This SELinux context has considerably fewer permissions than the default init context and its access is confined to files, properties, etc. that are either vendor-specific or part of the stable system-vendor ABI.

Init checks each script it loads to see if its path starts with /vendor and if so, tags it with an indication that its commands must be run in the vendor init context. Each init builtin is annotated with a boolean that specifies whether or not the command must be run in the vendor init subprocess:

  • Most commands that access the file system are annotated to run in the vendor init subprocess and are therefore subjected to the vendor init SEPolicy.
  • Most commands that impact internal init state (e.g., starting and stopping services) are run within the normal init process. These commands are made aware that a vendor script is calling them to do their own non-SELinux permissions handling.

The main processing loop of init contains a check that if a command is annotated to run in the vendor subprocess and originates from a vendor script, that command is sent via inter-process communication (IPC) to the vendor init subprocess, which runs the command and sends the result back to init.

Using Vendor Init

Vendor init is enabled by default and its restrictions apply to all init scripts present in the /vendor partition. Vendor init should be transparent to vendors whose scripts are already not accessing system only files, properties, etc.

However, if commands in a given vendor script violate the vendor init restrictions, the commands will fail. Failing commands have a line in the kernel log (visible with dmesg) from init indicating failure. An SELinux audit accompanies any failing command that failed due to the SELinux policy. Example of a failure including an SELinux audit:

type=1400 audit(1511821362.996:9): avc: denied { search } for pid=540 comm="init" name="nfc" dev="sda45" ino=1310721 scontext=u:r:vendor_init:s0 tcontext=u:object_r:nfc_data_file:s0 tclass=dir permissive=0
init: Command 'write /data/nfc/bad_file_access 1234' action=boot (/vendor/etc/init/hw/init.walleye.rc:422) took 2ms and failed: Unable to write to file '/data/nfc/bad_file_access': open() failed: Permission denied

If a command fails, there are two options:

  • If the command is failing due to an intended restriction (such as if the command is accessing a system file or property), the command must be re-implemented in a Treble-friendly way, going through only stable interfaces. Neverallow rules prevent adding permissions to access system files that are not part of the stable system-vendor ABI.
  • If the SELinux label is new and is not already granted permissions in the system vendor_init.te nor excluded permissions via the neverallow rules, the new label may be granted permissions in the device-specific vendor_init.te.

For devices launching before Android 9, the neverallows rules may be bypassed by adding the data_between_core_and_vendor_violators typeattribute to the device-specific vendor_init.te file.

For devices launching with Android 9, a GTS check prevents the usage of data_between_core_and_vendor_violators.

Code Locations

The bulk of the logic for the vendor init IPC is in system/core/init/subcontext.cpp.

The table of commands is in the BuiltinFunctionMap class in system/core/init/builtins.cpp and includes annotations that indicate if the command must run in the vendor init subprocess.

The SEPolicy for vendor init is split across the private (system/sepolicy/private/vendor_init.te) and public (system/sepolicy/public/vendor_init.te) directories in system/sepolicy.