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
39 changes: 29 additions & 10 deletions crates/cardwire-daemon/src/analyzer/dynamic_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,42 @@ pub fn check_steam_environ(environ: &[u8]) -> bool {
}

/// Read the proc `maps` file to find the gamemodeauto.so
pub fn check_gamemode(environ: &[u8]) -> bool {
environ
.windows(18)
pub fn check_gamemode(map: &[u8]) -> bool {
map.windows(18)
.any(|window| window == b"libgamemodeauto.so")
}

/// Read the environ map to file the FLATPAK_ID and compare with .desktop apps
pub fn check_flatpak_environ(environ: &[u8], xdg_list: &HashMap<String, bool>) -> bool {
// Check if the byte array contains the substring
for var in environ.split(|&b| b == 0) {
if var.starts_with(b"FLATPAK_ID=")
&& let Ok(str) = std::str::from_utf8(&var[11..])
&& xdg_list.contains_key(str)
/// Check if the comm is in the xdg list
pub fn check_fdo_app_id(comm: &str, xdg_list: &HashMap<String, bool>) -> bool {
xdg_list.contains_key(comm)
}

pub fn check_for_flatpak_run(cmdline: &str, xdg_list: &HashMap<String, bool>) -> bool {
let mut args = cmdline.split('\0').filter(|s| !s.is_empty());

if let Some(arg0) = args.next() {
// Ensure the actual executable is flatpak or bwrap, not a wrapper like 'niri msg'
if !arg0.ends_with("flatpak")
&& !arg0.ends_with(".flatpak-wrapped")
&& !arg0.ends_with("bwrap")
{
return false;
}
} else {
return false;
}

// Now check if any of the arguments match our allowed app
for arg in args {
if let Some(exec) = arg.strip_prefix("--command=") {
if xdg_list.contains_key(exec) {
return true;
}
} else if xdg_list.contains_key(arg) {
return true;
}
}

false
}

Expand Down
32 changes: 23 additions & 9 deletions crates/cardwire-daemon/src/analyzer/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use tokio::{

use crate::analyzer::{
dynamic_analysis::{
check_cardwire_allow, check_flatpak_environ, check_gamemode, check_gpu_env, check_steam_environ
check_cardwire_allow, check_fdo_app_id, check_for_flatpak_run, check_gamemode, check_gpu_env, check_steam_environ
}, static_analysis
};
#[repr(C)]
Expand Down Expand Up @@ -150,7 +150,8 @@ impl CardwireAnalyzer {
Some(name) => name,
None => return,
};
if let Some(result) = self_clone.evaluate_app(event.pid).await
if let Some(result) =
self_clone.evaluate_app(event.pid, &real_app_name).await
&& result
{
info!(
Expand Down Expand Up @@ -190,26 +191,39 @@ impl CardwireAnalyzer {
}

/// Default app are blocked, try to find if it's a game or a gpu intensive app
async fn evaluate_app(&self, pid: u32) -> Option<bool> {
async fn evaluate_app(&self, pid: u32, comm: &str) -> Option<bool> {
let path = format!("/proc/{}/environ", pid);
let time = Instant::now();
let environ = match fs::read(path) {
Ok(content) => content,
Err(_) => return None,
};
let time = time.elapsed().as_micros();
// First check CARDWIRE_ALLOW, if None continue
if let Some(allow) = check_cardwire_allow(&environ) {
return Some(allow);
}
let xdg_list = self.xdg_list.read().await;

let result = check_flatpak_environ(&environ, &xdg_list)
|| check_gamemode(&environ)
let mut result = check_fdo_app_id(comm, &xdg_list)
|| check_steam_environ(&environ)
|| check_gpu_env(&environ);
if result {
info!("time to read environ for pid {}: {}", pid, time);
// if no result with environ file, read cmdline
// The goal is to reduce unnecessary reads
if !result {
let path_cmd = format!("/proc/{}/cmdline", pid);
let cmdline = match fs::read_to_string(path_cmd) {
Ok(content) => content,
Err(_) => return None,
};
result = check_for_flatpak_run(&cmdline, &xdg_list);
}
// reading map is slow, should be done if every test are false
if !result {
let path_map = format!("/proc/{}/map", pid);
let map = match fs::read(path_map) {
Ok(content) => content,
Err(_) => return None,
};
result = check_gamemode(&map);
}
Some(result)
}
Expand Down
18 changes: 11 additions & 7 deletions crates/cardwire-daemon/src/analyzer/static_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,18 @@ pub async fn get_fdo_apps() -> anyhow::Result<HashMap<String, bool>> {
// ignore if app doesnt end with .desktop
if let Some(ext) = path.extension()
&& ext == "desktop"
&& let Ok(app_fdo) = DesktopEntry::from_path(path, Some(&locales))
&& app_fdo.prefers_non_default_gpu()
&& let Some(name) = app_fdo.name(&locales)
{
// for now, only keep flatpak apps that prefers a non default gpu
// the reason we only keep flatpak apps is because i can match a process with
// it's FLATPAK_ID env
if let Ok(app_fdo) = DesktopEntry::from_path(path, Some(&locales))
&& app_fdo.prefers_non_default_gpu()
&& let Some(flatpak_id) = app_fdo.flatpak()
{
// Push both lowercase and normal name to the hashmap
// the RPCS3 .desktop contain the name `RPCS3` but the comm is `rpcs3`, so
// we need to lowercase it On the other, Ryujinx
// .desktop's name is `Ryujinx` and the comm is `Ryujinx`, so we also push
// the default name
app_list.insert(name.to_ascii_lowercase(), true);
app_list.insert(name.to_string(), true);
if let Some(flatpak_id) = app_fdo.flatpak() {
app_list.insert(flatpak_id.to_string(), true);
}
}
Expand Down
15 changes: 13 additions & 2 deletions crates/cardwire-ebpf/src/bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,25 @@ int trace_process_exit(void *ctx)
if (!is_smart())
return 0;

// Now create and send a close event containing the pid to the userspace
// we get the pid_tgdi
__u64 pid_tgid = bpf_get_current_pid_tgid();
// extract the tgid
__u32 tgid = pid_tgid >> 32;
// and the pid
__u32 pid = pid_tgid & 0xFFFFFFFF;

// Only send close event if the main thread is exiting
if (pid != tgid)
return 0;

struct close_t *rb_data = {};
rb_data = bpf_ringbuf_reserve(&cw_close_events, sizeof(struct close_t),
0);
// if struct error, exit
if (!rb_data) {
return 0;
}
rb_data->pid = bpf_get_current_pid_tgid() >> 32;
rb_data->pid = tgid;

bpf_ringbuf_submit(rb_data, 0);
return 0;
Expand Down
Loading