Skip to content
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

Fix assorted crashes found via fuzzing #706

Merged
merged 8 commits into from
May 6, 2024
6 changes: 6 additions & 0 deletions src/aro/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,12 @@ pub fn nextLargestIntSameSign(comp: *const Compilation, ty: Type) ?Type {
return null;
}

/// Maximum size of an array, in bytes
pub fn maxArrayBytes(comp: *const Compilation) u64 {
const max_bits = @min(61, comp.target.ptrBitWidth());
return (@as(u64, 1) << @truncate(max_bits)) - 1;
}

/// If `enum E { ... }` syntax has a fixed underlying integer type regardless of the presence of
/// __attribute__((packed)) or the range of values of the corresponding enumerator constants,
/// specify it here.
Expand Down
5 changes: 5 additions & 0 deletions src/aro/Diagnostics/messages.def
Original file line number Diff line number Diff line change
Expand Up @@ -2487,3 +2487,8 @@ invalid_type_underlying_enum
.msg = "non-integral type '{s}' is an invalid underlying type"
.extra = .str
.kind = .@"error"

auto_type_self_initialized
.msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer"
.extra = .str
.kind = .@"error"
72 changes: 54 additions & 18 deletions src/aro/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ const_decl_folding: ConstDeclFoldingMode = .fold_const_decls,
/// address-of-label expression (tracked with contains_address_of_label)
computed_goto_tok: ?TokenIndex = null,

/// __auto_type may only be used with a single declarator. Keep track of the name
/// so that it is not used in its own initializer.
auto_type_decl_name: StringId = .empty,

