Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion builtin/security/vpnhide/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# builtin/scripts/integrate.py at apply time (one source of truth: kmod/generated,
# kmod/shared). -I$(src) lets core.c/hook_*.c include <linux/vpnhide.h> from the
# tree's own include/linux and the vendored generated/shared headers locally.
ccflags-y += -I$(src)
# Shared diagnostic atomics must remain in kernel text, without compiler-rt.
ccflags-y += -I$(src) -mno-outline-atomics

obj-$(CONFIG_VPNHIDE) += vpnhide.o
vpnhide-y := core.o hook_iface.o hook_socket.o hook_route.o
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
_2026-11-04_

## English

Prevent kernel diagnostic RAW controls from triggering CFI panics on older GKI kernels.

## Русский

Исправлены CFI-паники при обращении к диагностическому RAW-контролю на старых ядрах GKI.
11 changes: 9 additions & 2 deletions crates/core-daemon/src/maintenance/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,18 @@ fn executable(root: &Path, relative: &str, uid: u32) -> io::Result<(String, File
// AOSP /data is system:system 0771, not root-only. Trust exactly
// that platform parent; /data/adb and every executable descendant
// still require the retained root-owned, non-writable boundary.
let platform_uid = if uid == 0 { 1000 } else { uid };
// Non-root owners exist only for unprivileged tests; their files carry
// the process's primary group, which need not equal the uid (e.g. CI
// runners are uid 1001, gid 118).
let (platform_uid, platform_gid) = if uid == 0 {
(1000, 1000)
} else {
(uid, unsafe { libc::getegid() })
};
let platform_data = parent == Path::new("data")
&& meta.is_dir()
&& meta.uid() == platform_uid
&& meta.gid() == platform_uid
&& meta.gid() == platform_gid
&& meta.mode() & 0o002 == 0;
if !platform_data && (meta.uid() != uid || meta.mode() & 0o022 != 0) {
return Err(invalid());
Expand Down
5 changes: 5 additions & 0 deletions crates/core-daemon/src/maintenance/manager/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ fn properties_do_not_guess_escaped_or_duplicate_installer_semantics() {
fn platform_data_parent_is_distinct_from_privileged_adb_boundary() {
let root = tempfile::tempdir().unwrap();
let manager = install(root.path());
// Model AOSP /data: system:system 0771. As root (CI containers) that is
// uid/gid 1000; an unprivileged owner keeps its own uid and primary gid.
if unsafe { libc::geteuid() } == 0 {
std::os::unix::fs::chown(root.path().join("data"), Some(1000), Some(1000)).unwrap();
}
fs::set_permissions(root.path().join("data"), fs::Permissions::from_mode(0o771)).unwrap();
manager.require_unchanged().unwrap();
fs::set_permissions(
Expand Down
8 changes: 7 additions & 1 deletion crates/tooling/src/qemu/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,13 +259,19 @@ fn boot(plan: &Plan, image: &Path, root: &Path, work: &Path, tag: &str) -> io::R
32 * 1024 * 1024,
&work.join(format!("serial-{tag}.log")),
)?;
let serial = String::from_utf8_lossy(&r.output.stdout);
// CI runners discard /tmp after the job. Keep the complete bounded serial
// evidence in the job log before the verifier rejects a panic or timeout.
if !success(&r) || !model::panic_lines(&serial).is_empty() {
eprintln!("QEMU serial evidence ({tag}):\n{serial}");
}
if !success(&r) {
return Err(error(format!(
"QEMU failed/timed out/truncated in {tag}; retained evidence {}",
work.display()
)));
}
for line in String::from_utf8_lossy(&r.output.stdout).lines() {
for line in serial.lines() {
if [
"RESULT ",
"SUMMARY ",
Expand Down
4 changes: 4 additions & 0 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ restore protection. The separate root-only proc endpoint and ioctl handshake do
not change control2/telemetry1 or require broader app SELinux proc access.
Absent kernel helpers leave this optional capability unavailable without changing
backend health or triggering fallback.
The Linux adapters compile shared atomics inline, without compiler-rt helpers.
The `.ko` calls its dynamically resolved PID/clock helpers through narrow,
non-inlined `__nocfi` boundaries: kallsyms addresses are not canonical jump-table
entries on older CFI kernels. Other module callsites keep their CFI checks.

The Root lease/controller and unprivileged collector adapter have Linux fixture
oracle coverage for real same-thread RAW/protected results, same-UID thread
Expand Down
3 changes: 3 additions & 0 deletions kmod/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
obj-m += vpnhide_kmod.o

# Kernel code cannot call compiler-rt userspace outline-atomic helpers.
ccflags-y += -mno-outline-atomics

# Kbuild includes the external module Makefile in its modpost stage. Replace
# only the checker executable here, preserving this kernel's own recursive
# MODPOST flags (including $@, symbol inputs and later += additions). Passing
Expand Down
29 changes: 22 additions & 7 deletions kmod/shared/vpnhide_diag_proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,49 @@ static struct vpnhide_diag_scope vpnhide_diag_state;
struct vpnhide_diag_file {
unsigned long long nonce;
};
static void *vpnhide_diag_pid_current(void *unused)
/* The .ko supplies raw kallsyms addresses, not canonical CFI jump-table
* entries. Keep only these resolved calls behind non-inlined __nocfi boundaries,
* like the module's other resolved VFS/socket helpers. The callbacks themselves
* retain their typed callsites; barriers also prevent tail-call folding.
*/
static noinline __nocfi void *vpnhide_diag_pid_current(void *unused)
{
struct pid *identity;

(void)unused;
return vpnhide_diag_getpid(current, PIDTYPE_PID);
identity = vpnhide_diag_getpid(current, PIDTYPE_PID);
barrier();
return identity;
}
static unsigned int vpnhide_diag_pid_number(void *unused, void *identity)
static noinline __nocfi unsigned int vpnhide_diag_pid_number(void *unused,
void *identity)
{
pid_t number;

(void)unused;
number = vpnhide_diag_pidnr(identity);
barrier();
return number > 0 ? (unsigned int)number : 0;
}
static unsigned int vpnhide_diag_uid(void *unused)
{
(void)unused;
return __kuid_val(current_uid());
}
static void vpnhide_diag_pid_put(void *unused, void *identity)
static noinline __nocfi void vpnhide_diag_pid_put(void *unused, void *identity)
{
(void)unused;
vpnhide_diag_putpid(identity);
barrier();
}
static unsigned long long vpnhide_diag_now(void *unused)
static noinline __nocfi unsigned long long vpnhide_diag_now(void *unused)
{
ktime_t now;

(void)unused;
return (unsigned long long)ktime_to_ns(
vpnhide_diag_clock(TK_OFFS_BOOT));
now = vpnhide_diag_clock(TK_OFFS_BOOT);
barrier();
return (unsigned long long)ktime_to_ns(now);
}
static const struct vpnhide_diag_ops vpnhide_diag_callbacks = {
.current_identity = vpnhide_diag_pid_current,
Expand Down
7 changes: 6 additions & 1 deletion scripts/build-native-fixture-oracle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ exec "$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/clang" --targe
SH
chmod 0700 "$linker"
rustup target add aarch64-unknown-linux-musl
CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$linker" \
# C build scripts (ring) otherwise guess aarch64-linux-musl-gcc, absent on
# hosts and CI. Compile with the same NDK clang target and LLVM archiver.
llvm_bin="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
CC_aarch64_unknown_linux_musl="$llvm_bin/clang --target=aarch64-linux-musl" \
AR_aarch64_unknown_linux_musl="$llvm_bin/llvm-ar" \
CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER="$linker" \
cargo build --target aarch64-unknown-linux-musl -p vpnhide_core_daemon --features fixture-oracle --bin vpnhide-fixture-oracle
mkdir -p "$(dirname "$output")"
install -m 0755 target/aarch64-unknown-linux-musl/debug/vpnhide-fixture-oracle "$output"
Expand Down
Loading