Zig: bitCast: segmentation fault at address 0x0

Created on 23 Oct 2020  路  6Comments  路  Source: ziglang/zig

This code produces a segmentation fault depending on whether the target in calculate_hash_chain_root() is passed in place or indirectly to the @bitCast, and you can toggle the segfault on/off by changing const segfault = true:

zig test segfault.zig
const std = @import("std");
const assert = std.debug.assert;

const segfault = true;

pub const JournalHeader = packed struct {
         hash_chain_root: u128 = undefined,
    prev_hash_chain_root: u128,
                checksum: u128 = undefined,
                   magic: u64,
                 command: u32,
                    size: u32,

    pub fn calculate_checksum(self: *const JournalHeader, entry: []const u8) u128 {
        assert(entry.len >= @sizeOf(JournalHeader));
        assert(entry.len == self.size);

        const checksum_offset = @byteOffsetOf(JournalHeader, "checksum");
        const checksum_size = @sizeOf(@TypeOf(self.checksum));
        assert(checksum_offset == 0 + 16 + 16);
        assert(checksum_size == 16);

        var target: [32]u8 = undefined;
        std.crypto.hash.Blake3.hash(entry[checksum_offset + checksum_size..], target[0..], .{});
        return @bitCast(u128, target[0..checksum_size].*);
    }

    pub fn calculate_hash_chain_root(self: *const JournalHeader) u128 {
        const hash_chain_root_size = @sizeOf(@TypeOf(self.hash_chain_root));
        assert(hash_chain_root_size == 16);

        const prev_hash_chain_root_offset = @byteOffsetOf(JournalHeader, "prev_hash_chain_root");
        const prev_hash_chain_root_size = @sizeOf(@TypeOf(self.prev_hash_chain_root));
        assert(prev_hash_chain_root_offset == 0 + 16);
        assert(prev_hash_chain_root_size == 16);

        const checksum_offset = @byteOffsetOf(JournalHeader, "checksum");
        const checksum_size = @sizeOf(@TypeOf(self.checksum));
        assert(checksum_offset == 0 + 16 + 16);
        assert(checksum_size == 16);

        assert(prev_hash_chain_root_offset + prev_hash_chain_root_size == checksum_offset);

        const header = @bitCast([@sizeOf(JournalHeader)]u8, self.*);
        const source = header[prev_hash_chain_root_offset..checksum_offset + checksum_size];
        assert(source.len == prev_hash_chain_root_size + checksum_size);
        var target: [32]u8 = undefined;
        std.crypto.hash.Blake3.hash(source, target[0..], .{});
        if (segfault) {
            return @bitCast(u128, target[0..hash_chain_root_size].*);
        } else {
            var array = target[0..hash_chain_root_size].*;
            return @bitCast(u128, array);
        }
    }

    pub fn set_checksum_and_hash_chain_root(self: *JournalHeader, entry: []const u8) void {
        self.checksum = self.calculate_checksum(entry);
        self.hash_chain_root = self.calculate_hash_chain_root();
    }
};

test "" {
  var buffer = [_]u8{0} ** 65536;
  var entry = std.mem.bytesAsValue(JournalHeader, buffer[0..@sizeOf(JournalHeader)]);
  entry.* = .{
      .prev_hash_chain_root = 0,
      .magic = 0,
      .command = 0,
      .size = 64 + 128
  };
  entry.set_checksum_and_hash_chain_root(buffer[0..entry.size]);
  std.debug.print("{}\n", .{ entry });
}

Something interesting with this:

  • Both calculate_checksum() and calculate_hash_chain_root() use target in the same style, but only one of them causes the segfault.
bug stage1

Most helpful comment

If the crash was happening inside a memcpy operation I'd guess #6698 solved this problem. The use of i128 with its 16 byte alignment seems to point towards that direction too.

All 6 comments

I can't reproduce the crash under any optimization mode, are you using a fresh version of the Zig compiler?

A segfault at runtime or is the compiler segfaulting? I'm also not able to repro. Also both the compiler and the binary run valgrind-clean for me.

Thanks @LemonBoy and @andrewrk, this was on 0.6.0+e20703183, from 2 October, and was a runtime segfault if the slice was inline, but not if referenced with a variable name.

On 0.6.0+91a1c20e7, from 24 October, I also can't reproduce it anymore.

Any idea what it could have been?

If the crash was happening inside a memcpy operation I'd guess #6698 solved this problem. The use of i128 with its 16 byte alignment seems to point towards that direction too.

I added your code as a regression test in e83334274f1d35690e4546938aff065397347943.

Awesome! Great to see a little TigerBeetle journalling protocol helping with regressions. 馃槃 Wish I could have reduced that test case further... it has so many asserts in there that if anything is changed it will blow up.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DavidYKay picture DavidYKay  路  3Comments

dobkeratops picture dobkeratops  路  3Comments

andrewrk picture andrewrk  路  3Comments

andrewrk picture andrewrk  路  3Comments

zimmi picture zimmi  路  3Comments