Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/ffi-common.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,27 @@ pub fn getAllocator() std.mem.Allocator {
// zlinter-disable no_global_vars
var threaded: std.Io.Threaded = .init_single_threaded;

const have_sig_io = std.posix.SIG != void and @hasField(std.posix.SIG, "IO");
const have_sig_pipe = std.posix.SIG != void and @hasField(std.posix.SIG, "PIPE");

fn initThreaded() void {
threaded = .init(std.heap.c_allocator, .{});

if (std.posix.Sigaction != void) {
if (have_sig_io) setSignalHandlerOnStack(.IO);
if (have_sig_pipe) setSignalHandlerOnStack(.PIPE);
}
}

// std.Io.Threaded installs its SIGIO/SIGPIPE handlers without SA_ONSTACK; host runtimes (Go) with
// small thread stacks require it, otherwise the kernel's signal frame overflows the interrupted
// stack.
fn setSignalHandlerOnStack(sig: std.posix.SIG) void {
var act: std.posix.Sigaction = undefined;
std.posix.sigaction(sig, null, &act);

act.flags |= std.posix.SA.ONSTACK;
std.posix.sigaction(sig, &act, null);
}

// zlinter-disable no_global_vars
Expand Down
15 changes: 13 additions & 2 deletions src/ffi-driver.zig
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,6 @@ pub const FfiDriver = struct {
operation_result: *[]u8,
operation_result_lens: *[]u64,
operation_result_failed_indicator: *[]u8,
operation_error: *[]u8,
) void {
_ = self;

Expand Down Expand Up @@ -764,7 +763,19 @@ pub const FfiDriver = struct {
);
}

operation_error.* = "";
// NOTE: deliberately nothing is written to the caller's error buffer here. On the success
// path ls_cli_fetch_operation_sizes already reported an error size of zero, so the caller
// knows there is no error and never reads the buffer.
//
// This previously did `operation_error.* = "";`, which *assigned a zig slice* through the
// caller's pointer rather than copying into the buffer it points at. Callers hand us a
// pointer to their own slice/buffer header (go's []byte header is 24 bytes -- ptr/len/cap
// -- against zig's 16 byte ptr/len), so that assignment overwrote the caller's header with
// a pointer into *our* .rodata plus a length, planting a foreign pointer inside memory the
// caller's runtime owns and manages. It also zeroed the caller's length, which silently
// discarded every error message on the way back out. Everywhere else in the ffi layer
// (including the error paths here and in ffi-root-netconf.zig) we only ever `copyForwards`
// *into* caller owned buffers -- never assign over their headers. Keep it that way.
}
};

Expand Down
1 change: 0 additions & 1 deletion src/ffi-root-cli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,6 @@ export fn ls_cli_fetch_operation(
operation_result,
operation_result_lens,
operation_result_failed_indicator,
operation_error,
);
}

Expand Down
11 changes: 11 additions & 0 deletions src/ffi-root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,17 @@ test "ffi: ls_netconf_alloc_with_result invalid options" {
try std.testing.expectEqual(@backingInt(ffi_common.FfiResult.invalid_argument), result);
}

test "ffi: initIo leaves signal handlers on the alternate signal stack" {
ffi_common.initIo();

for ([_]std.posix.SIG{ .IO, .PIPE }) |sig| {
var act: std.posix.Sigaction = undefined;
std.posix.sigaction(sig, null, &act);

try std.testing.expect(act.flags & std.posix.SA.ONSTACK != 0);
}
}

test "ffi: ls_session_write null buf" {
const result = ls_session_write(@ptrFromInt(0xDEADBEEF), null, false);

Expand Down
Loading