Segfault when writing to instance of Io.LockedStderr

When updating one of my projects from zig 0.15.1 to 0.16.0, I started receiving memory protection errors when printing using a file writer received from the Io.lockStderr function.

I have tried clearing my cache and reinstalling the compiler, but the problem persists.

Here’s a stripped-down example:

Code:

const std = @import("std");
const log = std.log;

pub const std_options = std.Options{
  .logFn = logFn,
};

var allocator = std.heap.DebugAllocator(.{}).init;
const gpa = allocator.allocator();

var ioImpl: std.Io.Threaded = undefined;
const io: std.Io = ioImpl.io();

pub fn main() void
{
  ioImpl = .init(gpa, .{});

  std.debug.print("Test with debug stream\n", .{});
  log.debug("Test with initialized io\n", .{});
}

pub fn logFn(
  comptime message_level: log.Level,
  comptime scope: @TypeOf(.enum_literal),
  comptime format: []const u8, args: anytype) void
{
  _ = message_level;
  _ = scope;

  var writeBuffer: [64]u8 = undefined;
  var writer: *std.Io.File.Writer = undefined;
  writer =
    (io.lockStderr(&writeBuffer, .escape_codes) catch
    {
      std.debug.print("Failed to open\n", .{});
      return;
    }).file_writer;
  var stderrWriter = writer.interface;

  defer io.unlockStderr();

  stderrWriter.print(format, args) catch
  {
    std.debug.print("Failed to print\n", .{});
  };
  stderrWriter.flush() catch
  {
    std.debug.print("Failed to flush\n", .{});
  };
}

Stderr output:

Test with debug stream
Segmentation fault at address 0x79
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io.zig:453:29: 0x104b753 in operate (std.zig)
    return io.vtable.operate(io.userdata, operation);
                            ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io/File.zig:614:27: 0x11e8219 in writeStreaming (std.zig)
    return (try io.operate(.{ .file_write_streaming = .{
                          ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io/File/Writer.zig:126:36: 0x11e7cb6 in drainStreaming (std.zig)
    const n = w.file.writeStreaming(io, header, data, splat) catch |err| {
                                   ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io/File/Writer.zig:93:63: 0x11e76cc in drain (std.zig)
        .streaming, .streaming_simple => return drainStreaming(w, data, splat),
                                                              ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io/Writer.zig:319:39: 0x10481d2 in defaultFlush (std.zig)
    while (w.end != 0) _ = try drainFn(w, &.{""}, 1);
                                      ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/Io/Writer.zig:313:26: 0x113c453 in flush (std.zig)
    return w.vtable.flush(w);
                         ^
/home/PegaFox/Programs/Zig/mission_bitcoin/test.zig:46:21: 0x11e6ad2 in logFn__anon_32826 (test.zig)
  stderrWriter.flush() catch
                    ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/log.zig:72:22: 0x11e683c in log__anon_32823 (std.zig)
    std.options.logFn(level, scope, format, args);
                     ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/log.zig:175:16: 0x11e670c in debug__anon_32814 (std.zig)
            log(.debug, scope, format, args);
               ^
/home/PegaFox/Programs/Zig/mission_bitcoin/test.zig:19:12: 0x11e6648 in main (test.zig)
  log.debug("Test with initialized io\n", .{});
           ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/start.zig:698:59: 0x11e5ed1 in callMain (std.zig)
    if (fn_info.params.len == 0) return wrapMain(root.main());
                                                          ^
/home/PegaFox/.cache/zig/p/N-V-__8AAFFSVRWqblwBIcA-Yqv-u7sbjsJoww8K0mWaHbmJ/lib/std/start.zig:190:5: 0x11e5901 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^

(I misclicked while trying to format the error message, please disregard the incorrect layout there)

this is a common mistake.
change this line

var stderrWriter = writer.interface;

to this

const stderrWriter = &writer.interface;

with the first one, you’re creating a copy of the interface seperate from the underlying file writer. you should instead take a reference of the file writer’s original writer interface for it to not break

1 Like