Skip to content

"reinterpret extern union" behavior test causing undefined behavior in the compiler #19389

@andrewrk

Description

@andrewrk

Zig Version

0.12.0-dev.3405+31791ae15

Steps to Reproduce and Observed Behavior

fn littleToNativeEndian(comptime T: type, v: T) T {
    return if (endian == .little) v else @byteSwap(v);
}

test "reinterpret extern union" {
    const U = extern union {
        foo: u8,
        baz: u32 align(8),
        bar: u32,
    };

    const S = struct {
        fn doTheTest() !void {
            {
                // Undefined initialization
                const u = blk: {
                    var u: U = undefined;
                    @memset(std.mem.asBytes(&u), 0);
                    u.bar = 0xbbbbbbbb;
                    u.foo = 0x2a;
                    break :blk u;
                };

                try expectEqual(@as(u8, 0x2a), u.foo);
                try expectEqual(littleToNativeEndian(u32, 0xbbbbbb2a), u.bar);
                try expectEqual(littleToNativeEndian(u32, 0xbbbbbb2a), u.baz);
            }

            {
                // Union initialization
                var u: U = .{
                    .foo = 0x2a,
                };

                {
                    const expected, const mask = switch (endian) {
                        .little => .{ 0x2a, 0xff },
                        .big => .{ 0x2a000000, 0xff000000 },
                    };

                    try expectEqual(@as(u8, 0x2a), u.foo);
                    try expectEqual(@as(u32, expected), u.bar & mask);
                    try expectEqual(@as(u32, expected), u.baz & mask);
                }

                // Writing to a larger field
                u.baz = 0xbbbbbbbb;
                try expectEqual(@as(u8, 0xbb), u.foo);
                try expectEqual(@as(u32, 0xbbbbbbbb), u.bar);
                try expectEqual(@as(u32, 0xbbbbbbbb), u.baz);

                // Writing to the same field
                u.baz = 0xcccccccc;
                try expectEqual(@as(u8, 0xcc), u.foo);
                try expectEqual(@as(u32, 0xcccccccc), u.bar);
                try expectEqual(@as(u32, 0xcccccccc), u.baz);

                // Writing to a smaller field
                u.foo = 0xdd;
                try expectEqual(@as(u8, 0xdd), u.foo);
                try expectEqual(littleToNativeEndian(u32, 0xccccccdd), u.bar);
                try expectEqual(littleToNativeEndian(u32, 0xccccccdd), u.baz);
            }
        }
    };

    try comptime S.doTheTest();
}
$ valgrind debug/bin/zig test example.zig -fno-emit-bin
==1662211== Thread 1:
==1662211== Conditional jump or move depends on uninitialised value(s)
==1662211==    at 0x643A894: math.big.int.calcLimbLen__anon_89347 (int.zig:28)
==1662211==    by 0x6128550: math.big.int.Mutable.set__anon_78775 (int.zig:232)
==1662211==    by 0x5E58728: math.big.int.Mutable.init__anon_63941 (int.zig:178)
==1662211==    by 0x6654DF0: InternPool.Key.Int.Storage.toBigInt (InternPool.zig:929)
==1662211==    by 0x691F720: InternPool.Key.hash64 (InternPool.zig:1137)
==1662211==    by 0x6431A57: InternPool.Key.hash32 (InternPool.zig:1065)
==1662211==    by 0x6123366: InternPool.KeyAdapter.hash (InternPool.zig:358)
==1662211==    by 0x612455A: checkedHash (array_hash_map.zig:1841)
==1662211==    by 0x612455A: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutInternal__anon_78747 (array_hash_map.zig:1658)
==1662211==    by 0x5E546B5: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutAssumeCapacityAdapted__anon_63790 (array_hash_map.zig:816)
==1662211==    by 0x5E5383D: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutContextAdapted__anon_63786 (array_hash_map.zig:753)
==1662211==    by 0x5E54795: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutAdapted__anon_63785 (array_hash_map.zig:738)
==1662211==    by 0x5C62D55: InternPool.get (InternPool.zig:4992)
==1662211== 
==1662211== Conditional jump or move depends on uninitialised value(s)
==1662211==    at 0x5C01157: debug.assert (debug.zig:403)
==1662211==    by 0x690229D: math.log2_int__anon_104847 (math.zig:1347)
==1662211==    by 0x65F7A20: math.log2.log2__anon_92960 (log2.zig:39)
==1662211==    by 0x643A8A4: math.big.int.calcLimbLen__anon_89347 (int.zig:33)
==1662211==    by 0x6128550: math.big.int.Mutable.set__anon_78775 (int.zig:232)
==1662211==    by 0x5E58728: math.big.int.Mutable.init__anon_63941 (int.zig:178)
==1662211==    by 0x6654DF0: InternPool.Key.Int.Storage.toBigInt (InternPool.zig:929)
==1662211==    by 0x691F720: InternPool.Key.hash64 (InternPool.zig:1137)
==1662211==    by 0x6431A57: InternPool.Key.hash32 (InternPool.zig:1065)
==1662211==    by 0x6123366: InternPool.KeyAdapter.hash (InternPool.zig:358)
==1662211==    by 0x612455A: checkedHash (array_hash_map.zig:1841)
==1662211==    by 0x612455A: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutInternal__anon_78747 (array_hash_map.zig:1658)
==1662211==    by 0x5E546B5: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutAssumeCapacityAdapted__anon_63790 (array_hash_map.zig:816)
==1662211== 
==1662211== Conditional jump or move depends on uninitialised value(s)
==1662211==    at 0x69022BA: math.log2_int__anon_104847 (math.zig:1348)
==1662211==    by 0x65F7A20: math.log2.log2__anon_92960 (log2.zig:39)
==1662211==    by 0x643A8A4: math.big.int.calcLimbLen__anon_89347 (int.zig:33)
==1662211==    by 0x6128550: math.big.int.Mutable.set__anon_78775 (int.zig:232)
==1662211==    by 0x5E58728: math.big.int.Mutable.init__anon_63941 (int.zig:178)
==1662211==    by 0x6654DF0: InternPool.Key.Int.Storage.toBigInt (InternPool.zig:929)
==1662211==    by 0x691F720: InternPool.Key.hash64 (InternPool.zig:1137)
==1662211==    by 0x6431A57: InternPool.Key.hash32 (InternPool.zig:1065)
==1662211==    by 0x6123366: InternPool.KeyAdapter.hash (InternPool.zig:358)
==1662211==    by 0x612455A: checkedHash (array_hash_map.zig:1841)
==1662211==    by 0x612455A: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutInternal__anon_78747 (array_hash_map.zig:1658)
==1662211==    by 0x5E546B5: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutAssumeCapacityAdapted__anon_63790 (array_hash_map.zig:816)
==1662211==    by 0x5E5383D: array_hash_map.ArrayHashMapUnmanaged(void,void,array_hash_map.AutoContext(void),true).getOrPutContextAdapted__anon_63786 (array_hash_map.zig:753)

Expected Behavior

No Valgrind errors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behaviorfrontendTokenization, parsing, AstGen, Sema, and Liveness.miscompilationThe compiler reports success but produces semantically incorrect code.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions