ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
32.52k stars 2.37k forks source link

Comptime interpretation of address silently yields zero #20459

Open ikskuh opened 2 weeks ago

ikskuh commented 2 weeks ago

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

run with zig test:

comptime {
    const desc = Descriptor.init(getInterruptStub(0), 0x08, .interruptGate, .bits32, 0, true);
    @compileLog(desc);
}

const Descriptor = packed struct(u64) {
    offset0: u16, // 0-15 Offset 0-15 Gibt das Offset des ISR innerhalb des Segments an. Wenn der entsprechende Interrupt auftritt, wird eip auf diesen Wert gesetzt.
    selector: u16, // 16-31 Selector Gibt den Selector des Codesegments an, in das beim Auftreten des Interrupts gewechselt werden soll. Im Allgemeinen ist dies das Kernel-Codesegment (Ring 0).
    ist: u3 = 0, // 32-34 000 / IST Gibt im LM den Index in die IST an, ansonsten 0!
    _0: u5 = 0, // 35-39 Reserviert Wird ignoriert
    type: InterruptType, // 40-42 Typ Gibt die Art des Interrupts an
    bits: InterruptBits, // 43 D Gibt an, ob es sich um ein 32bit- (1) oder um ein 16bit-Segment (0) handelt.
    // Im LM: Für 64-Bit LDT 0, ansonsten 1
    _1: u1 = 0, // 44 0
    privilege: u2, // 45-46 DPL Gibt das Descriptor Privilege Level an, das man braucht um diesen Interrupt aufrufen zu dürfen.
    enabled: bool, // 47 P Gibt an, ob dieser Eintrag benutzt wird.
    offset1: u16, // 48-63 Offset 16-31

    pub fn init(offset: ?*const fn () callconv(.Naked) void, selector: u16, _type: InterruptType, bits: InterruptBits, privilege: u2, enabled: bool) Descriptor {
        const offset_val = @intFromPtr(offset);
        return Descriptor{
            .offset0 = @as(u16, @truncate(offset_val & 0xFFFF)),
            .offset1 = @as(u16, @truncate((offset_val >> 16) & 0xFFFF)),
            .selector = selector,
            .type = _type,
            .bits = bits,
            .privilege = privilege,
            .enabled = enabled,
        };
    }
};

fn getInterruptStub(comptime i: u32) *const fn () callconv(.Naked) void {
    const Wrapper = struct {
        fn stub_with_zero() callconv(.Naked) void {
            asm volatile (
            // this handler has no error code pushed by the cpu, so we have to
                \\ pushl $0
                \\ pushl %[nr]
                \\ jmp common_isr_handler
                :
                : [nr] "n" (i),
            );
        }
        fn stub_with_errorcode() callconv(.Naked) void {
            asm volatile (
            // error code was already pushed by cpu already
                \\ pushl %[nr]
                \\ jmp common_isr_handler
                :
                : [nr] "n" (i),
            );
        }
    };
    return switch (i) {
        8, 10...14, 17 => &Wrapper.stub_with_errorcode,
        else => &Wrapper.stub_with_zero,
    };
}

const InterruptType = enum(u3) {
    interruptGate = 0b110,
    trapGate = 0b111,
    taskGate = 0b101,
};

const InterruptBits = enum(u1) {
    bits32 = 1,
    bits16 = 0,
};

outputs the following code:

/home/felix/projects/articles/bugreports/bad-comptime-code/badcode.zig:3:5: error: found compile log statement
    @compileLog(desc);
    ^~~~~~~~~~~~~~~~~

Compile Log Output:
@as(badcode.Descriptor, .{ .offset0 = 0, .selector = 8, .ist = 0, ._0 = 0, .type = .interruptGate, .bits = .bits32, ._1 = 0, .privilege = 0, .enabled = true, .offset1 = 0 })
exit code: 1

Expected Behavior

This code is interpreting the address returned by getInterruptStub() as 0, while it's not available yet. this should definitly create a compile error and not silently assume 0.

