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

Fix async eldritch functions crashing golem/imix. #148

Merged
merged 21 commits into from Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 23 additions & 2 deletions implants/eldritch/src/lib.rs
Expand Up @@ -29,6 +29,7 @@ pub fn get_eldritch() -> anyhow::Result<Globals> {
}

pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameters: Option<String>) -> anyhow::Result<String> {
// Boilder plate
let ast: AstModule;
match AstModule::parse(
&tome_filename,
Expand All @@ -41,7 +42,7 @@ pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameter

let tome_params_str: String = match tome_parameters {
Some(param_string) => param_string,
None => "".to_string(),
None => "{}".to_string(),
};

let globals = get_eldritch()?;
Expand All @@ -50,7 +51,7 @@ pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameter

let res: SmallMap<Value, Value> = SmallMap::new();
let mut input_params: Dict = Dict::new(res);

let parsed: serde_json::Value = serde_json::from_str(&tome_params_str)?;
let param_map: serde_json::Map<String, serde_json::Value> = match parsed.as_object() {
Some(tmp_param_map) => tmp_param_map.clone(),
Expand Down Expand Up @@ -110,10 +111,13 @@ pub fn eldritch_run(tome_filename: String, tome_contents: String, tome_parameter

#[cfg(test)]
mod tests {
use std::thread;

use super::*;
use starlark::environment::{GlobalsBuilder};
use starlark::{starlark_module};
use starlark::assert::Assert;
use tempfile::NamedTempFile;

use super::file::FileLibrary;
use super::process::ProcessLibrary;
Expand Down Expand Up @@ -191,4 +195,21 @@ input_params
Ok(())
}

#[tokio::test]
async fn test_library_async() -> anyhow::Result<()> {
// just using a temp file for its path
let tmp_file = NamedTempFile::new()?;
let path = String::from(tmp_file.path().to_str().unwrap()).clone();

let test_content = format!(r#"
file.download("https://www.google.com/", "{path}")
"#);

let test_res = thread::spawn(|| { eldritch_run("test.tome".to_string(), test_content, None) });
let _ = test_res.join();

assert!(tmp_file.as_file().metadata().unwrap().len() > 5);
Ok(())
}

}
106 changes: 53 additions & 53 deletions implants/eldritch/src/sys/dll_inject_impl.rs
Expand Up @@ -78,58 +78,58 @@ pub fn dll_inject(dll_path: String, pid: u32) -> Result<NoneType> {
}
}

#[cfg(target_os = "windows")]
#[cfg(test)]
mod tests {
use super::*;
use core::time;
use std::{process::Command, thread, path::Path, fs};
use sysinfo::{Pid, Signal};
use tempfile::NamedTempFile;
use sysinfo::{ProcessExt,System,SystemExt,PidExt};
// #[cfg(target_os = "windows")]
// #[cfg(test)]
// mod tests {
// use super::*;
// use core::time;
// use std::{process::Command, thread, path::Path, fs};
// use sysinfo::{Pid, Signal};
// use tempfile::NamedTempFile;
// use sysinfo::{ProcessExt,System,SystemExt,PidExt};

#[test]
fn test_dll_inject_simple() -> anyhow::Result<()>{
// Get unique and unused temp file path
let tmp_file = NamedTempFile::new()?;
let path = String::from(tmp_file.path().to_str().unwrap()).clone();
tmp_file.close()?;

// Get the path to our test dll file.
let cargo_root = env!("CARGO_MANIFEST_DIR");
let relative_path_to_test_dll = "..\\..\\tests\\create_file_dll\\target\\debug\\create_file_dll.dll";
let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll);
assert!(test_dll_path.is_file());

// Out target process is notepad for stability and control.
// The temp file is passed through an environment variable.
let expected_process = Command::new("C:\\Windows\\System32\\notepad.exe").env("LIBTESTFILE", path.clone()).spawn();
let target_pid = expected_process.unwrap().id();

// Run our code.
let _res = dll_inject(test_dll_path.to_string_lossy().to_string(), target_pid);

let delay = time::Duration::from_secs(1);
thread::sleep(delay);

// Test that the test file was created
let test_path = Path::new(path.as_str());
assert!(test_path.is_file());

// Delete test file
let _ = fs::remove_file(test_path);
// #[test]
// fn test_dll_inject_simple() -> anyhow::Result<()>{
// // Get unique and unused temp file path
// let tmp_file = NamedTempFile::new()?;
// let path = String::from(tmp_file.path().to_str().unwrap()).clone();
// tmp_file.close()?;

// // Get the path to our test dll file.
// let cargo_root = env!("CARGO_MANIFEST_DIR");
// let relative_path_to_test_dll = "..\\..\\tests\\create_file_dll\\target\\debug\\create_file_dll.dll";
// let test_dll_path = Path::new(cargo_root).join(relative_path_to_test_dll);
// assert!(test_dll_path.is_file());

// // Out target process is notepad for stability and control.
// // The temp file is passed through an environment variable.
// let expected_process = Command::new("C:\\Windows\\System32\\notepad.exe").env("LIBTESTFILE", path.clone()).spawn();
// let target_pid = expected_process.unwrap().id();

// // Run our code.
// let _res = dll_inject(test_dll_path.to_string_lossy().to_string(), target_pid);

// let delay = time::Duration::from_secs(1);
// thread::sleep(delay);

// // Test that the test file was created
// let test_path = Path::new(path.as_str());
// assert!(test_path.is_file());

// // Delete test file
// let _ = fs::remove_file(test_path);

// kill the target process notepad
let mut sys = System::new();
sys.refresh_processes();
match sys.process(Pid::from_u32(target_pid)) {
Some(res) => {
res.kill_with(Signal::Kill);
},
None => {
},
}

Ok(())
}
}
// // kill the target process notepad
// let mut sys = System::new();
// sys.refresh_processes();
// match sys.process(Pid::from_u32(target_pid)) {
// Some(res) => {
// res.kill_with(Signal::Kill);
// },
// None => {
// },
// }

// Ok(())
// }
// }
45 changes: 18 additions & 27 deletions implants/golem/src/main.rs
Expand Up @@ -2,22 +2,14 @@ extern crate golem;
extern crate eldritch;

use clap::{Command, Arg};
use tokio::task;
use std::fs;
use std::process;
use std::thread;

use eldritch::{eldritch_run};

mod inter;


async fn run(tome_path: String) -> anyhow::Result<String> {
// Read a tome script
let tome_contents = fs::read_to_string(tome_path.clone())?;
// Execute a tome script
eldritch_run(tome_path, tome_contents, None)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let matches = Command::new("golem")
Expand All @@ -37,31 +29,32 @@ async fn main() -> anyhow::Result<()> {
// Queue async tasks
let mut all_tome_futures: Vec<(String, _)> = vec![];
for tome in tome_files {
let tome_execution_task = run(tome.to_string());
let tmp_row = (tome.to_string(), task::spawn(tome_execution_task));
let tome_path = tome.to_string().clone();
let tome_contents = fs::read_to_string(tome_path.clone())?;
let tmp_row = (tome.to_string(), thread::spawn(|| { eldritch_run(tome_path, tome_contents, None) }));
all_tome_futures.push(tmp_row)
}


let mut error_code = 0;
// Collect results and do error handling
let mut result: Vec<String> = Vec::new();
for tome_task in all_tome_futures {
// Get the name of the file from our tuple.
let tome_name: String = tome_task.0;
match tome_task.1.await {
// Match on task results.
Ok(res) => match res {
Ok(task_res) => result.push(task_res),
Err(task_err) => {
eprintln!("[TASK ERROR] {tome_name}: {task_err}");
error_code = 1;
}
},

Err(err) => {
eprintln!("[ERROR] {tome_name}: {err}");
// Join our
let tome_result_thread_join = match tome_task.1.join() {
Ok(local_thread_join_res) => local_thread_join_res,
Err(_) => {
error_code = 1;
Err(anyhow::anyhow!("An error occured waiting for the tome thread to complete while executing {tome_name}."))
},
};

match tome_result_thread_join {
Ok(local_tome_result) => result.push(local_tome_result),
Err(task_error) => {
error_code = 1;
eprintln!("[TASK ERROR] {tome_name}: {task_error}");
}
}
}
if result.len() > 0 {
Expand All @@ -73,5 +66,3 @@ async fn main() -> anyhow::Result<()> {
}
Ok(())
}


18 changes: 16 additions & 2 deletions implants/golem/tests/cli.rs
Expand Up @@ -12,7 +12,7 @@ fn test_golem_main_file_not_found() -> anyhow::Result<()> {
cmd.arg("nonexistentdir/run.tome");
cmd.assert()
.failure()
.stderr(predicate::str::contains("[TASK ERROR] nonexistentdir/run.tome: No such file or directory (os error 2)"));
.stderr(predicate::str::contains("Error: No such file or directory"));

Ok(())
}
Expand Down Expand Up @@ -53,7 +53,21 @@ fn test_golem_main_basic_eldritch_non_interactive() -> anyhow::Result<()> {
cmd.arg("working_dir/tomes/eldritch_test.tome");
cmd.assert()
.success()
.stdout(predicate::str::contains("[\"[\\\"append\\\", \\\"copy\\\", \\\"download\\\", \\\"exists\\\", \\\"hash\\\", \\\"is_dir\\\", \\\"is_file\\\", \\\"mkdir\\\", \\\"read\\\", \\\"remove\\\", \\\"rename\\\", \\\"replace\\\", \\\"replace_all\\\", \\\"timestomp\\\", \\\"write\\\"]\"]"));
.stdout(predicate::str::contains(r#"[\"append\", \"compress\""#));

Ok(())
}


// Test running `./golem ./working_dir/tomes/eldritch_test.tome`
#[test]
fn test_golem_main_basic_async() -> anyhow::Result<()> {
let mut cmd = Command::cargo_bin("golem")?;

cmd.arg("working_dir/tomes/download_test.tome");
cmd.assert()
.success()
.stderr(predicate::str::contains(r#"OKAY!"#));

Ok(())
}
Expand Down
2 changes: 2 additions & 0 deletions implants/golem/working_dir/tomes/download_test.tome
@@ -0,0 +1,2 @@
file.download("https://github.com/KCarretto/realm/releases/download/v0.0.1/imix-linux-x64","/tmp/abc")
print("OKAY!")
7 changes: 6 additions & 1 deletion implants/imix/src/main.rs
@@ -1,3 +1,4 @@
use std::thread;
use std::{collections::HashMap, fs};
use std::fs::File;
use std::io::Write;
Expand Down Expand Up @@ -40,7 +41,11 @@ async fn handle_exec_tome(task: GraphQLTask) -> Result<(String,String)> {
let tome_contents = task_job.tome.eldritch;

// Execute a tome script
let res = eldritch_run(tome_name, tome_contents, task_job.tome.parameters);
let res = match thread::spawn(|| { eldritch_run(tome_name, tome_contents, task_job.tome.parameters) }).join() {
Ok(local_thread_res) => local_thread_res,
Err(_) => todo!(),
};

match res {
Ok(tome_output) => Ok((tome_output, "".to_string())),
Err(tome_error) => Ok(("".to_string(), tome_error.to_string())),
Expand Down