feat: store magic signature as a meaningful constant

This commit is contained in:
Erica Marigold 2024-01-05 17:08:43 +05:30
parent 94b27d81d1
commit 3c2464d3ce
No known key found for this signature in database
GPG key ID: 2768CC0C23D245D1
3 changed files with 33 additions and 30 deletions

View file

@ -8,6 +8,11 @@ use tokio::{
use anyhow::Result; use anyhow::Result;
use mlua::Compiler as LuaCompiler; use mlua::Compiler as LuaCompiler;
// The signature which separates indicates the presence of bytecode to execute
// If a binary contains this magic signature as the last 8 bytes, that must mean
// it is a standalone binary
pub const MAGIC: &[u8; 8] = b"cr3sc3nt";
/** /**
Compiles and embeds the bytecode of a requested lua file to form a standalone binary, Compiles and embeds the bytecode of a requested lua file to form a standalone binary,
then writes it to an output file, with the required permissions. then writes it to an output file, with the required permissions.
@ -32,10 +37,6 @@ pub async fn build_standalone<T: AsRef<Path>>(
let mut patched_bin = fs::read(env::current_exe()?).await?; let mut patched_bin = fs::read(env::current_exe()?).await?;
let base_bin_offset = u64::try_from(patched_bin.len())?; let base_bin_offset = u64::try_from(patched_bin.len())?;
// The signature which separates indicates the presence of bytecode to execute
// If a binary contains this signature, that must mean it is a standalone binary
let signature: Vec<u8> = vec![0x4f, 0x3e, 0xf8, 0x41, 0xc3, 0x3a, 0x52, 0x16];
// Compile luau input into bytecode // Compile luau input into bytecode
let bytecode = LuaCompiler::new() let bytecode = LuaCompiler::new()
.set_optimization_level(2) .set_optimization_level(2)
@ -45,18 +46,18 @@ pub async fn build_standalone<T: AsRef<Path>>(
println!(" {bytecode_prefix} {script_path}"); println!(" {bytecode_prefix} {script_path}");
patched_bin.append(&mut bytecode.clone()); patched_bin.extend(&bytecode);
let mut meta = base_bin_offset.to_ne_bytes().to_vec(); let mut meta = base_bin_offset.to_ne_bytes().to_vec(); // Start with the base bytecode offset
// Include metadata in the META chunk, each field is 8 bytes // Include metadata in the META chunk, each field is 8 bytes
meta.append(&mut (bytecode.len() as u64).to_ne_bytes().to_vec()); // Size of bytecode, used to calculate end offset at runtime meta.extend((bytecode.len() as u64).to_ne_bytes()); // Size of bytecode, used to calculate end offset at runtime
meta.append(&mut 1_u64.to_ne_bytes().to_vec()); // Number of files, padded with null bytes - for future use meta.extend(1_u64.to_ne_bytes()); // Number of files, padded with null bytes - for future use
patched_bin.append(&mut meta); patched_bin.extend(meta);
// Append the signature to the base binary // Append the magic signature to the base binary
patched_bin.append(&mut signature.clone()); patched_bin.extend(MAGIC);
// Write the compiled binary to file // Write the compiled binary to file
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]

View file

@ -1,5 +1,6 @@
use std::{env, ops::ControlFlow, process::ExitCode}; use std::{env, ops::ControlFlow, process::ExitCode};
use crate::cli::build::MAGIC;
use lune::Lune; use lune::Lune;
use anyhow::Result; use anyhow::Result;
@ -9,10 +10,7 @@ use tokio::fs::read as read_to_vec;
Returns information about whether the execution environment is standalone Returns information about whether the execution environment is standalone
or not, the standalone binary signature, and the contents of the binary. or not, the standalone binary signature, and the contents of the binary.
*/ */
pub async fn check_env() -> (bool, Vec<u8>, Vec<u8>) { pub async fn check_env() -> (bool, Vec<u8>) {
// Signature which is only present in standalone lune binaries
let signature: Vec<u8> = vec![0x4f, 0x3e, 0xf8, 0x41, 0xc3, 0x3a, 0x52, 0x16];
// Read the current lune binary to memory // Read the current lune binary to memory
let bin = if let Ok(contents) = read_to_vec( let bin = if let Ok(contents) = read_to_vec(
env::current_exe().expect("failed to get path to current running lune executable"), env::current_exe().expect("failed to get path to current running lune executable"),
@ -24,20 +22,21 @@ pub async fn check_env() -> (bool, Vec<u8>, Vec<u8>) {
Vec::new() Vec::new()
}; };
let is_standalone = !bin.is_empty() && bin[bin.len() - signature.len()..bin.len()] == signature; let is_standalone =
!bin.is_empty() && bin[bin.len() - MAGIC.len()..bin.len()] == MAGIC.to_vec();
(is_standalone, signature, bin) (is_standalone, bin)
} }
/** /**
Discovers, loads and executes the bytecode contained in a standalone binary. Discovers, loads and executes the bytecode contained in a standalone binary.
*/ */
pub async fn run_standalone(signature: Vec<u8>, bin: Vec<u8>) -> Result<ExitCode> { pub async fn run_standalone(bin: Vec<u8>) -> Result<ExitCode> {
let mut bytecode_offset = 0; let mut bytecode_offset = 0;
let mut bytecode_size = 0; let mut bytecode_size = 0;
// standalone binary structure (reversed, 8 bytes per field) // standalone binary structure (reversed, 8 bytes per field)
// [0] => signature // [0] => magic signature
// ---------------- // ----------------
// -- META Chunk -- // -- META Chunk --
// [1] => file count // [1] => file count
@ -47,21 +46,21 @@ pub async fn run_standalone(signature: Vec<u8>, bin: Vec<u8>) -> Result<ExitCode
// -- MISC Chunk -- // -- MISC Chunk --
// [4..n] => bytecode (variable size) // [4..n] => bytecode (variable size)
// ---------------- // ----------------
// NOTE: All integers are 8 byte unsigned 64 bit (u64's). // NOTE: All integers are 8 byte, padded, unsigned & 64 bit (u64's).
// The rchunks will have unequally sized sections in the beginning // The rchunks will have unequally sized sections in the beginning
// but that doesn't matter to us because we don't need anything past the // but that doesn't matter to us because we don't need anything past the
// middle chunks where the bytecode is stored // middle chunks where the bytecode is stored
bin.rchunks(signature.len()) bin.rchunks(MAGIC.len())
.enumerate() .enumerate()
.try_for_each(|(idx, chunk)| { .try_for_each(|(idx, chunk)| {
if bytecode_offset != 0 && bytecode_size != 0 { if bytecode_offset != 0 && bytecode_size != 0 {
return ControlFlow::Break(()); return ControlFlow::Break(());
} }
if idx == 0 && chunk != signature { if idx == 0 && chunk != MAGIC {
// Binary is guaranteed to be standalone, we've confirmed this before // Binary is guaranteed to be standalone, we've confirmed this before
unreachable!("expected proper signature for standalone binary") unreachable!("expected proper magic signature for standalone binary")
} }
if idx == 3 { if idx == 3 {
@ -78,16 +77,19 @@ pub async fn run_standalone(signature: Vec<u8>, bin: Vec<u8>) -> Result<ExitCode
// If we were able to retrieve the required metadata, we load // If we were able to retrieve the required metadata, we load
// and execute the bytecode // and execute the bytecode
// println!("offset: {}", bytecode_offset);
// println!("size: {}", bytecode_size);
// Skip the first argument which is the path to current executable // Skip the first argument which is the path to current executable
let args = env::args().skip(1).collect::<Vec<_>>(); let args = env::args().skip(1).collect::<Vec<_>>();
let bytecode =
&bin[usize::try_from(bytecode_offset)?..usize::try_from(bytecode_offset + bytecode_size)?];
// println!("bytecode: {:?}", bytecode);
let result = Lune::new() let result = Lune::new()
.with_args(args) .with_args(args)
.run( .run("STANDALONE", bytecode)
"STANDALONE",
&bin[usize::try_from(bytecode_offset)?
..usize::try_from(bytecode_offset + bytecode_size)?],
)
.await; .await;
Ok(match result { Ok(match result {

View file

@ -28,12 +28,12 @@ async fn main() -> ExitCode {
.with_level(true) .with_level(true)
.init(); .init();
let (is_standalone, signature, bin) = executor::check_env().await; let (is_standalone, bin) = executor::check_env().await;
if is_standalone { if is_standalone {
// It's fine to unwrap here since we don't want to continue // It's fine to unwrap here since we don't want to continue
// if something fails // if something fails
return executor::run_standalone(signature, bin).await.unwrap(); return executor::run_standalone(bin).await.unwrap();
} }
match Cli::parse().run().await { match Cli::parse().run().await {