| author | |
| committer | |
| log | 6a62a15ecde10300f6281e2d49fed34031d5f68a |
| tree | d0c5e7abd7ef2a1bc2b1d4ee283354f1da25322b |
| parent | a7661f115dccf26b141557c923171f325cdc2757 |
| parent | c7e45aebafef0372fe231816eeffd18198240f14 |
| signature |
Sema: fix UAF in zirClosureGet 10 files changed, 176 insertions(+), 32 deletions(-)
src/Module.zig+10| ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { | ... | @@ -345,6 +345,15 @@ pub const CaptureScope = struct { |
| 345 | /// During sema, this map is backed by the gpa. Once sema completes, | 345 | /// During sema, this map is backed by the gpa. Once sema completes, |
| 346 | /// it is reallocated using the value_arena. | 346 | /// it is reallocated using the value_arena. |
| 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, | 347 | captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, TypedValue) = .{}, |
| 348 | |||
| 349 | pub fn failed(noalias self: *const @This()) bool { | ||
| 350 | return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32); | ||
| 351 | } | ||
| 352 | |||
| 353 | pub fn fail(noalias self: *@This()) void { | ||
| 354 | self.captures.available = 0; | ||
| 355 | self.captures.size = std.math.maxInt(u32); | ||
| 356 | } | ||
| 348 | }; | 357 | }; |
| 349 | 358 | ||
| 350 | pub const WipCaptureScope = struct { | 359 | pub const WipCaptureScope = struct { |
| ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { | ... | @@ -383,6 +392,7 @@ pub const WipCaptureScope = struct { |
| 383 | pub fn deinit(noalias self: *@This()) void { | 392 | pub fn deinit(noalias self: *@This()) void { |
| 384 | if (!self.finalized) { | 393 | if (!self.finalized) { |
| 385 | self.scope.captures.deinit(self.gpa); | 394 | self.scope.captures.deinit(self.gpa); |
| 395 | self.scope.fail(); | ||
| 386 | } | 396 | } |
| 387 | self.* = undefined; | 397 | self.* = undefined; |
| 388 | } | 398 | } |
src/Sema.zig+32-30| ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( | ... | @@ -5956,7 +5956,6 @@ fn analyzeCall( |
| 5956 | error.NeededSourceLocation => { | 5956 | error.NeededSourceLocation => { |
| 5957 | _ = sema.inst_map.remove(inst); | 5957 | _ = sema.inst_map.remove(inst); |
| 5958 | const decl = sema.mod.declPtr(block.src_decl); | 5958 | const decl = sema.mod.declPtr(block.src_decl); |
| 5959 | child_block.src_decl = block.src_decl; | ||
| 5960 | try sema.analyzeInlineCallArg( | 5959 | try sema.analyzeInlineCallArg( |
| 5961 | block, | 5960 | block, |
| 5962 | &child_block, | 5961 | &child_block, |
| ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( | ... | @@ -13740,6 +13739,16 @@ fn zirClosureGet( |
| 13740 | const tv = while (true) { | 13739 | const tv = while (true) { |
| 13741 | // Note: We don't need to add a dependency here, because | 13740 | // Note: We don't need to add a dependency here, because |
| 13742 | // decls always depend on their lexical parents. | 13741 | // decls always depend on their lexical parents. |
| 13742 | |||
| 13743 | // Fail this decl if a scope it depended on failed. | ||
| 13744 | if (scope.failed()) { | ||
| 13745 | if (sema.owner_func) |owner_func| { | ||
| 13746 | owner_func.state = .dependency_failure; | ||
| 13747 | } else { | ||
| 13748 | sema.owner_decl.analysis = .dependency_failure; | ||
| 13749 | } | ||
| 13750 | return error.AnalysisFail; | ||
| 13751 | } | ||
| 13743 | if (scope.captures.getPtr(inst_data.inst)) |tv| { | 13752 | if (scope.captures.getPtr(inst_data.inst)) |tv| { |
| 13744 | break tv; | 13753 | break tv; |
| 13745 | } | 13754 | } |
| ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 | ... | @@ -18076,8 +18085,8 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 |
| 18076 | const target = sema.mod.getTarget(); | 18085 | const target = sema.mod.getTarget(); |
| 18077 | 18086 | ||
| 18078 | try sema.resolveTypeLayout(block, lhs_src, ty); | 18087 | try sema.resolveTypeLayout(block, lhs_src, ty); |
| 18079 | switch (ty.tag()) { | 18088 | switch (ty.zigTypeTag()) { |
| 18080 | .@"struct", .tuple, .anon_struct => {}, | 18089 | .Struct => {}, |
| 18081 | else => { | 18090 | else => { |
| 18082 | const msg = msg: { | 18091 | const msg = msg: { |
| 18083 | const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); | 18092 | const msg = try sema.errMsg(block, lhs_src, "expected struct type, found '{}'", .{ty.fmt(sema.mod)}); |
| ... | @@ -19617,28 +19626,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -19617,28 +19626,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 19617 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 19626 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19618 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 19627 | const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19619 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 19628 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19620 | const dest_ptr = try sema.resolveInst(extra.dest); | 19629 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); |
| 19621 | const dest_ptr_ty = sema.typeOf(dest_ptr); | ||
| 19622 | 19630 | ||
| 19623 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | 19631 | // TODO AstGen's coerced_ty cannot handle volatile here |
| 19624 | if (dest_ptr_ty.isConstPtr()) { | 19632 | var dest_ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; |
| 19625 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | 19633 | dest_ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); |
| 19626 | } | 19634 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, dest_ptr_info); |
| 19635 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); | ||
| 19627 | 19636 | ||
| 19628 | const uncasted_src_ptr = try sema.resolveInst(extra.source); | 19637 | const uncasted_src_ptr = try sema.resolveInst(extra.source); |
| 19629 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | 19638 | var src_ptr_info = Type.initTag(.manyptr_const_u8).ptrInfo().data; |
| 19630 | try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); | 19639 | src_ptr_info.@"volatile" = sema.typeOf(uncasted_src_ptr).isVolatilePtr(); |
| 19631 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | 19640 | const src_ptr_ty = try Type.ptr(sema.arena, sema.mod, src_ptr_info); |
| 19632 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, sema.mod, .{ | 19641 | const src_ptr = try sema.coerce(block, src_ptr_ty, uncasted_src_ptr, src_src); |
| 19633 | .pointee_type = dest_ptr_ty.elemType2(), | ||
| 19634 | .@"align" = src_ptr_info.@"align", | ||
| 19635 | .@"addrspace" = src_ptr_info.@"addrspace", | ||
| 19636 | .mutable = false, | ||
| 19637 | .@"allowzero" = src_ptr_info.@"allowzero", | ||
| 19638 | .@"volatile" = src_ptr_info.@"volatile", | ||
| 19639 | .size = .Many, | ||
| 19640 | }); | ||
| 19641 | const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src); | ||
| 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | 19642 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19643 | 19643 | ||
| 19644 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { | 19644 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { |
| ... | @@ -19674,14 +19674,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -19674,14 +19674,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 19674 | const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | 19675 | const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 19676 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 19677 | const dest_ptr = try sema.resolveInst(extra.dest); | 19677 | const uncasted_dest_ptr = try sema.resolveInst(extra.dest); |
| 19678 | const dest_ptr_ty = sema.typeOf(dest_ptr); | 19678 | |
| 19679 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); | 19679 | // TODO AstGen's coerced_ty cannot handle volatile here |
| 19680 | if (dest_ptr_ty.isConstPtr()) { | 19680 | var ptr_info = Type.initTag(.manyptr_u8).ptrInfo().data; |
| 19681 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty.fmt(sema.mod)}); | 19681 | ptr_info.@"volatile" = sema.typeOf(uncasted_dest_ptr).isVolatilePtr(); |
| 19682 | } | 19682 | const dest_ptr_ty = try Type.ptr(sema.arena, sema.mod, ptr_info); |
| 19683 | const elem_ty = dest_ptr_ty.elemType2(); | 19683 | const dest_ptr = try sema.coerce(block, dest_ptr_ty, uncasted_dest_ptr, dest_src); |
| 19684 | const value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.byte), value_src); | 19684 | |
| 19685 | const value = try sema.coerce(block, Type.u8, try sema.resolveInst(extra.byte), value_src); | ||
| 19685 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); | 19686 | const len = try sema.coerce(block, Type.usize, try sema.resolveInst(extra.byte_count), len_src); |
| 19686 | 19687 | ||
| 19687 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { | 19688 | const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |ptr_val| rs: { |
| ... | @@ -26013,6 +26014,7 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref | ... | @@ -26013,6 +26014,7 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref |
| 26013 | .pointee_type = decl_tv.ty, | 26014 | .pointee_type = decl_tv.ty, |
| 26014 | .mutable = false, | 26015 | .mutable = false, |
| 26015 | .@"addrspace" = decl.@"addrspace", | 26016 | .@"addrspace" = decl.@"addrspace", |
| 26017 | .@"align" = decl.@"align", | ||
| 26016 | }), | 26018 | }), |
| 26017 | try Value.Tag.decl_ref.create(sema.arena, decl_index), | 26019 | try Value.Tag.decl_ref.create(sema.arena, decl_index), |
| 26018 | ); | 26020 | ); |
src/codegen/llvm.zig+10-1| ... | @@ -9204,6 +9204,12 @@ pub const FuncGen = struct { | ... | @@ -9204,6 +9204,12 @@ pub const FuncGen = struct { |
| 9204 | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); | 9204 | return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); |
| 9205 | } | 9205 | } |
| 9206 | 9206 | ||
| 9207 | if (info.pointee_type.isPtrAtRuntime()) { | ||
| 9208 | const same_size_int = self.context.intType(elem_bits); | ||
| 9209 | const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); | ||
| 9210 | return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); | ||
| 9211 | } | ||
| 9212 | |||
| 9207 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); | 9213 | return self.builder.buildTrunc(shifted_value, elem_llvm_ty, ""); |
| 9208 | } | 9214 | } |
| 9209 | 9215 | ||
| ... | @@ -9235,7 +9241,10 @@ pub const FuncGen = struct { | ... | @@ -9235,7 +9241,10 @@ pub const FuncGen = struct { |
| 9235 | // Convert to equally-sized integer type in order to perform the bit | 9241 | // Convert to equally-sized integer type in order to perform the bit |
| 9236 | // operations on the value to store | 9242 | // operations on the value to store |
| 9237 | const value_bits_type = self.context.intType(elem_bits); | 9243 | const value_bits_type = self.context.intType(elem_bits); |
| 9238 | const value_bits = self.builder.buildBitCast(elem, value_bits_type, ""); | 9244 | const value_bits = if (elem_ty.isPtrAtRuntime()) |
| 9245 | self.builder.buildPtrToInt(elem, value_bits_type, "") | ||
| 9246 | else | ||
| 9247 | self.builder.buildBitCast(elem, value_bits_type, ""); | ||
| 9239 | 9248 | ||
| 9240 | var mask_val = value_bits_type.constAllOnes(); | 9249 | var mask_val = value_bits_type.constAllOnes(); |
| 9241 | mask_val = mask_val.constZExt(containing_int_ty); | 9250 | mask_val = mask_val.constZExt(containing_int_ty); |
src/translate_c.zig+1-1| ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD | ... | @@ -1167,7 +1167,7 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD |
| 1167 | } | 1167 | } |
| 1168 | 1168 | ||
| 1169 | if (!c.zig_is_stage1 and is_packed) { | 1169 | if (!c.zig_is_stage1 and is_packed) { |
| 1170 | return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{}); | 1170 | return failDecl(c, record_loc, name, "cannot translate packed record union", .{}); |
| 1171 | } | 1171 | } |
| 1172 | 1172 | ||
| 1173 | const record_payload = try c.arena.create(ast.Payload.Record); | 1173 | const record_payload = try c.arena.create(ast.Payload.Record); |
test/behavior.zig+1| ... | @@ -86,6 +86,7 @@ test { | ... | @@ -86,6 +86,7 @@ test { |
| 86 | _ = @import("behavior/bugs/12430.zig"); | 86 | _ = @import("behavior/bugs/12430.zig"); |
| 87 | _ = @import("behavior/bugs/12486.zig"); | 87 | _ = @import("behavior/bugs/12486.zig"); |
| 88 | _ = @import("behavior/bugs/12680.zig"); | 88 | _ = @import("behavior/bugs/12680.zig"); |
| 89 | _ = @import("behavior/bugs/12776.zig"); | ||
| 89 | _ = @import("behavior/byteswap.zig"); | 90 | _ = @import("behavior/byteswap.zig"); |
| 90 | _ = @import("behavior/byval_arg_var.zig"); | 91 | _ = @import("behavior/byval_arg_var.zig"); |
| 91 | _ = @import("behavior/call.zig"); | 92 | _ = @import("behavior/call.zig"); |
test/behavior/bugs/12776.zig created+42| ... | @@ -0,0 +1,42 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 4 | const RAM = struct { | ||
| 5 | data: [0xFFFF + 1]u8, | ||
| 6 | fn new() !RAM { | ||
| 7 | return RAM{ .data = [_]u8{0} ** 0x10000 }; | ||
| 8 | } | ||
| 9 | fn get(self: *RAM, addr: u16) u8 { | ||
| 10 | return self.data[addr]; | ||
| 11 | } | ||
| 12 | }; | ||
| 13 | |||
| 14 | const CPU = packed struct { | ||
| 15 | interrupts: bool, | ||
| 16 | ram: *RAM, | ||
| 17 | fn new(ram: *RAM) !CPU { | ||
| 18 | return CPU{ | ||
| 19 | .ram = ram, | ||
| 20 | .interrupts = false, | ||
| 21 | }; | ||
| 22 | } | ||
| 23 | fn tick(self: *CPU) !void { | ||
| 24 | var queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F); | ||
| 25 | if (self.interrupts and queued_interrupts != 0) { | ||
| 26 | self.interrupts = false; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | test { | ||
| 32 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 33 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 34 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 35 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 36 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 37 | |||
| 38 | var ram = try RAM.new(); | ||
| 39 | var cpu = try CPU.new(&ram); | ||
| 40 | try cpu.tick(); | ||
| 41 | try std.testing.expect(cpu.interrupts == false); | ||
| 42 | } | ||
test/behavior/pointers.zig+11| ... | @@ -486,3 +486,14 @@ test "array slicing to slice" { | ... | @@ -486,3 +486,14 @@ test "array slicing to slice" { |
| 486 | try S.doTheTest(); | 486 | try S.doTheTest(); |
| 487 | comptime try S.doTheTest(); | 487 | comptime try S.doTheTest(); |
| 488 | } | 488 | } |
| 489 | |||
| 490 | test "pointer to constant decl preserves alignment" { | ||
| 491 | const S = struct { | ||
| 492 | a: u8, | ||
| 493 | b: u8, | ||
| 494 | const aligned align(8) = @This(){ .a = 3, .b = 4 }; | ||
| 495 | }; | ||
| 496 | |||
| 497 | const alignment = @typeInfo(@TypeOf(&S.aligned)).Pointer.alignment; | ||
| 498 | try std.testing.expect(alignment == 8); | ||
| 499 | } |
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | pub inline fn instanceRequestAdapter() void {} | ||
| 2 | |||
| 3 | pub inline fn requestAdapter( | ||
| 4 | comptime callbackArg: fn () callconv(.Inline) void, | ||
| 5 | ) void { | ||
| 6 | _ = (struct { | ||
| 7 | pub fn callback() callconv(.C) void { | ||
| 8 | callbackArg(); | ||
| 9 | } | ||
| 10 | }).callback; | ||
| 11 | instanceRequestAdapter(undefined); // note wrong number of arguments here | ||
| 12 | } | ||
| 13 | |||
| 14 | inline fn foo() void {} | ||
| 15 | |||
| 16 | pub export fn entry() void { | ||
| 17 | requestAdapter(foo); | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=stage2 | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :11:5: error: expected 0 argument(s), found 1 | ||
| 25 | // :1:12: note: function declared here | ||
| 26 | // :17:19: note: called from here | ||
test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | fn Observable(comptime T: type) type { | ||
| 2 | return struct { | ||
| 3 | fn map(Src: T, Dst: anytype, function: fn (T) Dst) Dst { | ||
| 4 | _ = Src; | ||
| 5 | _ = function; | ||
| 6 | return Observable(Dst); | ||
| 7 | } | ||
| 8 | }; | ||
| 9 | } | ||
| 10 | |||
| 11 | fn u32Tou64(x: u32) u64 { | ||
| 12 | _ = x; | ||
| 13 | return 0; | ||
| 14 | } | ||
| 15 | |||
| 16 | pub export fn entry() void { | ||
| 17 | Observable(u32).map(u32, u64, u32Tou64(0)); | ||
| 18 | } | ||
| 19 | |||
| 20 | // error | ||
| 21 | // backend=stage2 | ||
| 22 | // target=native | ||
| 23 | // | ||
| 24 | // :17:25: error: expected type 'u32', found 'type' | ||
test/cases/compile_errors/incorrect_type_to_memset_memcpy.zig created+19| ... | @@ -0,0 +1,19 @@ | ||
| 1 | pub export fn entry() void { | ||
| 2 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 3 | var slice: []u8 = &buf; | ||
| 4 | const a: u32 = 1234; | ||
| 5 | @memcpy(slice, @ptrCast([*]const u8, &a), 4); | ||
| 6 | } | ||
| 7 | pub export fn entry1() void { | ||
| 8 | var buf: [5]u8 = .{ 1, 2, 3, 4, 5 }; | ||
| 9 | var ptr: *u8 = &buf[0]; | ||
| 10 | @memcpy(ptr, 0, 4); | ||
| 11 | } | ||
| 12 | |||
| 13 | // error | ||
| 14 | // backend=stage2 | ||
| 15 | // target=native | ||
| 16 | // | ||
| 17 | // :5:13: error: expected type '[*]u8', found '[]u8' | ||
| 18 | // :10:13: error: expected type '[*]u8', found '*u8' | ||
| 19 | // :10:13: note: a single pointer cannot cast into a many pointer | ||