Files
bun.sh/src/handle_oom.zig
taylor.fish 437e15bae5 Replace catch bun.outOfMemory() with safer alternatives (#22141)
Replace `catch bun.outOfMemory()`, which can accidentally catch
non-OOM-related errors, with either `bun.handleOom` or a manual `catch
|err| switch (err)`.

(For internal tracking: fixes STAB-1070)

---------

Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
2025-08-26 12:50:25 -07:00

67 lines
2.4 KiB
Zig

fn isOomOnlyError(comptime ErrorUnionOrSet: type) bool {
@setEvalBranchQuota(10000);
const ErrorSet = switch (@typeInfo(ErrorUnionOrSet)) {
.error_union => |union_info| union_info.error_set,
.error_set => ErrorUnionOrSet,
else => @compileError("argument must be an error union or error set"),
};
for (@typeInfo(ErrorSet).error_set orelse &.{}) |err| {
if (!std.mem.eql(u8, err.name, "OutOfMemory")) return false;
}
return true;
}
/// If `error_union_or_set` is `error.OutOfMemory`, calls `bun.outOfMemory`. Otherwise:
///
/// * If that was the only possible error, returns the non-error payload for error unions, or
/// `noreturn` for error sets.
/// * If other errors are possible, returns the same error union or set, but without
/// `error.OutOfMemory` in the error set.
///
/// Prefer this method over `catch bun.outOfMemory()`, since that could mistakenly catch
/// non-OOM-related errors.
///
/// There are two ways to use this function:
///
/// ```
/// // option 1:
/// const thing = bun.handleOom(allocateThing());
/// // option 2:
/// const thing = allocateThing() catch |err| bun.handleOom(err);
/// ```
pub fn handleOom(error_union_or_set: anytype) return_type: {
const ArgType = @TypeOf(error_union_or_set);
const arg_info = @typeInfo(ArgType);
break :return_type if (isOomOnlyError(ArgType)) switch (arg_info) {
.error_union => |union_info| union_info.payload,
.error_set => noreturn,
else => unreachable,
} else @TypeOf(blk: {
const err = switch (comptime arg_info) {
.error_union => if (error_union_or_set) |success| break :blk success else |err| err,
.error_set => error_union_or_set,
else => unreachable,
};
break :blk switch (err) {
error.OutOfMemory => unreachable,
else => |other_error| other_error,
};
});
} {
const ArgType = @TypeOf(error_union_or_set);
const err = switch (comptime @typeInfo(ArgType)) {
.error_union => if (error_union_or_set) |success| return success else |err| err,
.error_set => error_union_or_set,
else => unreachable,
};
return if (comptime isOomOnlyError(ArgType))
bun.outOfMemory()
else switch (err) {
error.OutOfMemory => bun.outOfMemory(),
else => |other_error| other_error,
};
}
const bun = @import("bun");
const std = @import("std");