Rexicon226 commented 2 weeks ago
Stack Trace ``` thread 962528 panic: attempt to use null value Analyzing main.zig %61 = ret_type() node_offset:24:5 to :24:11 %62 = dbg_stmt(2, 28) > %63 = int_from_ptr(%45) node_offset:25:28 to :25:47 %64 = dbg_var_val(%63, "offset_val") %65 = dbg_stmt(3, 9)4 %66 = block_comptime({ %67 = decl_val("Descriptor") token_offset:26:16 to :26:26 %68 = break(%66, %67) }) node_offset:26:16 to :26:26 %69 = validate_struct_init_ty(%66) node_offset:26:16 to :26:16 %70 = struct_init_field_type(%66, offset0) node_offset:27:24 to :27:64 %71 = int(65535) %72 = bit_and(%63, %71) node_offset:27:43 to :27:62 %73 = dbg_stmt(4, 33) %74 = truncate(@u16_type, %72) node_offset:27:33 to :27:63 %75 = as_node(@u16_type, %74) node_offset:27:33 to :27:63 %76 = struct_init_field_type(%66, offset1) node_offset:28:24 to :28:72 %77 = typeof_log2_int_type(%63) node_offset:28:44 to :28:54 %78 = int(16) %79 = as_shift_operand(%77, %78) node_offset:28:58 to :28:60 %80 = dbg_stmt(5, 55) %81 = shr(%63, %79) node_offset:28:44 to :28:60 %82 = int(65535) %83 = bit_and(%81, %82) node_offset:28:43 to :28:70 %84 = dbg_stmt(5, 33) %85 = truncate(@u16_type, %83) node_offset:28:33 to :28:71 %86 = as_node(@u16_type, %85) node_offset:28:33 to :28:71 %87 = struct_init_field_type(%66, selector) node_offset:29:25 to :29:33 %88 = struct_init_field_type(%66, type) node_offset:30:21 to :30:26 %89 = struct_init_field_type(%66, bits) node_offset:31:21 to :31:25 %90 = struct_init_field_type(%66, privilege) node_offset:32:26 to :32:35 %91 = struct_init_field_type(%66, enabled) node_offset:33:24 to :33:31 %92 = struct_init([%70, %75], [%76, %86], [%87, %47], [%88, %50], [%89, %53], [%90, %56], [%91, %58]) node_offset:26:16 to :26:16 %93 = restore_err_ret_index_unconditional(.none) node_offset:26:9 to :26:15 %94 = dbg_stmt(3, 9) %95 = ret_node(%92) node_offset:26:9 to :26:15 For full context, use the command zig ast-check -t main.zig in main.zig > %4 = field_call(.compile_time, %2, "init", [ {%5..%9}, {%10, %11}, {%12, %13}, {%14, %15}, {%16}, {%17}, ]) node_offset:7:18 to :7:94 Unwind error at address `exe:0x8d5e9fa` (error.AddressOutOfRange), trace may be incomplete └─ main.comptime_0 /home/david/Code/zig/src/Sema.zig:10218:68: 0x9429bc8 in zirIntFromPtr () (try operand_val.getUnsignedIntAdvanced(zcu, sema)).?, ^ /home/david/Code/zig/src/Sema.zig:1122:68: 0x8e15421 in analyzeBodyInner () .int_from_ptr => try sema.zirIntFromPtr(block, inst), ^ /home/david/Code/zig/src/Sema.zig:903:26: 0x88421be in analyzeFnBody () sema.analyzeBodyInner(block, body) catch |err| switch (err) { ^ /home/david/Code/zig/src/Sema.zig:7866:35: 0x96ff072 in analyzeCall () sema.analyzeFnBody(&child_block, fn_info.body) catch |err| switch (err) { ^ /home/david/Code/zig/src/Sema.zig:7066:43: 0x93c16b5 in zirCall__anon_88464 () const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);eneration ^ /home/david/Code/zig/src/Sema.zig:1033:62: 0x8e10e70 in analyzeBodyInner () .field_call => try sema.zirCall(block, inst, .field), ```