Skip to content

Commit 9045ce5

Browse files
committed
Fix std.bytes decode functions crashing on a negative offset
decodeAt's multi-byte variants (u16be_at through f64le_at — everything but byte_at) cast the offset argument straight to usize with @intcast, without checking its sign first. A negative offset made that cast a Zig safety-check failure, aborting the whole process, instead of raising the catchable RangeError the docs promise for "insufficient bytes at i" (byte_at already handled this correctly). Fixed with a shared offsetToUsize helper that raises RangeError for a negative offset before ever reaching the read functions' own upper-bound checks.
1 parent 0b18ff9 commit 9045ce5

2 files changed

Lines changed: 34 additions & 10 deletions

File tree

src/compiler_test.zig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3075,6 +3075,21 @@ test "compiler: std.bytes decode family matches the native-call path byte for by
30753075
);
30763076
}
30773077

3078+
// A negative offset used to reach @intCast on a negative i64 (a Zig
3079+
// safety-check panic that aborts the process) instead of raising a
3080+
// catchable RangeError like every other out-of-bounds offset — decodeAt's
3081+
// multi-byte variants (everything but byte_at) cast straight to usize
3082+
// without checking the sign first. Fixed with offsetToUsize (bytes.zig).
3083+
test "compiler: std.bytes decode family raises RangeError (not a crash) on a negative offset" {
3084+
var rt = try setup();
3085+
defer rt.deinit();
3086+
try runSrc(&rt,
3087+
\\std := import("std")
3088+
\\func f() int { return std.bytes.u32be_at(std.bytes.u32be(1234), -1) }
3089+
);
3090+
try std.testing.expectError(error.RangeError, rt.callGlobal("f", &.{}));
3091+
}
3092+
30783093
test "compiler: field = field + const fuses into field_add_const" {
30793094
// The "c.tx_id = c.tx_id + 1" idiom (found independently in gengo-modbus
30803095
// and gengo-mqtt as a transaction/packet-ID counter), issue #207.

src/lang/native/bytes.zig

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,23 +117,32 @@ pub const DecodeKind = enum(u8) {
117117
f64le = 10,
118118
};
119119

120+
// A negative offset must raise RangeError like every other out-of-bounds
121+
// case, not panic: @intCast from a negative i64 straight to usize is a Zig
122+
// safety-check failure (process abort), not a catchable Gengo error.
123+
fn offsetToUsize(offset_arg: Value) !usize {
124+
const idx = try argAsI64(offset_arg);
125+
if (idx < 0) return error.RangeError;
126+
return @intCast(idx);
127+
}
128+
120129
pub fn decodeAt(kind: DecodeKind, s: []const u8, offset_arg: Value) !Value {
121130
switch (kind) {
122131
.byte_at => {
123132
const idx = try argAsI64(offset_arg);
124133
if (idx < 0 or idx >= @as(i64, @intCast(s.len))) return error.RangeError;
125134
return .{ .int = @as(i64, s[@as(usize, @intCast(idx))]) };
126135
},
127-
.u16be => return .{ .int = try readU16be(s, @intCast(try argAsI64(offset_arg))) },
128-
.u16le => return .{ .int = try readU16le(s, @intCast(try argAsI64(offset_arg))) },
129-
.u32be => return .{ .int = try readU32be(s, @intCast(try argAsI64(offset_arg))) },
130-
.u32le => return .{ .int = try readU32le(s, @intCast(try argAsI64(offset_arg))) },
131-
.u64be => return .{ .int = try readU64be(s, @intCast(try argAsI64(offset_arg))) },
132-
.u64le => return .{ .int = try readU64le(s, @intCast(try argAsI64(offset_arg))) },
133-
.f32be => return .{ .float = try readF32be(s, @intCast(try argAsI64(offset_arg))) },
134-
.f32le => return .{ .float = try readF32le(s, @intCast(try argAsI64(offset_arg))) },
135-
.f64be => return .{ .float = try readF64be(s, @intCast(try argAsI64(offset_arg))) },
136-
.f64le => return .{ .float = try readF64le(s, @intCast(try argAsI64(offset_arg))) },
136+
.u16be => return .{ .int = try readU16be(s, try offsetToUsize(offset_arg)) },
137+
.u16le => return .{ .int = try readU16le(s, try offsetToUsize(offset_arg)) },
138+
.u32be => return .{ .int = try readU32be(s, try offsetToUsize(offset_arg)) },
139+
.u32le => return .{ .int = try readU32le(s, try offsetToUsize(offset_arg)) },
140+
.u64be => return .{ .int = try readU64be(s, try offsetToUsize(offset_arg)) },
141+
.u64le => return .{ .int = try readU64le(s, try offsetToUsize(offset_arg)) },
142+
.f32be => return .{ .float = try readF32be(s, try offsetToUsize(offset_arg)) },
143+
.f32le => return .{ .float = try readF32le(s, try offsetToUsize(offset_arg)) },
144+
.f64be => return .{ .float = try readF64be(s, try offsetToUsize(offset_arg)) },
145+
.f64le => return .{ .float = try readF64le(s, try offsetToUsize(offset_arg)) },
137146
}
138147
}
139148

0 commit comments

Comments
 (0)