Future cannot be sent between threads safely using frida

Hello everyone , i am quite new with rust.

I started created an app with tauri which made me use rust as backend.
I used a lot of different programming langague but i wasn't used to lifetime and others specific things from rust. I am actualy facing the issue future cannot be sent between threads safely after some research i think i understood why.

I am using the frida library which use a raw pointer session_ptr: *mut _FridaSession, and raw pointer can't be sent safely. I tried to find some workaround by using spanw_local() but i faced another weird issue and it wouldn't realy solve my problem as my async function would not realy be executed in different thread.

at this point i am a bit lost and don't know what do to ? Did I missunderstood something about rust / how to use it ?

Here is the complete code:

mod bot;
mod hook;

use bot::Bot;

use tauri::async_runtime::Mutex as TauriMutex;
use tauri::State;
use tokio::task::spawn_local;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(
            tauri_plugin_log::Builder::new()
                .target(tauri_plugin_log::Target::new(
                    tauri_plugin_log::TargetKind::LogDir {
                        file_name: Some("logs".to_string()),
                    },
                ))
                .build(),
        )
        .manage(TauriMutex::new(Bot::new().expect("Failed to create Bot")))
        .invoke_handler(tauri::generate_handler![is_programm_running])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/

#[tauri::command]
async fn is_programm_running<'a>(state: State<'a, TauriMutex<Bot<'a>>>) -> Result<bool, String> {
    {
        let mut bot = state.lock().await;

        bot.is_programm_running().await;
    }

    Ok(true)
}

The struct inside of bot creating the issue:

use frida::{Device, DeviceManager, Frida, ScriptOption, Session};
use std::error::Error;

pub struct ProgramHook<'a> {
    programm_exe_name: String,
    script: String,
    device: Device<'a>,
    session: Option<Session<'a>>,
}

impl<'a> ProgramHook<'a> {
    pub fn new() -> Result<Self, Box<dyn Error>> {
        let script = "zearar".to_string();

        let frida: &'a Frida = Box::leak(Box::new(unsafe { Frida::obtain() }));
        let device_manager = DeviceManager::obtain(frida);
        let device_manager_boxed = Box::leak(Box::new(device_manager));
        let device = device_manager_boxed.get_local_device().unwrap();
        Ok(Self {
            programm_exe_name: "programm.exe".to_string(),
            script,
            device,
            session: None,
        })
    }
   
    pub async fn get_programm_pid(&self) -> Result<u32, Box<dyn Error>> {
        let processes = self.device.enumerate_processes();
        let program_process = processes
            .iter()
            .find(|process| process.get_name() == self.program_exe_name)
            .ok_or("process not found")?;
        Ok(program_process.get_pid())
    }

    pub async fn is_programm_running(&self) -> Result<bool, Box<dyn Error>> {
        let processes = self.device.enumerate_processes();
        Ok(processes
            .iter()
            .any(|process| process.get_name() == self.program_exe_name))
    }
}


Tauri uses tokio, and tokio's spawn requires async code to be thread-safe, so that it can be moved to another thread (that's what Send means).

Is the Frida library compatible with this? If it is, and the code using _FridaSession type wouldn't mind being called from different threads, then you can tell Rust that the foreign code is thread-safe:

struct SessionWrapper {
    session_ptr: *mut _FridaSession
}
unsafe impl Send for SessionWrapper {}

This needs to be done by the library that uses these session pointers.

If the Frida library is not guaranteed to be thread safe, then this error is preventing a bug.

If you have a library that has to be single-threaded, but you need your code to be multi-threaded, then usually you would spawn a dedicated thread for using the library, and use channels to send commands to the thread, and receive replies.

std::thread::spawn(|| {
   init_library();
   while let Ok(cmd) = channel.recv() {
      // interpret the cmd and call the library
   }
});
3 Likes