diff --git a/native/src/Cargo.lock b/native/src/Cargo.lock index 90e6ba8de..4da9ca543 100644 --- a/native/src/Cargo.lock +++ b/native/src/Cargo.lock @@ -6,6 +6,7 @@ version = 3 name = "base" version = "0.0.0" dependencies = [ + "cfg-if", "cxx", "libc", ] @@ -16,6 +17,12 @@ version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "cxx" version = "1.0.72" diff --git a/native/src/base/Android.mk b/native/src/base/Android.mk index df3b7ca37..637f41cdd 100644 --- a/native/src/base/Android.mk +++ b/native/src/base/Android.mk @@ -14,7 +14,6 @@ LOCAL_SRC_FILES := \ misc.cpp \ selinux.cpp \ logging.cpp \ - xwrap.cpp \ stream.cpp \ ../external/cxx-rs/src/cxx.cc include $(BUILD_STATIC_LIBRARY) diff --git a/native/src/base/Cargo.toml b/native/src/base/Cargo.toml index ea0bc7099..a487fb03d 100644 --- a/native/src/base/Cargo.toml +++ b/native/src/base/Cargo.toml @@ -9,3 +9,4 @@ path = "lib.rs" [dependencies] cxx = { path = "../external/cxx-rs" } libc = "0.2" +cfg-if = "1.0" diff --git a/native/src/base/files.cpp b/native/src/base/files.cpp index 11a9ff7cc..417c4751f 100644 --- a/native/src/base/files.cpp +++ b/native/src/base/files.cpp @@ -9,38 +9,12 @@ using namespace std; -ssize_t fd_path(int fd, char *path, size_t size) { - ssprintf(path, size, "/proc/self/fd/%d", fd); - return xreadlink(path, path, size); -} - int fd_pathat(int dirfd, const char *name, char *path, size_t size) { - if (fd_path(dirfd, path, size) < 0) + if (fd_path(dirfd, byte_slice(path, size)) < 0) return -1; auto len = strlen(path); path[len] = '/'; - strlcpy(path + len + 1, name, size - len - 1); - return 0; -} - -int mkdirs(const char *path, mode_t mode) { - char buf[4096]; - strlcpy(buf, path, sizeof(buf)); - errno = 0; - for (char *p = &buf[1]; *p; ++p) { - if (*p == '/') { - *p = '\0'; - if (mkdir(buf, mode) == -1) { - if (errno != EEXIST) - return -1; - } - *p = '/'; - } - } - if (mkdir(buf, mode) == -1) { - if (errno != EEXIST) - return -1; - } + strscpy(path + len + 1, name, size - len - 1); return 0; } @@ -410,14 +384,14 @@ void parse_mnt(const char *file, const function &fn) { void backup_folder(const char *dir, vector &files) { char path[PATH_MAX]; - xrealpath(dir, path); + xcanonical_path(dir, path, sizeof(path)); int len = strlen(path); pre_order_walk(xopen(dir, O_RDONLY), [&](int dfd, dirent *entry) -> walk_result { int fd = xopenat(dfd, entry->d_name, O_RDONLY); if (fd < 0) return SKIP; run_finally f([&]{ close(fd); }); - if (fd_path(fd, path, sizeof(path)) < 0) + if (fd_path(fd, byte_slice(path, sizeof(path))) < 0) return SKIP; raw_file file; file.path = path + len + 1; diff --git a/native/src/base/files.hpp b/native/src/base/files.hpp index 787c489ae..708cdb110 100644 --- a/native/src/base/files.hpp +++ b/native/src/base/files.hpp @@ -58,9 +58,16 @@ struct mmap_data : public byte_data { mmap_data& operator=(mmap_data &&other) { swap(other); return *this; } }; -ssize_t fd_path(int fd, char *path, size_t size); -int fd_pathat(int dirfd, const char *name, char *path, size_t size); +extern "C" { + int mkdirs(const char *path, mode_t mode); +ssize_t canonical_path(const char *path, char *buf, size_t bufsiz); +ssize_t read_link(const char *pathname, char *buf, size_t bufsiz); + +} // extern "C" + +using rust::fd_path; +int fd_pathat(int dirfd, const char *name, char *path, size_t size); void rm_rf(const char *path); void mv_path(const char *src, const char *dest); void mv_dir(int src, int dest); diff --git a/native/src/base/files.rs b/native/src/base/files.rs new file mode 100644 index 000000000..60e40b14f --- /dev/null +++ b/native/src/base/files.rs @@ -0,0 +1,112 @@ +use std::ffi::CStr; +use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd, RawFd}; + +use libc::{c_char, mode_t, EEXIST, ENOENT, O_CLOEXEC, O_PATH}; + +use crate::{bfmt_cstr, errno, xopen}; + +mod unsafe_impl { + use std::ffi::CStr; + use std::slice; + + use libc::c_char; + + #[no_mangle] + pub unsafe extern "C" fn read_link(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { + let r = libc::readlink(path, buf.cast(), bufsz - 1); + if r >= 0 { + *buf.offset(r) = b'\0'; + } + return r; + } + + #[no_mangle] + unsafe extern "C" fn canonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { + super::canonical_path(CStr::from_ptr(path), slice::from_raw_parts_mut(buf, bufsz)) + } +} + +pub fn __open_fd_impl(path: &CStr, flags: i32, mode: mode_t) -> Option { + let fd = xopen(path.as_ptr(), flags, mode); + if fd >= 0 { + unsafe { Some(OwnedFd::from_raw_fd(fd)) } + } else { + None + } +} + +#[macro_export] +macro_rules! open_fd { + ($path:expr, $flags:expr) => { + crate::__open_fd_impl($path, $flags, 0) + }; + ($path:expr, $flags:expr, $mode:expr) => { + crate::__open_fd_impl($path, $flags, $mode) + }; +} + +pub fn readlink(path: &CStr, data: &mut [u8]) -> isize { + unsafe { unsafe_impl::read_link(path.as_ptr(), data.as_mut_ptr(), data.len()) } +} + +pub fn fd_path(fd: RawFd, buf: &mut [u8]) -> isize { + let mut fd_buf: [u8; 40] = [0; 40]; + let fd_path = bfmt_cstr!(&mut fd_buf, "/proc/self/fd/{}", fd); + readlink(fd_path, buf) +} + +// Inspired by https://android.googlesource.com/platform/bionic/+/master/libc/bionic/realpath.cpp +pub fn canonical_path(path: &CStr, buf: &mut [u8]) -> isize { + if let Some(fd) = open_fd!(path, O_PATH | O_CLOEXEC) { + let mut st1: libc::stat; + let mut st2: libc::stat; + unsafe { + st1 = std::mem::zeroed(); + if libc::fstat(fd.as_raw_fd(), &mut st1) < 0 { + *errno() = ENOENT; + return -1; + } + } + let len = fd_path(fd.as_raw_fd(), buf); + unsafe { + st2 = std::mem::zeroed(); + if libc::stat(buf.as_ptr().cast(), &mut st2) < 0 + || st2.st_dev != st1.st_dev + || st2.st_ino != st1.st_ino + { + *errno() = ENOENT; + return -1; + } + } + return len; + } else { + *errno() = ENOENT; + -1 + } +} + +extern "C" { + fn strscpy(dst: *mut c_char, src: *const c_char, size: usize) -> usize; +} + +#[no_mangle] +pub extern "C" fn mkdirs(path: *const c_char, mode: mode_t) -> i32 { + unsafe { + let mut buf = [0 as u8; 4096]; + let ptr: *mut c_char = buf.as_mut_ptr().cast(); + let len = strscpy(ptr, path, buf.len()); + let mut curr = &mut buf[1..len]; + while let Some(p) = curr.iter().position(|c| *c == b'/') { + curr[p] = b'\0'; + if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST { + return -1; + } + curr[p] = b'/'; + curr = &mut curr[(p + 1)..]; + } + if libc::mkdir(ptr, mode) < 0 && *errno() != EEXIST { + return -1; + } + 0 + } +} diff --git a/native/src/base/lib.rs b/native/src/base/lib.rs index 10720625b..88fffa40f 100644 --- a/native/src/base/lib.rs +++ b/native/src/base/lib.rs @@ -2,10 +2,12 @@ pub use libc; +pub use files::*; pub use logging::*; pub use misc::*; pub use xwrap::*; +mod files; mod logging; mod misc; mod xwrap; @@ -31,9 +33,7 @@ pub mod ffi { #[cxx::bridge(namespace = "rust")] pub mod ffi2 { extern "Rust" { - fn xwrite(fd: i32, data: &[u8]) -> isize; - fn xread(fd: i32, data: &mut [u8]) -> isize; - fn xxread(fd: i32, data: &mut [u8]) -> isize; fn xpipe2(fds: &mut [i32; 2], flags: i32) -> i32; + fn fd_path(fd: i32, buf: &mut [u8]) -> isize; } } diff --git a/native/src/base/logging.cpp b/native/src/base/logging.cpp index 132e13819..0654683e1 100644 --- a/native/src/base/logging.cpp +++ b/native/src/base/logging.cpp @@ -41,7 +41,7 @@ extern "C" int magisk_log_print(int prio, const char *tag, const char *fmt, ...) } char fmt_buf[4096]; - auto len = strlcpy(fmt_buf, tag, sizeof(fmt_buf) - 1); + auto len = strscpy(fmt_buf, tag, sizeof(fmt_buf) - 1); // Prevent format specifications in the tag std::replace(fmt_buf, fmt_buf + len, '%', '_'); len = ssprintf(fmt_buf + len, sizeof(fmt_buf) - len - 1, ": %s", fmt) + len; diff --git a/native/src/base/misc.cpp b/native/src/base/misc.cpp index 1a9664a63..d02f65f8f 100644 --- a/native/src/base/misc.cpp +++ b/native/src/base/misc.cpp @@ -130,7 +130,7 @@ void init_argv0(int argc, char **argv) { void set_nice_name(const char *name) { memset(argv0, 0, name_len); - strlcpy(argv0, name, name_len); + strscpy(argv0, name, name_len); prctl(PR_SET_NAME, name); } @@ -224,3 +224,8 @@ int ssprintf(char *dest, size_t size, const char *fmt, ...) { va_end(va); return r; } + +#undef strlcpy +size_t strscpy(char *dest, const char *src, size_t size) { + return std::min(strlcpy(dest, src, size), size - 1); +} diff --git a/native/src/base/misc.hpp b/native/src/base/misc.hpp index 23a076d65..c193a2c12 100644 --- a/native/src/base/misc.hpp +++ b/native/src/base/misc.hpp @@ -6,6 +6,8 @@ #include #include +#include + #define DISALLOW_COPY_AND_MOVE(clazz) \ clazz(const clazz &) = delete; \ clazz(clazz &&) = delete; @@ -118,6 +120,11 @@ struct StringCmp { bool operator()(std::string_view a, std::string_view b) const { return a < b; } }; +template +rust::Slice byte_slice(T *buf, size_t sz) { + return rust::Slice(reinterpret_cast(buf), sz); +} + int parse_int(std::string_view s); using thread_entry = void *(*)(void *); @@ -160,10 +167,14 @@ std::vector split_ro(std::string_view, std::string_view delims int vssprintf(char *dest, size_t size, const char *fmt, va_list ap); // Similar to snprintf, but the return value is the written number of bytes int ssprintf(char *dest, size_t size, const char *fmt, ...); +// This is not actually the strscpy from the Linux kernel. +// Silently truncates, and returns the number of bytes written. +extern "C" size_t strscpy(char *dest, const char *src, size_t size); // Ban usage of unsafe cstring functions #define vsnprintf __use_vssprintf_instead__ #define snprintf __use_ssprintf_instead__ +#define strlcpy __use_strscpy_instead__ struct exec_t { bool err = false; diff --git a/native/src/base/misc.rs b/native/src/base/misc.rs index ec288421d..08f08bf6e 100644 --- a/native/src/base/misc.rs +++ b/native/src/base/misc.rs @@ -41,6 +41,21 @@ pub fn fmt_to_buf(buf: &mut [u8], args: Arguments) -> usize { } } +#[macro_export] +macro_rules! bfmt { + ($buf:expr, $($args:tt)*) => { + $crate::fmt_to_buf($buf, format_args!($($args)*)); + }; +} + +#[macro_export] +macro_rules! bfmt_cstr { + ($buf:expr, $($args:tt)*) => {{ + let len = $crate::fmt_to_buf($buf, format_args!($($args)*)); + unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&$buf[..(len + 1)]) } + }}; +} + // The cstr! macro is inspired by https://github.com/Nugine/const-str macro_rules! const_assert { @@ -88,19 +103,12 @@ macro_rules! cstr { ($s:literal) => {{ const LEN: usize = $crate::ToCStr($s).eval_len(); const BUF: [u8; LEN] = $crate::ToCStr($s).eval_bytes(); - unsafe { CStr::from_bytes_with_nul_unchecked(&BUF) } + unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&BUF) } }}; } -#[macro_export] -macro_rules! r_cstr { - ($s:literal) => { - cstr!($s).as_ptr() - }; -} - -pub unsafe fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str { - CStr::from_ptr(ptr.cast()).to_str().unwrap_or("") +pub fn ptr_to_str<'a, T>(ptr: *const T) -> &'a str { + unsafe { CStr::from_ptr(ptr.cast()) }.to_str().unwrap_or("") } pub fn errno() -> &'static mut i32 { diff --git a/native/src/base/xwrap.cpp b/native/src/base/xwrap.cpp deleted file mode 100644 index 8df52907f..000000000 --- a/native/src/base/xwrap.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -ssize_t xwrite(int fd, const void *buf, size_t count) { - return rust::xwrite(fd, rust::Slice(static_cast(buf), count)); -} - -ssize_t xread(int fd, void *buf, size_t count) { - return rust::xread(fd, rust::Slice(static_cast(buf), count)); -} - -ssize_t xxread(int fd, void *buf, size_t count) { - return rust::xxread(fd, rust::Slice(static_cast(buf), count)); -} - -dirent *xreaddir(DIR *dirp) { - errno = 0; - for (dirent *e;;) { - e = readdir(dirp); - if (e == nullptr) { - if (errno) - PLOGE("readdir"); - return nullptr; - } else if (e->d_name == "."sv || e->d_name == ".."sv) { - // Filter . and .. for users - continue; - } - return e; - } -} - -ssize_t xreadlink(const char *pathname, char *buf, size_t bufsiz) { - ssize_t ret = readlink(pathname, buf, bufsiz); - if (ret < 0) { - PLOGE("readlink %s", pathname); - } else { - buf[ret] = '\0'; - } - return ret; -} - -ssize_t xreadlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) { - // readlinkat() may fail on x86 platform, returning random value - // instead of number of bytes placed in buf (length of link) -#if defined(__i386__) || defined(__x86_64__) - memset(buf, 0, bufsiz); - ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz); - if (ret < 0) { - PLOGE("readlinkat %s", pathname); - } - return ret; -#else - ssize_t ret = readlinkat(dirfd, pathname, buf, bufsiz); - if (ret < 0) { - PLOGE("readlinkat %s", pathname); - } else { - buf[ret] = '\0'; - } - return ret; -#endif -} - -int xmkdirs(const char *pathname, mode_t mode) { - int ret = mkdirs(pathname, mode); - if (ret < 0) { - PLOGE("mkdirs %s", pathname); - } - return ret; -} - -void *xmmap(void *addr, size_t length, int prot, int flags, - int fd, off_t offset) { - void *ret = mmap(addr, length, prot, flags, fd, offset); - if (ret == MAP_FAILED) { - PLOGE("mmap"); - return nullptr; - } - return ret; -} - -ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count) { - ssize_t ret = sendfile(out_fd, in_fd, offset, count); - if (ret < 0) { - PLOGE("sendfile"); - } - return ret; -} - -int xpoll(struct pollfd *fds, nfds_t nfds, int timeout) { - int ret = poll(fds, nfds, timeout); - if (ret < 0) { - PLOGE("poll"); - } - return ret; -} - -char *xrealpath(const char *path, char *resolved_path) { - char buf[PATH_MAX]; - char *ret = realpath(path, buf); - if (ret == nullptr) { - PLOGE("xrealpath"); - } else { - strcpy(resolved_path, buf); - } - return ret; -} diff --git a/native/src/base/xwrap.hpp b/native/src/base/xwrap.hpp index ffa302518..9be6e9d50 100644 --- a/native/src/base/xwrap.hpp +++ b/native/src/base/xwrap.hpp @@ -57,8 +57,8 @@ int xmkdirat(int dirfd, const char *pathname, mode_t mode); void *xmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ssize_t xsendfile(int out_fd, int in_fd, off_t *offset, size_t count); pid_t xfork(); -int xpoll(struct pollfd *fds, nfds_t nfds, int timeout); -char *xrealpath(const char *path, char *resolved_path); +int xpoll(pollfd *fds, nfds_t nfds, int timeout); +ssize_t xcanonical_path(const char *path, char *buf, size_t bufsiz); int xmknod(const char *pathname, mode_t mode, dev_t dev); } // extern "C" diff --git a/native/src/base/xwrap.rs b/native/src/base/xwrap.rs index 72ff28dd2..d0a71d192 100644 --- a/native/src/base/xwrap.rs +++ b/native/src/base/xwrap.rs @@ -1,11 +1,100 @@ use std::ffi::CStr; use std::os::unix::io::RawFd; +use std::ptr; use libc::{ - c_char, c_uint, c_ulong, c_void, dev_t, mode_t, sockaddr, socklen_t, ssize_t, SYS_dup3, + c_char, c_uint, c_ulong, c_void, dev_t, mode_t, nfds_t, off_t, pollfd, sockaddr, socklen_t, + ssize_t, SYS_dup3, }; -use crate::{errno, error, perror, ptr_to_str}; +use crate::{canonical_path, cstr, errno, error, mkdirs, perror, ptr_to_str, readlink}; + +mod unsafe_impl { + use std::ffi::CStr; + use std::os::unix::io::RawFd; + use std::slice; + + use cfg_if::cfg_if; + use libc::{c_char, nfds_t, off_t, pollfd}; + + use crate::{perror, ptr_to_str}; + + #[no_mangle] + unsafe extern "C" fn xwrite(fd: RawFd, buf: *const u8, bufsz: usize) -> isize { + super::xwrite(fd, slice::from_raw_parts(buf, bufsz)) + } + + #[no_mangle] + unsafe extern "C" fn xread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize { + super::xread(fd, slice::from_raw_parts_mut(buf, bufsz)) + } + + #[no_mangle] + unsafe extern "C" fn xxread(fd: RawFd, buf: *mut u8, bufsz: usize) -> isize { + super::xxread(fd, slice::from_raw_parts_mut(buf, bufsz)) + } + + #[no_mangle] + unsafe extern "C" fn xcanonical_path(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { + super::xcanonical_path(CStr::from_ptr(path), slice::from_raw_parts_mut(buf, bufsz)) + } + + #[no_mangle] + unsafe extern "C" fn xreadlink(path: *const c_char, buf: *mut u8, bufsz: usize) -> isize { + super::xreadlink(CStr::from_ptr(path), slice::from_raw_parts_mut(buf, bufsz)) + } + + #[no_mangle] + pub unsafe extern "C" fn xreadlinkat( + dirfd: RawFd, + path: *const c_char, + buf: *mut u8, + bufsz: usize, + ) -> isize { + // readlinkat() may fail on x86 platform, returning random value + // instead of number of bytes placed in buf (length of link) + cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + libc::memset(buf.cast(), 0, bufsz); + let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1); + if r < 0 { + perror!("readlinkat {}", ptr_to_str(path)) + } + } else { + let r = libc::readlinkat(dirfd, path, buf.cast(), bufsz - 1); + if r < 0 { + perror!("readlinkat {}", ptr_to_str(path)) + } else { + *buf.offset(r) = b'\0'; + } + } + } + return r; + } + + #[no_mangle] + pub unsafe extern "C" fn xpoll(fds: *mut pollfd, nfds: nfds_t, timeout: i32) -> i32 { + let r = libc::poll(fds, nfds, timeout); + if r < 0 { + perror!("poll"); + } + return r; + } + + #[no_mangle] + pub unsafe extern "C" fn xsendfile( + out_fd: RawFd, + in_fd: RawFd, + offset: *mut off_t, + count: usize, + ) -> isize { + let r = libc::sendfile(out_fd, in_fd, offset, count); + if r < 0 { + perror!("sendfile"); + } + return r; + } +} #[no_mangle] pub extern "C" fn xfopen(path: *const c_char, mode: *const c_char) -> *mut libc::FILE { @@ -194,6 +283,29 @@ pub extern "C" fn xfdopendir(fd: RawFd) -> *mut libc::DIR { } } +#[no_mangle] +pub extern "C" fn xreaddir(dirp: *mut libc::DIR) -> *mut libc::dirent { + #[allow(unused_unsafe)] + unsafe { + *errno() = 0; + loop { + let e = libc::readdir(dirp); + if e.is_null() { + if *errno() != 0 { + perror!("readdir") + } + } else { + // Filter out . and .. + let s = CStr::from_ptr((*e).d_name.as_ptr()); + if s == cstr!(".") || s == cstr!("..") { + continue; + } + }; + return e; + } + } +} + #[no_mangle] pub extern "C" fn xsetsid() -> i32 { unsafe { @@ -386,21 +498,15 @@ pub extern "C" fn xdup3(oldfd: RawFd, newfd: RawFd, flags: i32) -> RawFd { } pub fn xreadlink(path: &CStr, data: &mut [u8]) -> isize { - mod e { - extern "C" { - pub fn xreadlink(path: *const u8, buf: *mut u8, bufsz: usize) -> isize; - } + let r = readlink(path, data); + if r < 0 { + perror!("readlink {}", path.to_str().unwrap_or("")) } - unsafe { e::xreadlink(path.as_ptr().cast(), data.as_mut_ptr(), data.len()) } + return r; } pub fn xreadlinkat(dirfd: RawFd, path: &CStr, data: &mut [u8]) -> isize { - mod e { - extern "C" { - pub fn xreadlinkat(dirfd: i32, path: *const u8, buf: *mut u8, bufsz: usize) -> isize; - } - } - unsafe { e::xreadlinkat(dirfd, path.as_ptr().cast(), data.as_mut_ptr(), data.len()) } + unsafe { unsafe_impl::xreadlinkat(dirfd, path.as_ptr(), data.as_mut_ptr(), data.len()) } } #[no_mangle] @@ -507,6 +613,15 @@ pub extern "C" fn xmkdir(path: *const c_char, mode: mode_t) -> i32 { } } +#[no_mangle] +pub extern "C" fn xmkdirs(path: *const c_char, mode: mode_t) -> i32 { + let r = mkdirs(path, mode); + if r < 0 { + perror!("mkdirs {}", ptr_to_str(path)); + } + return r; +} + #[no_mangle] pub extern "C" fn xmkdirat(dirfd: RawFd, path: *const c_char, mode: mode_t) -> i32 { unsafe { @@ -518,6 +633,32 @@ pub extern "C" fn xmkdirat(dirfd: RawFd, path: *const c_char, mode: mode_t) -> i } } +pub fn xsendfile(out_fd: RawFd, in_fd: RawFd, offset: Option<&mut off_t>, count: usize) -> isize { + unsafe { + let p = offset.map_or(ptr::null_mut(), |it| it); + unsafe_impl::xsendfile(out_fd, in_fd, p, count) + } +} + +#[no_mangle] +pub extern "C" fn xmmap( + addr: *mut c_void, + len: usize, + prot: i32, + flags: i32, + fd: RawFd, + offset: off_t, +) -> *mut c_void { + unsafe { + let r = libc::mmap(addr, len, prot, flags, fd, offset); + if r == libc::MAP_FAILED { + perror!("mmap"); + return ptr::null_mut(); + } + return r; + } +} + #[no_mangle] pub extern "C" fn xfork() -> i32 { unsafe { @@ -529,6 +670,18 @@ pub extern "C" fn xfork() -> i32 { } } +pub fn xpoll(fds: &mut [pollfd], timeout: i32) -> i32 { + unsafe { unsafe_impl::xpoll(fds.as_mut_ptr(), fds.len() as nfds_t, timeout) } +} + +pub fn xcanonical_path(path: &CStr, buf: &mut [u8]) -> isize { + let r = canonical_path(path, buf); + if r < 0 { + perror!("canonical_path {}", path.to_str().unwrap_or("")) + } + return r; +} + #[no_mangle] pub extern "C" fn xmknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> i32 { unsafe { diff --git a/native/src/init/getinfo.cpp b/native/src/init/getinfo.cpp index aa9c98e7a..9795fe9d2 100644 --- a/native/src/init/getinfo.cpp +++ b/native/src/init/getinfo.cpp @@ -124,7 +124,7 @@ extern "C" void klog_write(const char *msg, int len) { static int klog_with_rs(LogLevel level, const char *fmt, va_list ap) { char buf[4096]; - strlcpy(buf, "magiskinit: ", sizeof(buf)); + strscpy(buf, "magiskinit: ", sizeof(buf)); int len = vssprintf(buf + 12, sizeof(buf) - 12, fmt, ap) + 12; log_with_rs(level, rust::Str(buf, len)); return len; @@ -174,10 +174,10 @@ void BootConfig::set(const kv_pairs &kv) { LOGW("Skip invalid androidboot.slot_suffix=[normal]\n"); continue; } - strlcpy(slot, value.data(), sizeof(slot)); + strscpy(slot, value.data(), sizeof(slot)); } else if (key == "androidboot.slot") { slot[0] = '_'; - strlcpy(slot + 1, value.data(), sizeof(slot) - 1); + strscpy(slot + 1, value.data(), sizeof(slot) - 1); } else if (key == "skip_initramfs") { skip_initramfs = true; } else if (key == "androidboot.force_normal_boot") { @@ -185,13 +185,13 @@ void BootConfig::set(const kv_pairs &kv) { } else if (key == "rootwait") { rootwait = true; } else if (key == "androidboot.android_dt_dir") { - strlcpy(dt_dir, value.data(), sizeof(dt_dir)); + strscpy(dt_dir, value.data(), sizeof(dt_dir)); } else if (key == "androidboot.hardware") { - strlcpy(hardware, value.data(), sizeof(hardware)); + strscpy(hardware, value.data(), sizeof(hardware)); } else if (key == "androidboot.hardware.platform") { - strlcpy(hardware_plat, value.data(), sizeof(hardware_plat)); + strscpy(hardware_plat, value.data(), sizeof(hardware_plat)); } else if (key == "androidboot.fstab_suffix") { - strlcpy(fstab_suffix, value.data(), sizeof(fstab_suffix)); + strscpy(fstab_suffix, value.data(), sizeof(fstab_suffix)); } else if (key == "qemu") { emulator = true; } @@ -216,7 +216,7 @@ if (access(file_name, R_OK) == 0) { \ string data = full_read(file_name); \ if (!data.empty()) { \ data.pop_back(); \ - strlcpy(config->key, data.data(), sizeof(config->key)); \ + strscpy(config->key, data.data(), sizeof(config->key)); \ } \ } @@ -245,7 +245,7 @@ void load_kernel_info(BootConfig *config) { }); if (config->dt_dir[0] == '\0') - strlcpy(config->dt_dir, DEFAULT_DT_DIR, sizeof(config->dt_dir)); + strscpy(config->dt_dir, DEFAULT_DT_DIR, sizeof(config->dt_dir)); char file_name[128]; read_dt("fstab_suffix", fstab_suffix) diff --git a/native/src/init/mount.cpp b/native/src/init/mount.cpp index b82f7d530..ff5ea7f9e 100644 --- a/native/src/init/mount.cpp +++ b/native/src/init/mount.cpp @@ -118,8 +118,8 @@ static void switch_root(const string &path) { void MagiskInit::mount_rules_dir() { char path[128]; - xrealpath(BLOCKDIR, blk_info.block_dev); - xrealpath(MIRRDIR, path); + xcanonical_path(BLOCKDIR, blk_info.block_dev, sizeof(blk_info.block_dev)); + xcanonical_path(MIRRDIR, path, sizeof(path)); char *b = blk_info.block_dev + strlen(blk_info.block_dev); char *p = path + strlen(path); @@ -206,7 +206,7 @@ success: // Create symlink with relative path char s[128]; s[0] = '.'; - strlcpy(s + 1, rel + sizeof(MIRRDIR) - 1, sizeof(s) - 1); + strscpy(s + 1, rel + sizeof(MIRRDIR) - 1, sizeof(s) - 1); xsymlink(s, path); } else { xsymlink(custom_rules_dir.data(), path); diff --git a/native/src/su/connect.cpp b/native/src/su/connect.cpp index e46445400..37d358e6e 100644 --- a/native/src/su/connect.cpp +++ b/native/src/su/connect.cpp @@ -123,7 +123,7 @@ static void exec_cmd(const char *action, vector &data, ssprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_32); #endif } else { - strlcpy(exe, "/system/bin/app_process", sizeof(exe)); + strscpy(exe, "/system/bin/app_process", sizeof(exe)); } // First try content provider call method @@ -158,7 +158,7 @@ static void exec_cmd(const char *action, vector &data, }; // Then try start activity without package name - strlcpy(target, info->mgr_pkg.data(), sizeof(target)); + strscpy(target, info->mgr_pkg.data(), sizeof(target)); exec_command_sync(exec); if (check_no_error(exec.fd)) return; diff --git a/native/src/su/su_daemon.cpp b/native/src/su/su_daemon.cpp index b42745dbe..3470c65f7 100644 --- a/native/src/su/su_daemon.cpp +++ b/native/src/su/su_daemon.cpp @@ -142,7 +142,7 @@ void prune_su_access() { vector app_no_list = get_app_no_list(); vector rm_uids; char query[256], *err; - strlcpy(query, "SELECT uid FROM policies", sizeof(query)); + strscpy(query, "SELECT uid FROM policies", sizeof(query)); err = db_exec(query, [&](db_row &row) -> bool { int uid = parse_int(row["uid"]); int app_id = to_app_id(uid); @@ -412,7 +412,7 @@ void su_daemon_handler(int client, const sock_cred *cred) { char path[32]; ssprintf(path, sizeof(path), "/proc/%d/cwd", ctx.pid); char cwd[PATH_MAX]; - if (realpath(path, cwd)) + if (canonical_path(path, cwd, sizeof(cwd))) chdir(cwd); ssprintf(path, sizeof(path), "/proc/%d/environ", ctx.pid); auto env = full_read(path);