Migrate magiskinit selinux.cpp to Rust

This commit is contained in:
topjohnwu 2025-04-06 02:04:59 -07:00 committed by John Wu
parent 83e66767ff
commit d4a0286e13
10 changed files with 361 additions and 260 deletions

View File

@ -66,7 +66,6 @@ LOCAL_SRC_FILES := \
init/mount.cpp \
init/rootdir.cpp \
init/getinfo.cpp \
init/selinux.cpp \
init/init-rs.cpp
LOCAL_LDFLAGS := -static

View File

@ -1,4 +1,4 @@
use cxx::{type_id, ExternType};
use cxx::{ExternType, type_id};
use libc::c_char;
use std::borrow::Borrow;
use std::cmp::min;
@ -479,6 +479,11 @@ impl FsPath {
unsafe { mem::transmute(value.as_ref()) }
}
#[inline(always)]
pub const fn from_utfcstr(value: &Utf8CStr) -> &FsPath {
unsafe { mem::transmute(value) }
}
#[inline(always)]
pub fn from_mut<T: AsMut<Utf8CStr> + ?Sized>(value: &mut T) -> &mut FsPath {
unsafe { mem::transmute(value.as_mut()) }
@ -781,14 +786,10 @@ macro_rules! cstr {
#[macro_export]
macro_rules! raw_cstr {
($str:expr) => {{
$crate::cstr!($str).as_ptr()
}};
($str:expr) => {{ $crate::cstr!($str).as_ptr() }};
}
#[macro_export]
macro_rules! path {
($str:expr) => {{
$crate::FsPath::from($crate::cstr!($str))
}};
($str:expr) => {{ $crate::FsPath::from_utfcstr($crate::cstr!($str)) }};
}

View File

@ -199,7 +199,7 @@ impl FsPath {
}
pub fn create(&self, flags: i32, mode: mode_t) -> io::Result<File> {
Ok(File::from(open_fd!(self, flags, mode)?))
Ok(File::from(open_fd!(self, O_CREAT | flags, mode)?))
}
pub fn exists(&self) -> bool {
@ -431,6 +431,10 @@ impl FsPath {
pub fn create_symlink_to(&self, target: &FsPath) -> io::Result<()> {
unsafe { libc::symlink(target.as_ptr(), self.as_ptr()).as_os_err() }
}
pub fn mkfifo(&self, mode: mode_t) -> io::Result<()> {
unsafe { libc::mkfifo(self.as_ptr(), mode).as_os_err() }
}
}
impl FsPathFollow {

View File

@ -1,4 +1,4 @@
use crate::{FsPath, LibcReturn};
use crate::{FsPath, LibcReturn, Utf8CStr};
use libc::c_ulong;
use std::ptr;
@ -29,6 +29,19 @@ impl FsPath {
}
}
pub fn remount_with_data(&self, data: &Utf8CStr) -> std::io::Result<()> {
unsafe {
libc::mount(
ptr::null(),
self.as_ptr(),
ptr::null(),
libc::MS_REMOUNT,
data.as_ptr().cast(),
)
.as_os_err()
}
}
pub fn move_mount_to(&self, path: &FsPath) -> std::io::Result<()> {
unsafe {
libc::mount(

View File

@ -27,3 +27,4 @@ pub const DEVICEDIR: &str = concatcp!(INTERNAL_DIR, "/device");
pub const PREINITDEV: &str = concatcp!(DEVICEDIR, "/preinit");
pub const ROOTOVL: &str = concatcp!(INTERNAL_DIR, "/rootdir");
pub const ROOTMNT: &str = concatcp!(ROOTOVL, "/.mount_list");
pub const SELINUXMOCK: &str = concatcp!(INTERNAL_DIR, "/selinux");

View File

@ -11,10 +11,30 @@
#include <base.hpp>
#include <stream.hpp>
#include <sepolicy.hpp>
#include "init-rs.hpp"
int magisk_proxy_main(int, char *argv[]);
rust::Utf8CStr backup_init();
// Expose some constants to Rust
static inline rust::Utf8CStr split_plat_cil() {
return SPLIT_PLAT_CIL;
};
static inline rust::Utf8CStr preload_lib() {
return PRELOAD_LIB;
}
static inline rust::Utf8CStr preload_policy() {
return PRELOAD_POLICY;
}
static inline rust::Utf8CStr preload_ack() {
return PRELOAD_ACK;
}
#endif

View File

@ -3,11 +3,12 @@
#![feature(try_blocks)]
#![allow(clippy::missing_safety_doc)]
use base::FsPath;
use logging::setup_klog;
// Has to be pub so all symbols in that crate is included
pub use magiskpolicy;
use mount::{is_device_mounted, switch_root};
use rootdir::{inject_magisk_rc, OverlayAttr};
use rootdir::{OverlayAttr, inject_magisk_rc};
#[path = "../include/consts.rs"]
mod consts;
@ -16,6 +17,7 @@ mod init;
mod logging;
mod mount;
mod rootdir;
mod selinux;
mod twostage;
#[cxx::bridge]
@ -56,6 +58,12 @@ pub mod ffi {
unsafe fn magisk_proxy_main(argc: i32, argv: *mut *mut c_char) -> i32;
fn backup_init() -> Utf8CStrRef<'static>;
// Constants
fn split_plat_cil() -> Utf8CStrRef<'static>;
fn preload_lib() -> Utf8CStrRef<'static>;
fn preload_policy() -> Utf8CStrRef<'static>;
fn preload_ack() -> Utf8CStrRef<'static>;
}
#[namespace = "rust"]
@ -81,6 +89,7 @@ pub mod ffi {
type OverlayAttr;
fn parse_config_file(self: &mut MagiskInit);
fn mount_overlay(self: &mut MagiskInit, dest: Utf8CStrRef);
fn handle_sepolicy(self: &mut MagiskInit);
fn restore_overlay_contexts(self: &MagiskInit);
}
unsafe extern "C++" {
@ -94,7 +103,21 @@ pub mod ffi {
fn collect_devices(self: &MagiskInit);
fn mount_preinit_dir(self: &MagiskInit);
unsafe fn find_block(self: &MagiskInit, partname: *const c_char) -> u64;
fn handle_sepolicy(self: &mut MagiskInit);
unsafe fn patch_fissiond(self: &mut MagiskInit, tmp_path: *const c_char);
}
}
#[inline(always)]
fn preload_lib() -> &'static FsPath {
FsPath::from(ffi::preload_lib())
}
#[inline(always)]
fn preload_policy() -> &'static FsPath {
FsPath::from(ffi::preload_policy())
}
#[inline(always)]
fn preload_ack() -> &'static FsPath {
FsPath::from(ffi::preload_ack())
}

View File

@ -1,246 +0,0 @@
#include <sys/mount.h>
#include <sys/xattr.h>
#include <consts.hpp>
#include <sepolicy.hpp>
#include "init.hpp"
using namespace std;
#define POLICY_VERSION "/selinux_version"
#define MOCK_VERSION SELINUXMOCK "/version"
#define MOCK_LOAD SELINUXMOCK "/load"
#define MOCK_ENFORCE SELINUXMOCK "/enforce"
#define MOCK_REQPROT SELINUXMOCK "/checkreqprot"
static void mock_fifo(const char *target, const char *mock) {
LOGD("Hijack [%s]\n", target);
mkfifo(mock, 0666);
xmount(mock, target, nullptr, MS_BIND, nullptr);
}
static void mock_file(const char *target, const char *mock) {
LOGD("Hijack [%s]\n", target);
close(xopen(mock, O_CREAT | O_RDONLY, 0666));
xmount(mock, target, nullptr, MS_BIND, nullptr);
}
enum SePatchStrategy {
// 2SI, Android 10+
// On 2SI devices, the 2nd stage init is always a dynamic executable.
// This meant that instead of going through convoluted hacks, we can just
// LD_PRELOAD and replace security_load_policy with our own implementation.
LD_PRELOAD,
// Treble enabled, Android 8.0+
// selinuxfs is mounted in init.cpp. Errors when mounting selinuxfs is ignored,
// which means that we can directly mount selinuxfs ourselves and hijack nodes in it.
SELINUXFS,
// Dynamic patching, Android 6.0 - 7.1
// selinuxfs is mounted in libselinux's selinux_android_load_policy(). Errors when
// mounting selinuxfs is fatal, which means we need to block init's control flow after
// it mounted selinuxfs for us, then we can hijack nodes in it.
LEGACY,
};
void MagiskInit::handle_sepolicy() noexcept {
xmkdir(SELINUXMOCK, 0711);
// Read all custom rules into memory
string rules;
auto rule = "/data/" PREINITMIRR "/sepolicy.rule";
if (xaccess(rule, R_OK) == 0) {
LOGD("Loading custom sepolicy patch: [%s]\n", rule);
full_read(rule, rules);
}
// Step 0: determine strategy
SePatchStrategy strat;
if (access("/system/bin/init", F_OK) == 0) {
strat = LD_PRELOAD;
} else {
auto init = mmap_data("/init");
if (init.contains(SPLIT_PLAT_CIL)) {
// Supports split policy
strat = SELINUXFS;
} else if (init.contains(POLICY_VERSION)) {
// Does not support split policy, hijack /selinux_version
strat = LEGACY;
} else {
LOGE("Unknown sepolicy setup, abort...\n");
return;
}
}
// Step 1: setup for intercepting init boot control flow
switch (strat) {
case LD_PRELOAD: {
LOGI("SePatchStrategy: LD_PRELOAD\n");
cp_afc("init-ld", PRELOAD_LIB);
setenv("LD_PRELOAD", PRELOAD_LIB, 1);
mkfifo(PRELOAD_ACK, 0666);
break;
}
case SELINUXFS: {
LOGI("SePatchStrategy: SELINUXFS\n");
if (access(SELINUX_ENFORCE, F_OK) != 0) {
// selinuxfs was not already mounted, mount it ourselves
// Remount procfs with proper options
xmount(nullptr, "/proc", nullptr, MS_REMOUNT, "hidepid=2,gid=3009");
// Preserve sysfs and procfs
decltype(mount_list) new_mount_list;
std::remove_copy_if(
mount_list.begin(), mount_list.end(),
std::back_inserter(new_mount_list),
[](const auto &s) { return s == "/proc" || s == "/sys"; });
new_mount_list.swap(mount_list);
// Mount selinuxfs
xmount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, nullptr);
}
mock_file(SELINUX_LOAD, MOCK_LOAD);
mock_fifo(SELINUX_ENFORCE, MOCK_ENFORCE);
break;
}
case LEGACY: {
LOGI("SePatchStrategy: LEGACY\n");
if (access(POLICY_VERSION, F_OK) != 0) {
// The file does not exist, create one
close(xopen(POLICY_VERSION, O_RDONLY | O_CREAT, 0644));
}
// The only purpose of this is to block init's control flow after it mounts selinuxfs
// and before it calls security_load_policy().
// Target: selinux_android_load_policy() -> set_policy_index() -> open(POLICY_VERSION)
mock_fifo(POLICY_VERSION, MOCK_VERSION);
break;
}
}
// Create a new process waiting for init operations
if (xfork()) {
return;
}
// Step 2: wait for selinuxfs to be mounted (only for LEGACY)
if (strat == LEGACY) {
// Busy wait until selinuxfs is mounted
while (access(SELINUX_ENFORCE, F_OK)) {
// Retry every 100ms
usleep(100000);
}
// On Android 6.0, init does not call security_getenforce() first; instead it directly
// call security_setenforce() after security_load_policy(). What's even worse, it opens the
// enforce node with O_RDWR, which will not block when opening FIFO files. As a workaround,
// we do not mock the enforce node, and block init with mock checkreqprot instead.
// Android 7.0 - 7.1 doesn't have this issue, but for simplicity, let's just use the
// same blocking strategy for both since it also works just fine.
mock_file(SELINUX_LOAD, MOCK_LOAD);
mock_fifo(SELINUX_REQPROT, MOCK_REQPROT);
// This will unblock init at selinux_android_load_policy() -> set_policy_index().
close(xopen(MOCK_VERSION, O_WRONLY));
xumount2(POLICY_VERSION, MNT_DETACH);
// libselinux does not read /selinux_version after open; instead it mmap the file,
// which can never succeed on FIFO files. This is fine as set_policy_index() will just
// fallback to the default index 0.
}
// Step 3: obtain sepolicy, patch, and load the patched sepolicy
if (strat == LD_PRELOAD) {
// This open will block until preload.so finish writing the sepolicy
owned_fd ack_fd = xopen(PRELOAD_ACK, O_WRONLY);
auto sepol = SePolicy::from_file(PRELOAD_POLICY);
// Remove the files before loading the policy
unlink(PRELOAD_POLICY);
unlink(PRELOAD_ACK);
sepol.magisk_rules();
sepol.load_rules(rules);
sepol.to_file(SELINUX_LOAD);
// restore mounted files' context after sepolicy loaded
restore_overlay_contexts();
// Write ack to restore preload.so's control flow
xwrite(ack_fd, &ack_fd, 1);
} else {
int mock_enforce = -1;
if (strat == LEGACY) {
// Busy wait until sepolicy is fully written.
struct stat st{};
decltype(st.st_size) sz;
do {
sz = st.st_size;
// Check every 100ms
usleep(100000);
xstat(MOCK_LOAD, &st);
} while (sz == 0 || sz != st.st_size);
} else {
// This open will block until init calls security_getenforce().
mock_enforce = xopen(MOCK_ENFORCE, O_WRONLY);
}
// Cleanup the hijacks
umount2("/init", MNT_DETACH);
xumount2(SELINUX_LOAD, MNT_DETACH);
umount2(SELINUX_ENFORCE, MNT_DETACH);
umount2(SELINUX_REQPROT, MNT_DETACH);
auto sepol = SePolicy::from_file(MOCK_LOAD);
sepol.magisk_rules();
sepol.load_rules(rules);
sepol.to_file(SELINUX_LOAD);
// For some reason, restorecon on /init won't work in some cases
setxattr("/init", XATTR_NAME_SELINUX, "u:object_r:init_exec:s0", 24, 0);
// restore mounted files' context after sepolicy loaded
restore_overlay_contexts();
// We need to make sure the actual init process is blocked until sepolicy is loaded,
// or else restorecon will fail and re-exec won't change context, causing boot failure.
// We (ab)use the fact that init either reads the enforce node, or writes the checkreqprot
// node, and because both has been replaced with FIFO files, init will block until we
// handle it, effectively hijacking its control flow until the patched sepolicy is loaded.
if (strat == LEGACY) {
// init is blocked on checkreqprot, write to the real node first, then
// unblock init by opening the mock FIFO.
owned_fd real_req = xopen(SELINUX_REQPROT, O_WRONLY);
xwrite(real_req, "0", 1);
owned_fd mock_req = xopen(MOCK_REQPROT, O_RDONLY);
full_read(mock_req);
} else {
// security_getenforce was called
string data = full_read(SELINUX_ENFORCE);
xwrite(mock_enforce, data.data(), data.length());
close(mock_enforce);
}
}
// At this point, the init process will be unblocked
// and continue on with restorecon + re-exec.
// Terminate process
exit(0);
}

280
native/src/init/selinux.rs Normal file
View File

@ -0,0 +1,280 @@
use crate::consts::{PREINITMIRR, SELINUXMOCK};
use crate::ffi::{MagiskInit, split_plat_cil};
use crate::{preload_ack, preload_lib, preload_policy};
use base::const_format::concatcp;
use base::{
BytesExt, FsPath, LibcReturn, LoggedResult, MappedFile, ResultExt, cstr, debug, error, info,
libc, path, raw_cstr,
};
use magiskpolicy::ffi::SePolicy;
use std::io::{Read, Write};
use std::ptr;
use std::thread::sleep;
use std::time::Duration;
const POLICY_VERSION: &FsPath = path!("/selinux_version");
const MOCK_VERSION: &FsPath = path!(concatcp!(SELINUXMOCK, "/version"));
const MOCK_LOAD: &FsPath = path!(concatcp!(SELINUXMOCK, "/load"));
const MOCK_ENFORCE: &FsPath = path!(concatcp!(SELINUXMOCK, "/enforce"));
const MOCK_REQPROT: &FsPath = path!(concatcp!(SELINUXMOCK, "/checkreqprot"));
const SELINUX_MNT: &str = "/sys/fs/selinux";
const SELINUX_ENFORCE: &FsPath = path!(concatcp!(SELINUX_MNT, "/enforce"));
const SELINUX_LOAD: &FsPath = path!(concatcp!(SELINUX_MNT, "/load"));
const SELINUX_REQPROT: &FsPath = path!(concatcp!(SELINUX_MNT, "/checkreqprot"));
enum SePatchStrategy {
// 2SI, Android 10+
// On 2SI devices, the 2nd stage init is always a dynamic executable.
// This meant that instead of going through convoluted hacks, we can just
// LD_PRELOAD and replace security_load_policy with our own implementation.
LdPreload,
// Treble enabled, Android 8.0+
// selinuxfs is mounted in init.cpp. Errors when mounting selinuxfs is ignored,
// which means that we can directly mount selinuxfs ourselves and hijack nodes in it.
SelinuxFs,
// Dynamic patching, Android 6.0 - 7.1
// selinuxfs is mounted in libselinux's selinux_android_load_policy(). Errors when
// mounting selinuxfs is fatal, which means we need to block init's control flow after
// it mounted selinuxfs for us, then we can hijack nodes in it.
Legacy,
}
// Note for non-LD_PRELOAD strategy:
//
// We need to make sure the actual init process is blocked until sepolicy is loaded,
// or else restorecon will fail and re-exec won't change context, causing boot failure.
// We (ab)use the fact that init either reads the enforce node, or writes the checkreqprot
// node, and because both has been replaced with FIFO files, init will block until we
// handle it, effectively hijacking its control flow until the patched sepolicy is loaded.
fn mock_fifo(target: &FsPath, mock: &FsPath) -> LoggedResult<()> {
debug!("Hijack [{}]", target);
mock.mkfifo(0o666)?;
mock.bind_mount_to(target).log()
}
fn mock_file(target: &FsPath, mock: &FsPath) -> LoggedResult<()> {
debug!("Hijack [{}]", target);
drop(mock.create(libc::O_RDONLY, 0o666)?);
mock.bind_mount_to(target).log()
}
impl MagiskInit {
pub(crate) fn handle_sepolicy(&mut self) {
self.handle_sepolicy_impl().ok();
}
fn cleanup_and_load(&self, rules: &str) {
// Cleanup the hijacks
path!("/init").unmount().ok();
SELINUX_LOAD.unmount().log_ok();
SELINUX_ENFORCE.unmount().ok();
SELINUX_REQPROT.unmount().ok();
let mut sepol = SePolicy::from_file(MOCK_LOAD);
sepol.magisk_rules();
sepol.load_rules(rules);
sepol.to_file(SELINUX_LOAD);
// For some reason, restorecon on /init won't work in some cases
path!("/init")
.follow_link()
.set_secontext(cstr!("u:object_r:init_exec:s0"))
.ok();
// restore mounted files' context after sepolicy loaded
self.restore_overlay_contexts();
}
fn handle_sepolicy_impl(&mut self) -> LoggedResult<()> {
path!(SELINUXMOCK).mkdir(0o711)?;
let mut rules = String::new();
let rule_file = path!(concatcp!("/data/", PREINITMIRR, "/sepolicy.rule"));
if rule_file.exists() {
debug!("Loading custom sepolicy patch: [{}]", rule_file);
rule_file.open(libc::O_RDONLY)?.read_to_string(&mut rules)?;
}
// Step 0: determine strategy
let strat: SePatchStrategy;
if path!("/system/bin/init").exists() {
strat = SePatchStrategy::LdPreload;
} else {
let init = MappedFile::open(cstr!("/init"))?;
if init.contains(split_plat_cil().as_str().as_bytes()) {
// Supports split policy
strat = SePatchStrategy::SelinuxFs;
} else if init.contains(POLICY_VERSION.as_bytes()) {
// Does not support split policy, hijack /selinux_version
strat = SePatchStrategy::Legacy;
} else {
error!("Unknown sepolicy setup, abort...");
return Ok(());
}
}
// Step 1: setup for intercepting init boot control flow
match strat {
SePatchStrategy::LdPreload => {
info!("SePatchStrategy: LD_PRELOAD");
path!("init-ld").copy_to(preload_lib())?;
unsafe {
libc::setenv(raw_cstr!("LD_PRELOAD"), preload_lib().as_ptr(), 1);
}
preload_ack().mkfifo(0o666)?;
}
SePatchStrategy::SelinuxFs => {
info!("SePatchStrategy: SELINUXFS");
if !SELINUX_ENFORCE.exists() {
// selinuxfs was not already mounted, mount it ourselves
// Remount procfs with proper options
path!("/proc").remount_with_data(cstr!("hidepid=2,gid=3009"))?;
// Preserve sysfs and procfs
self.mount_list.retain(|s| s != "/proc" && s != "/sys");
// Mount selinuxfs
unsafe {
libc::mount(
raw_cstr!("selinuxfs"),
raw_cstr!(SELINUX_MNT),
raw_cstr!("selinuxfs"),
0,
ptr::null(),
)
.as_os_err()?;
}
}
mock_file(SELINUX_LOAD, MOCK_LOAD)?;
mock_fifo(SELINUX_ENFORCE, MOCK_ENFORCE)?;
}
SePatchStrategy::Legacy => {
info!("SePatchStrategy: LEGACY");
if !POLICY_VERSION.exists() {
// The file does not exist, create one
drop(POLICY_VERSION.create(libc::O_RDONLY, 0o666)?);
}
// The only purpose of this is to block init's control flow after it mounts
// selinuxfs and before it calls security_load_policy().
// selinux_android_load_policy() -> set_policy_index() -> open(POLICY_VERSION)
mock_fifo(POLICY_VERSION, MOCK_VERSION)?;
}
}
// Create a new process waiting for init operations
let pid = unsafe { libc::fork().check_os_err()? };
if pid != 0 {
return Ok(());
}
// Step 2: wait for selinuxfs to be mounted (only for LEGACY)
let wait = Duration::from_millis(100);
if matches!(strat, SePatchStrategy::Legacy) {
// Busy wait until selinuxfs is mounted
while !SELINUX_ENFORCE.exists() {
// Retry every 100ms
sleep(wait);
}
// On Android 6.0, init does not call security_getenforce() first; instead it directly
// call security_setenforce() after security_load_policy(). What's even worse, it opens
// the enforce node with O_RDWR, which will not block when opening FIFO files.
// As a workaround, we do not mock the enforce node, and block init with mocking
// checkreqprot instead.
// Android 7.0 - 7.1 doesn't have this issue, but for simplicity, let's just use the
// same blocking strategy for both since it also works just fine.
mock_file(SELINUX_LOAD, MOCK_LOAD)?;
mock_fifo(SELINUX_REQPROT, MOCK_REQPROT)?;
// This will unblock init at selinux_android_load_policy() -> set_policy_index().
drop(MOCK_VERSION.open(libc::O_WRONLY)?);
POLICY_VERSION.unmount()?;
// libselinux does not read /selinux_version after open; instead it mmap the file,
// which can never succeed on FIFO files. This is fine as set_policy_index() will just
// fallback to the default index 0.
}
// Step 3: obtain sepolicy, patch, and load the patched sepolicy
match strat {
SePatchStrategy::LdPreload => {
// This open will block until preload.so finish writing the sepolicy
let mut ack_fd = preload_ack().open(libc::O_WRONLY)?;
let mut sepol = SePolicy::from_file(preload_policy());
// Remove the files before loading the policy
preload_policy().remove()?;
preload_ack().remove()?;
sepol.magisk_rules();
sepol.load_rules(&rules);
sepol.to_file(SELINUX_LOAD);
self.restore_overlay_contexts();
// Write ack to restore preload.so's control flow
ack_fd.write_all("0".as_bytes())?;
}
SePatchStrategy::SelinuxFs => {
// This open will block until init calls security_getenforce().
let mut mock_enforce = MOCK_ENFORCE.open(libc::O_WRONLY)?;
self.cleanup_and_load(&rules);
// security_getenforce was called, read from real and redirect to mock
let mut data = vec![];
SELINUX_ENFORCE
.open(libc::O_RDONLY)?
.read_to_end(&mut data)?;
mock_enforce.write_all(&data)?;
}
SePatchStrategy::Legacy => {
let mut sz = 0_usize;
// Busy wait until sepolicy is fully written.
loop {
let attr = MOCK_LOAD.get_attr()?;
if sz != 0 && sz == attr.st.st_size as usize {
break;
}
sz = attr.st.st_size as usize;
// Check every 100ms
sleep(wait);
}
self.cleanup_and_load(&rules);
// init is blocked on checkreqprot, write to the real node first, then
// unblock init by opening the mock FIFO.
SELINUX_REQPROT
.open(libc::O_WRONLY)?
.write_all("0".as_bytes())?;
let mut v = vec![];
MOCK_REQPROT.open(libc::O_RDONLY)?.read_to_end(&mut v)?;
}
}
// At this point, the init process will be unblocked
// and continue on with restorecon + re-exec.
// Terminate process
std::process::exit(0);
}
}

View File

@ -87,14 +87,20 @@ pub mod ffi {
fn parse_statement(self: &mut SePolicy, statement: Utf8CStrRef);
fn magisk_rules(self: &mut SePolicy);
fn load_rule_file(self: &mut SePolicy, filename: Utf8CStrRef);
fn load_rules(self: &mut SePolicy, rules: &CxxString);
#[cxx_name = "load_rules"]
fn load_rules_for_cxx(self: &mut SePolicy, rules: &CxxString);
#[Self = SePolicy]
fn xperm_to_string(perm: &Xperm) -> String;
}
}
impl SePolicy {
fn load_rules(self: &mut SePolicy, rules: &CxxString) {
fn load_rules_for_cxx(self: &mut SePolicy, rules: &CxxString) {
let mut cursor = Cursor::new(rules.as_bytes());
self.load_rules_from_reader(&mut cursor);
}
pub fn load_rules(self: &mut SePolicy, rules: &str) {
let mut cursor = Cursor::new(rules.as_bytes());
self.load_rules_from_reader(&mut cursor);
}