Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented collector for jni_events #147

Merged
merged 10 commits into from
Dec 10, 2024
14 changes: 14 additions & 0 deletions frontend/client/src/main/java/de/amosproj3/ziofa/client/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ sealed class Event {
val fd: ULong,
val durationNanoSecs: ULong,
) : Event()

data class JniReferences(
val pid: UInt,
val tid: UInt,
val beginTimeStamp: ULong,
val jniMethodName: JniMethodName?,
) : Event() {
enum class JniMethodName {
AddLocalRef,
DeleteLocalRef,
AddGlobalRef,
DeleteGlobalRef,
}
}
}

data class Process(val pid: Int, val ppid: Int, val state: String, val cmd: Command?)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ object RustClient : Client {
)
)
}
configuration.jniReferences?.pids?.forEach {
emit(
Event.JniReferences(
pid = it,
tid = 1234u,
beginTimeStamp = System.currentTimeMillis().toULong(),
jniMethodName = Event.JniReferences.JniMethodName.AddLocalRef,
)
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import uniffi.client.jniMethodNameFromI32
import uniffi.shared.Cmd
import uniffi.shared.EventData
import uniffi.shared.JniMethodName

private fun uniffi.shared.Process.into() =
Process(
Expand Down Expand Up @@ -45,6 +47,23 @@ private fun uniffi.shared.Event.into() =
fd = d.v1.fd,
durationNanoSecs = d.v1.durationNanoSec,
)
is EventData.JniReferences ->
Event.JniReferences(
pid = d.v1.pid,
tid = d.v1.tid,
beginTimeStamp = d.v1.beginTimeStamp,
jniMethodName =
when (jniMethodNameFromI32(d.v1.jniMethodName)) {
JniMethodName.ADD_LOCAL_REF -> Event.JniReferences.JniMethodName.AddLocalRef
JniMethodName.DELETE_LOCAL_REF ->
Event.JniReferences.JniMethodName.DeleteLocalRef
JniMethodName.ADD_GLOBAL_REF ->
Event.JniReferences.JniMethodName.AddGlobalRef
JniMethodName.DELETE_GLOBAL_REF ->
Event.JniReferences.JniMethodName.DeleteGlobalRef
JniMethodName.UNDEFINED -> null
},
)
null -> null
}

Expand Down
58 changes: 51 additions & 7 deletions rust/backend/daemon/src/collector.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// SPDX-FileCopyrightText: 2024 Felix Hilgers <[email protected]>
// SPDX-FileCopyrightText: 2024 Benedikt Zinn <[email protected]>
// SPDX-FileCopyrightText: 2024 Robin Seidl <[email protected]>
//
// SPDX-License-Identifier: MIT

Expand All @@ -12,9 +14,10 @@ use tokio::io::unix::AsyncFd;
use tokio::{join, select};
use tonic::Status;
use tracing::error;
use backend_common::{SysSendmsgCall, VfsWriteCall};
use shared::ziofa::{Event, SysSendmsgEvent, VfsWriteEvent};
use backend_common::{JNICall, JNIMethodName, SysSendmsgCall, VfsWriteCall};
use shared::ziofa::{Event, JniReferencesEvent, SysSendmsgEvent, VfsWriteEvent};
use shared::ziofa::event::{EventData};
use shared::ziofa::jni_references_event;

pub trait CollectFromMap {
const MAP_NAME: &'static str;
Expand All @@ -23,6 +26,8 @@ pub trait CollectFromMap {
}

struct VfsWriteCollect;
struct JNICollect;
struct SysSendmsgCollect;

impl CollectFromMap for VfsWriteCollect {
const MAP_NAME: &'static str = "VFS_WRITE_EVENTS";
Expand All @@ -41,7 +46,31 @@ impl CollectFromMap for VfsWriteCollect {
}
}

struct SysSendmsgCollect;
impl CollectFromMap for JNICollect {
const MAP_NAME: &'static str = "JNI_REF_CALLS";

fn convert(item: RingBufItem<'_>) -> Result<Event, Status> {
let data = unsafe { &*(item.as_ptr() as *const JNICall) };

// manual cast from the ebpf (c rep.) typ to protobuf (rust rep.) type
let jni_method_name = match data.method_name {
JNIMethodName::AddLocalRef => jni_references_event::JniMethodName::AddLocalRef,
JNIMethodName::DeleteLocalRef => jni_references_event::JniMethodName::DeleteLocalRef,
JNIMethodName::AddGlobalRef => jni_references_event::JniMethodName::AddGlobalRef,
JNIMethodName::DeleteGlobalRef => jni_references_event::JniMethodName::DeleteGlobalRef,
};

Ok(Event {
event_data: Some(EventData::JniReferences(JniReferencesEvent {
pid: data.pid,
tid: data.tid,
begin_time_stamp: data.begin_time_stamp,
jni_method_name: i32::from(jni_method_name),
}))
})
}
}


impl CollectFromMap for SysSendmsgCollect {
const MAP_NAME: &'static str = "SYS_SENDMSG_EVENTS";
Expand All @@ -63,19 +92,22 @@ impl CollectFromMap for SysSendmsgCollect {
pub struct MultiCollector {
vfs_write: Option<Collector<VfsWriteCollect>>,
sys_sendmsg: Option<Collector<SysSendmsgCollect>>,
jni_event: Option<Collector<JNICollect>>,
}

impl MultiCollector {
pub fn from_ebpf(ebpf: &mut Ebpf) -> Result<Self, MapError> {
let vfs_write = Collector::<VfsWriteCollect>::from_ebpf(ebpf)?;
let sys_sendmsg = Collector::<SysSendmsgCollect>::from_ebpf(ebpf)?;
Ok(Self { vfs_write: Some(vfs_write), sys_sendmsg: Some(sys_sendmsg) })
let jni_collect = Collector::<JNICollect>::from_ebpf(ebpf)?;
Ok(Self { vfs_write: Some(vfs_write), sys_sendmsg: Some(sys_sendmsg), jni_event: Some(jni_collect) })
}

pub async fn collect(&mut self, tx: Sender<Result<Event, Status>>, shutdown: tokio::sync::oneshot::Receiver<()>) -> Result<(), std::io::Error> {

let (vfs_write_shutdown_tx, vfs_write_shutdown_rx) = tokio::sync::oneshot::channel();
let (sys_sendmsg_shutdown_tx, sys_sendmsg_shutdown_rx) = tokio::sync::oneshot::channel();
let (jni_event_shutdown_tx, jni_event_shutdown_rx) = tokio::sync::oneshot::channel();

let cancellation_task = async move {
if shutdown.await.is_err() {
Expand All @@ -87,6 +119,9 @@ impl MultiCollector {
if sys_sendmsg_shutdown_tx.send(()).is_err() {
error!("Error while cancelling sys_sendmsg collector");
}
if jni_event_shutdown_tx.send(()).is_err() {
error!("Error while cancelling sys_sendmsg collector");
}
};

let vfs_write_tx = tx.clone();
Expand All @@ -96,22 +131,31 @@ impl MultiCollector {
Ok::<(), std::io::Error>(())
};

let sys_sendmsg_tx = tx;
let sys_sendmsg_tx = tx.clone();
let mut sys_sendmsg = self.sys_sendmsg.take().expect("sys_sendmsg should be initialized");
let sys_sendmsg_task = async {
sys_sendmsg.collect(sys_sendmsg_tx, sys_sendmsg_shutdown_rx).await?;
Ok::<(), std::io::Error>(())
};

let jni_event_tx = tx;
let mut jni_event = self.jni_event.take().expect("jni_event should be initialized");
let jni_event_task = async {
jni_event.collect(jni_event_tx, jni_event_shutdown_rx).await?;
Ok::<(), std::io::Error>(())
};

let (_, vfs_write_result, sys_sendmsg_result) = join!(cancellation_task, vfs_write_task, sys_sendmsg_task);
let (_, vfs_write_result, sys_sendmsg_result, jni_event_result) = join!(cancellation_task, vfs_write_task, sys_sendmsg_task, jni_event_task);

self.vfs_write = Some(vfs_write);
self.sys_sendmsg = Some(sys_sendmsg);
self.jni_event = Some(jni_event);

// TODO: multiple errors
vfs_write_result?;
sys_sendmsg_result?;

jni_event_result?;

Ok(())
}
}
Expand Down
5 changes: 0 additions & 5 deletions rust/backend/daemon/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,3 @@ pub fn save_to_file(config: &Configuration, path: &str) -> io::Result<()> {
serde_json::to_writer(writer, config)?;
Ok(())
}

pub fn validate(_config: &Configuration) -> Result<(), io::Error> {
//TODO: Implement this function
Ok(())
}
4 changes: 1 addition & 3 deletions rust/backend/daemon/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ use std::net::SocketAddr;
pub(crate) const DEV_DEFAULT_FILE_PATH: &str = "./ziofa.json";

pub fn sock_addr() -> SocketAddr {
// "learn rust" they said, "it's a great language" they said
"[::1]:50051".parse().expect("is valid address")
}

// TODO: custom error type for file
pub const OATDUMP_PATH: &str = "/data/local/tmp/dump.json";
pub const OATDUMP_PATH: &str = "/data/local/tmp/dump.json";
18 changes: 6 additions & 12 deletions rust/backend/daemon/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ use crate::{
use async_broadcast::{broadcast, Receiver, Sender};
use aya::Ebpf;
use aya_log::EbpfLogger;
use shared::ziofa::{
Event, GetSymbolsRequest,
PidMessage, StringResponse, Symbol,
};
use shared::ziofa::{Event, GetSymbolsRequest, PidMessage, StringResponse, Symbol};
use shared::{
config::Configuration,
counter::counter_server::CounterServer,
Expand Down Expand Up @@ -97,9 +94,6 @@ impl Ziofa for ZiofaImpl {
) -> Result<Response<()>, Status> {
let config = request.into_inner();

// TODO: Implement function 'validate'
// TODO: if ? fails needs valid return value for the function so that the server doesn't fail
configuration::validate(&config)?;
configuration::save_to_file(&config, constants::DEV_DEFAULT_FILE_PATH)?;

let mut ebpf_guard = self.ebpf.lock().await;
Expand Down Expand Up @@ -171,13 +165,13 @@ impl Ziofa for ZiofaImpl {
let odex_file_path = PathBuf::from(odex_file_path_string);

let (tx, rx) = mpsc::channel(4);

let symbol_handler = self.symbol_handler.clone();

tokio::spawn(async move {
let mut symbol_handler_guard = symbol_handler.lock().await;

let symbol = match symbol_handler_guard.get_symbols(&odex_file_path).await{
let symbol = match symbol_handler_guard.get_symbols(&odex_file_path).await {
Ok(symbol) => symbol,
Err(e) => {
tx.send(Err(Status::from(e)))
Expand All @@ -187,12 +181,12 @@ impl Ziofa for ZiofaImpl {
}
};
for (symbol, offset) in symbol.iter() {
tx.send(Ok(Symbol{
tx.send(Ok(Symbol {
method: symbol.to_string(),
offset: *offset,
}))
.await
.expect("Error sending odex file to client");
.await
.expect("Error sending odex file to client");
}
});

Expand Down
6 changes: 6 additions & 0 deletions rust/client/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use shared::{config::Configuration, ziofa::Process};
use tokio::sync::Mutex;
use tokio_stream::{Stream, StreamExt};
use shared::ziofa::{Event, StringResponse, Symbol};
use shared::ziofa::jni_references_event::JniMethodName;

type Result<T> = core::result::Result<T, ClientError>;

Expand Down Expand Up @@ -171,3 +172,8 @@ impl Client {
Ok(SymbolStream(Mutex::new(Box::pin(stream))))
}
}

#[uniffi::export]
pub fn jni_method_name_from_i32(num: i32) -> JniMethodName {
JniMethodName::try_from(num).unwrap()
}
3 changes: 2 additions & 1 deletion rust/shared/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ static UNIFFI_RECORDS: LazyLock<Vec<&str>> = LazyLock::new(|| {
"Event",
"VfsWriteEvent",
"SysSendmsgEvent",
"JniReferencesEvent",
"VfsWriteConfig",
"SysSendmsgConfig",
"JniReferencesConfig",
Expand All @@ -35,7 +36,7 @@ static UNIFFI_RECORDS: LazyLock<Vec<&str>> = LazyLock::new(|| {

static UNIFFI_ENUMS: LazyLock<Vec<&str>> = LazyLock::new(|| {
if cfg!(feature = "uniffi") {
vec!["Process.cmd", "Event.event_data"]
vec!["Process.cmd", "Event.event_data", "JniReferencesEvent.JniMethodName"]
} else {
vec![]
}
Expand Down
16 changes: 16 additions & 0 deletions rust/shared/proto/ziofa.proto
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ message Event {
oneof event_data {
VfsWriteEvent vfs_write = 1;
SysSendmsgEvent sys_sendmsg = 2;
JniReferencesEvent jni_references = 3;
}
}

Expand All @@ -86,3 +87,18 @@ message SysSendmsgEvent {
uint64 fd = 4;
uint64 duration_nano_sec = 5;
}

message JniReferencesEvent {
enum JniMethodName {
Undefined = 0;
AddLocalRef = 1;
DeleteLocalRef = 2;
AddGlobalRef = 3;
DeleteGlobalRef = 4;
}
uint32 pid = 1;
uint32 tid = 2;
uint64 begin_time_stamp = 3;
JniMethodName jni_method_name = 4;
}