Skip to content

invalid enum value not always detected where enum size is not explicit #11761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jorangreef opened this issue May 31, 2022 · 3 comments
Open
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Milestone

Comments

@jorangreef
Copy link
Contributor

jorangreef commented May 31, 2022

Zig Version

0.9.1

Steps to Reproduce

@sentientwaffle and I found something interesting working on TigerBeetle's crash recovery:

const std = @import("std");

const Thing = enum {
    one,
    two,
    three,
};

test "cat in the hat" {
    const expectEqual = std.testing.expectEqual;

    try expectEqual(Thing.one, std.mem.bytesAsValue(Thing, &[_]u8{0}).*);   // 0b00
    try expectEqual(Thing.two, std.mem.bytesAsValue(Thing, &[_]u8{1}).*);   // 0b01
    try expectEqual(Thing.three, std.mem.bytesAsValue(Thing, &[_]u8{2}).*); // 0b10

    // Crash: `invalid enum value`
    //try expectEqual(Thing.one, std.mem.bytesAsValue(Thing, &[_]u8{3}).*); // 0b11

    // These will only crash with `invalid enum value` if you change Thing to enum(u8):
    try expectEqual(Thing.one, std.mem.bytesAsValue(Thing, &[_]u8{4}).*);   // 0b00
    try expectEqual(Thing.two, std.mem.bytesAsValue(Thing, &[_]u8{5}).*);   // 0b01
    try expectEqual(Thing.three, std.mem.bytesAsValue(Thing, &[_]u8{6}).*); // 0b10

    // Crash: invalid enum value
    //try expectEqual(Thing.one, std.mem.bytesAsValue(Thing, &[_]u8{7}).*); // 0b11
}

In our case, we were accidentally accessing an enum on an untrusted data header, before checking the header's checksum. After casting some bytes into a message header, we were inadvertently checking header.command == .prepare and header.valid_checksum() (instead of the other way around).

Expected Behavior

We expected Zig to catch this at runtime with invalid enum value for all invalid values.

Actual Behavior

Unfortunately, some invalid values were bypassing the runtime check, and worse, colliding with valid values if they shared the same binary suffix.

@jorangreef jorangreef added the bug Observed behavior contradicts documented or intended behavior label May 31, 2022
@jorangreef jorangreef changed the title invalid enum value not always emitted invalid enum value not always detected May 31, 2022
@jorangreef
Copy link
Contributor Author

On second thought, this might be a little surprising but it's not necessarily a bug, since the size of the enum here is not explicitly defined—there has to be some fixed size under the hood (in this case 2 bits) and it's natural for the safety check to operate on that range only without checking anything more significant.

@jorangreef
Copy link
Contributor Author

The test is also going out of band with std.mem.bytesAsValue instead of using @intToEnum.

@jorangreef jorangreef changed the title invalid enum value not always detected invalid enum value not always detected where enum size is not explicit May 31, 2022
@ifreund
Copy link
Member

ifreund commented May 31, 2022

This has the same root cause as #11263, namely LLVM's arbitrary bit width integer semantics which I described in that thread.

I don't think this is a compiler bug so much as a design flaw in the language. This behavior is not intuitive and I think it should be improved.

@andrewrk andrewrk added this to the 0.11.0 milestone Jun 1, 2022
@andrewrk andrewrk added the frontend Tokenization, parsing, AstGen, Sema, and Liveness. label Jun 1, 2022
@Vexu Vexu modified the milestones: 0.11.0, 0.13.0 Apr 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior frontend Tokenization, parsing, AstGen, Sema, and Liveness.
Projects
None yet
Development

No branches or pull requests

4 participants