From 0975eb51ebeddba3ced534a577ce069bd5ef1c1a Mon Sep 17 00:00:00 2001 From: LoveSy Date: Thu, 17 Apr 2025 00:34:58 +0800 Subject: [PATCH] Use splice to pump tty to avoid userspace copying --- native/src/core/lib.rs | 7 ++-- native/src/core/su/pts.rs | 70 +++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/native/src/core/lib.rs b/native/src/core/lib.rs index 7bbb394af..d32cda649 100644 --- a/native/src/core/lib.rs +++ b/native/src/core/lib.rs @@ -4,13 +4,14 @@ #![feature(fn_traits)] #![feature(unix_socket_ancillary_data)] #![feature(unix_socket_peek)] +#![feature(maybe_uninit_uninit_array)] #![allow(clippy::missing_safety_doc)] use crate::ffi::SuRequest; use crate::socket::Encodable; -use base::{Utf8CStr, libc}; -use cxx::{ExternType, type_id}; -use daemon::{MagiskD, daemon_entry}; +use base::{libc, Utf8CStr}; +use cxx::{type_id, ExternType}; +use daemon::{daemon_entry, MagiskD}; use derive::Decodable; use logging::{android_logging, setup_logfile, zygisk_close_logd, zygisk_get_logd, zygisk_logging}; use mount::{find_preinit_device, revert_unmount}; diff --git a/native/src/core/su/pts.rs b/native/src/core/su/pts.rs index b7a48cf13..7de3fd361 100644 --- a/native/src/core/su/pts.rs +++ b/native/src/core/su/pts.rs @@ -1,25 +1,21 @@ use base::{ - ResultExt, error, + error, libc::{ - POLLIN, SFD_CLOEXEC, SIG_BLOCK, SIGWINCH, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, - cfmakeraw, close, poll, pollfd, raise, sigaddset, sigemptyset, signalfd, sigprocmask, - sigset_t, tcsetattr, winsize, + cfmakeraw, close, pipe, poll, pollfd, raise, read, sigaddset, + sigemptyset, signalfd, signalfd_siginfo, sigprocmask, sigset_t, splice, tcsetattr, winsize, POLLIN, SFD_CLOEXEC, + SIGWINCH, SIG_BLOCK, TCSADRAIN, TCSAFLUSH, TIOCGWINSZ, TIOCSWINSZ, }, - libc::{STDIN_FILENO, STDOUT_FILENO, tcgetattr, termios}, + libc::{tcgetattr, termios, STDIN_FILENO, STDOUT_FILENO}, warn, }; -use std::fs::File; -use std::io::{Read, Write}; -use std::mem::ManuallyDrop; -use std::os::fd::{FromRawFd, RawFd}; -use std::ptr::null_mut; +use std::{ffi::c_int, mem::MaybeUninit, ptr::null_mut}; static mut OLD_STDIN: Option = None; const TIOCGPTN: u32 = 0x80045430; unsafe extern "C" { // Don't use the declaration from the libc crate as request should be u32 not i32 - fn ioctl(fd: RawFd, request: u32, ...) -> i32; + fn ioctl(fd: c_int, request: u32, ...) -> i32; } pub fn get_pty_num(fd: i32) -> i32 { @@ -79,6 +75,24 @@ fn resize_pty(outfd: i32) { } } +fn pump_via_pipe(infd: i32, outfd: i32, pipe: &[c_int; 2]) -> bool { + // usize::MAX will EINVAL in some kernels, use i32::MAX in case + let s = unsafe { splice(infd, null_mut(), pipe[1], null_mut(), i32::MAX as _, 0) }; + if s < 0 { + error!("splice error"); + return false; + } + if s == 0 { + return true; + } + let s = unsafe { splice(pipe[0], null_mut(), outfd, null_mut(), s as usize, 0) }; + if s < 0 { + error!("splice error"); + return false; + } + true +} + pub fn pump_tty(infd: i32, outfd: i32) { set_stdin_raw(); @@ -112,7 +126,11 @@ pub fn pump_tty(infd: i32, outfd: i32) { }, ]; - let mut buf = [0u8; 4096]; + let mut p: [c_int; 2] = [0; 2]; + if unsafe { pipe(&mut p as *mut c_int) } < 0 { + error!("pipe error"); + return; + } 'poll: loop { let ready = unsafe { poll(pfds.as_mut_ptr(), pfds.len() as _, -1) }; @@ -123,21 +141,25 @@ pub fn pump_tty(infd: i32, outfd: i32) { for pfd in &pfds { if pfd.revents & POLLIN != 0 { - let mut in_file = ManuallyDrop::new(unsafe { File::from_raw_fd(pfd.fd) }); - - let Ok(n) = in_file.read(&mut buf) else { - error!("read error"); - break 'poll; - }; - - if pfd.fd == STDIN_FILENO { - let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(outfd) }); - out.write_all(&buf[..n]).log_ok(); + let res = if pfd.fd == STDIN_FILENO { + pump_via_pipe(pfd.fd, outfd, &p) } else if pfd.fd == infd { - let mut out = ManuallyDrop::new(unsafe { File::from_raw_fd(STDOUT_FILENO) }); - out.write_all(&buf[..n]).log_ok(); + pump_via_pipe(pfd.fd, STDOUT_FILENO, &p) } else if pfd.fd == sfd { resize_pty(outfd); + let mut buf: [MaybeUninit; size_of::()] = + MaybeUninit::uninit_array(); + if unsafe { read(pfd.fd, buf.as_mut_ptr() as *mut _, buf.len()) } < 0 { + error!("read error"); + false + } else { + true + } + } else { + false + }; + if !res { + break 'poll; } } else if pfd.revents != 0 && pfd.fd == infd { unsafe { close(pfd.fd) };