/// Various variables that are different for each function.
func: struct {
/// null if not in function, will always be plain func, var_args_func or old_style_func
Expand Down Expand Up @@ -1421,6 +1425,8 @@ fn typeof(p: *Parser) Error!?Type {
const l_paren = try p.expectToken(.l_paren);
if (try p.typeName()) |ty| {
try p.expectClosing(l_paren, .r_paren);
if (ty.is(.invalid)) return null;

const typeof_ty = try p.arena.create(Type);
typeof_ty.* = .{
.data = ty.data,
Expand All @@ -1442,6 +1448,8 @@ fn typeof(p: *Parser) Error!?Type {
.specifier = .nullptr_t,
.qual = if (unqual) .{} else typeof_expr.ty.qual.inheritFromTypeof(),
};
} else if (typeof_expr.ty.is(.invalid)) {
return null;
}

const inner = try p.arena.create(Type.Expr);
Expand Down Expand Up @@ -1788,6 +1796,8 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?
} else {
apply_var_attributes = true;
}
const c23_auto = init_d.d.ty.is(.c23_auto);
const auto_type = init_d.d.ty.is(.auto_type);

if (p.eatToken(.equal)) |eq| init: {
if (decl_spec.storage_class == .typedef or
Expand Down Expand Up @@ -1815,6 +1825,11 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?

const interned_name = try StrInt.intern(p.comp, p.tokSlice(init_d.d.name));
try p.syms.declareSymbol(p, interned_name, init_d.d.ty, init_d.d.name, .none);
if (c23_auto or auto_type) {
p.auto_type_decl_name = interned_name;
}
defer p.auto_type_decl_name = .empty;

var init_list_expr = try p.initializer(init_d.d.ty);
init_d.initializer = init_list_expr;
if (!init_list_expr.ty.isArray()) break :init;
Expand All @@ -1824,8 +1839,7 @@ fn initDeclarator(p: *Parser, decl_spec: *DeclSpec, attr_buf_top: usize) Error!?
}

const name = init_d.d.name;
const c23_auto = init_d.d.ty.is(.c23_auto);
if (init_d.d.ty.is(.auto_type) or c23_auto) {
if (auto_type or c23_auto) {
if (init_d.initializer.node == .none) {
init_d.d.ty = Type.invalid;
if (c23_auto) {
Expand Down Expand Up @@ -3011,9 +3025,6 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
}

const outer = try p.directDeclarator(base_type, d, kind);
var max_bits = p.comp.target.ptrBitWidth();
if (max_bits > 61) max_bits = 61;
const max_bytes = (@as(u64, 1) << @truncate(max_bits)) - 1;

if (!size.ty.isInt()) {
try p.errStr(.array_size_non_int, size_tok, try p.typeStr(size.ty));
Expand Down Expand Up @@ -3050,7 +3061,7 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
} else {
// `outer` is validated later so it may be invalid here
const outer_size = outer.sizeof(p.comp);
const max_elems = max_bytes / @max(1, outer_size orelse 1);
const max_elems = p.comp.maxArrayBytes() / @max(1, outer_size orelse 1);

var size_val = size.val;
if (size_val.isZero(p.comp)) {
Expand Down Expand Up @@ -3140,12 +3151,14 @@ fn directDeclarator(p: *Parser, base_type: Type, d: *Declarator, kind: Declarato
fn pointer(p: *Parser, base_ty: Type) Error!Type {
var ty = base_ty;
while (p.eatToken(.asterisk)) |_| {
const elem_ty = try p.arena.create(Type);
elem_ty.* = ty;
ty = Type{
.specifier = .pointer,
.data = .{ .sub_type = elem_ty },
};
if (!ty.is(.invalid)) {
const elem_ty = try p.arena.create(Type);
elem_ty.* = ty;
ty = Type{
.specifier = .pointer,
.data = .{ .sub_type = elem_ty },
};
}
var quals = Type.Qualifiers.Builder{};
_ = try p.typeQual(&quals);
try quals.finish(p, &ty);
Expand Down Expand Up @@ -3848,6 +3861,12 @@ fn convertInitList(p: *Parser, il: InitList, init_ty: Type) Error!NodeIndex {
.data = .{ .bin = .{ .lhs = .none, .rhs = .none } },
};

const max_elems = p.comp.maxArrayBytes() / (elem_ty.sizeof(p.comp) orelse 1);
if (start > max_elems) {
try p.errTok(.array_too_large, il.tok);
start = max_elems;
}

if (init_ty.specifier == .incomplete_array) {
arr_init_node.ty.specifier = .array;
arr_init_node.ty.data.array.len = start;
Expand Down Expand Up @@ -5114,6 +5133,8 @@ pub const Result = struct {
ty: Type = .{ .specifier = .int },
val: Value = .{},

const invalid: Result = .{ .ty = Type.invalid };

pub fn str(res: Result, p: *Parser) ![]const u8 {
switch (res.val.opt_ref) {
.none => return "(none)",
Expand Down Expand Up @@ -6932,8 +6953,11 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re
try ptr.lvalConversion(p);
try index.lvalConversion(p);

if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket_tok);
try p.checkArrayBounds(index, lhs, l_bracket_tok);
if (index.ty.isInt()) {
try p.checkArrayBounds(index, lhs, l_bracket_tok);
} else {
try p.errTok(.invalid_index, l_bracket_tok);
}

try index.saveValue(p);
try ptr.bin(p, .array_access_expr, index);
Expand Down Expand Up @@ -7260,6 +7284,7 @@ fn unExpr(p: *Parser) Error!Result {
var operand = try p.castExpr();
try operand.expect(p);
try operand.lvalConversion(p);
if (operand.ty.is(.invalid)) return Result.invalid;
if (!operand.ty.isInt() and !operand.ty.isFloat()) {
try p.errStr(.invalid_imag, imag_tok, try p.typeStr(operand.ty));
}
Expand Down Expand Up @@ -7290,6 +7315,7 @@ fn unExpr(p: *Parser) Error!Result {
var operand = try p.castExpr();
try operand.expect(p);
try operand.lvalConversion(p);
if (operand.ty.is(.invalid)) return Result.invalid;
if (!operand.ty.isInt() and !operand.ty.isFloat()) {
try p.errStr(.invalid_real, real_tok, try p.typeStr(operand.ty));
}
Expand Down Expand Up @@ -7435,12 +7461,18 @@ fn suffixExpr(p: *Parser, lhs: Result) Error!Result {
try index.lvalConversion(p);
if (ptr.ty.isPtr()) {
ptr.ty = ptr.ty.elemType();
if (!index.ty.isInt()) try p.errTok(.invalid_index, l_bracket);
try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket);
if (index.ty.isInt()) {
try p.checkArrayBounds(index_before_conversion, array_before_conversion, l_bracket);
} else {
try p.errTok(.invalid_index, l_bracket);
}
} else if (index.ty.isPtr()) {
index.ty = index.ty.elemType();
if (!ptr.ty.isInt()) try p.errTok(.invalid_index, l_bracket);
try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket);
if (ptr.ty.isInt()) {
try p.checkArrayBounds(array_before_conversion, index_before_conversion, l_bracket);
} else {
try p.errTok(.invalid_index, l_bracket);
}
std.mem.swap(Result, &ptr, &index);
} else {
try p.errTok(.invalid_subscript, l_bracket);
Expand Down Expand Up @@ -7742,6 +7774,10 @@ fn primaryExpr(p: *Parser) Error!Result {
const name_tok = try p.expectIdentifier();
const name = p.tokSlice(name_tok);
const interned_name = try StrInt.intern(p.comp, name);
if (interned_name == p.auto_type_decl_name) {
try p.errStr(.auto_type_self_initialized, name_tok, name);
return error.ParsingFailed;
}
if (p.syms.findSymbol(interned_name)) |sym| {
try p.checkDeprecatedUnavailable(sym.ty, name_tok, sym.tok);
if (sym.kind == .constexpr) {
Expand Down
3 changes: 2 additions & 1 deletion src/aro/Tokenizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,8 @@ pub const Token = struct {

pub fn symbol(id: Id) []const u8 {
return switch (id) {
.macro_string, .invalid => unreachable,
.macro_string => unreachable,
.invalid => "invalid bytes",
.identifier,
.extended_identifier,
.macro_func,
Expand Down
2 changes: 2 additions & 0 deletions src/aro/Type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,8 @@ pub fn integerPromotion(ty: Type, comp: *Compilation) Type {
switch (specifier) {
.@"enum" => {
if (ty.hasIncompleteSize()) return .{ .specifier = .int };
if (ty.data.@"enum".fixed) return ty.data.@"enum".tag_ty.integerPromotion(comp);

specifier = ty.data.@"enum".tag_ty.specifier;
},
.bit_int, .complex_bit_int => return .{ .specifier = specifier, .data = ty.data },
Expand Down
14 changes: 14 additions & 0 deletions test/cases/__auto_type self init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//aro-args -std=c23
void foo(void) {
int x;
__auto_type x = x;

__auto_type y = (int []){1, y};

auto z = z + 1;
}

#define EXPECTED_ERRORS "__auto_type self init.c:4:21: error: variable 'x' declared with deduced type '__auto_type' cannot appear in its own initializer" \
"__auto_type self init.c:6:33: error: variable 'y' declared with deduced type '__auto_type' cannot appear in its own initializer" \
"__auto_type self init.c:8:14: error: variable 'z' declared with deduced type '__auto_type' cannot appear in its own initializer" \

7 changes: 7 additions & 0 deletions test/cases/array designator too large.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
int arr1[] = { [0x800000000000000 - 1] = 0 };
int arr2[] = { [0x800000000000000 - 2] = 0 };
_Static_assert(sizeof(arr1), "");
_Static_assert(sizeof(arr2), "");

#define EXPECTED_ERRORS "array designator too large.c:1:14: error: array is too large" \

3 changes: 3 additions & 0 deletions test/cases/array subscript with string.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#define NO_ERROR_VALIDATION
int x[2];
x[""[""]
Binary file added test/cases/assembly invalid token.c
Binary file not shown.
8 changes: 8 additions & 0 deletions test/cases/enum fixed _BitInt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
enum E: _BitInt(20) {
A,
};

void foo(void) {
enum E e = 1;
int x = -e;
}
3 changes: 3 additions & 0 deletions test/cases/invalid _BitInt pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#define NO_ERROR_VALIDATION

_BitInt(1) *e= __real__ e;
2 changes: 2 additions & 0 deletions test/cases/typeof invalid pointer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#define NO_ERROR_VALIDATION
typeof((void)0 + 0) *a = 2;
Loading