compiler: Inspect reference shapes and allocator fields safely

b72fe7367782c217edaa043b472db28e2562cdcd12bed9bb5e617e8072fb5567
Alexis Sellier committed ago 1 parent 0ed82ea3
lib/std/lang/resolver.rad +18 -14
2881 2881
        else => return false,
2882 2882
    }
2883 2883
}
2884 2884
2885 2885
/// Return whether a stored type contains a reference without a named region.
2886 -
unsafe fn containsUnscopedRef(ty: Type) -> bool {
2886 +
fn containsUnscopedRef(ty: Type) -> bool {
2887 2887
    if isRefType(ty) and referenceRegion(ty) == nil {
2888 2888
        return true;
2889 2889
    }
2890 2890
    if let case Type::Pointer { target, .. } = ty {
2891 2891
        return containsUnscopedRef(*target);
7537 7537
7538 7538
    // Associate return type to call.
7539 7539
    return setNodeType(self, node, *applied.returnType);
7540 7540
}
7541 7541
7542 -
/// Check the allocator layout and the callback ABI used by slice append.
7543 -
unsafe fn isSliceAllocator(ty: Type) -> bool {
7544 -
    let case Type::Nominal(NominalType::Record(rec)) = ty else return false;
7545 -
    if rec.fields.len <> 2 or not rec.labeled {
7542 +
/// Check labeled record fields against the slice allocator layout and callback ABI.
7543 +
fn isSliceAllocator(fields: &[RecordField]) -> bool {
7544 +
    if fields.len <> 2 {
7546 7545
        return false;
7547 7546
    }
7548 -
    let func = rec.fields[0];
7549 -
    let ctx = rec.fields[1];
7547 +
    let func = fields[0];
7548 +
    let ctx = fields[1];
7550 7549
    let funcName = func.name else return false;
7551 7550
    let ctxName = ctx.name else return false;
7552 7551
    if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or
7553 7552
       func.offset <> 0 or ctx.offset <> 8
7554 7553
    {
7593 7592
    // The allocator stores its callback and context at fixed offsets.
7594 7593
    let allocatorTy = try infer(self, args[1]);
7595 7594
    if let case Type::Nominal(info) = allocatorTy {
7596 7595
        try ensureNominalResolved(self, info, args[1]);
7597 7596
    }
7598 -
    if not isSliceAllocator(allocatorTy) {
7597 +
    let mut validAllocator = false;
7598 +
    if let case Type::Nominal(NominalType::Record(rec)) = allocatorTy; rec.labeled {
7599 +
        set validAllocator = isSliceAllocator(&rec.fields[..]);
7600 +
    }
7601 +
    if not validAllocator {
7599 7602
        throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator);
7600 7603
    }
7601 7604
    set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType };
7602 7605
7603 7606
    // Return the parent's type so the caller can rebind:
8637 8640
        else => return false,
8638 8641
    }
8639 8642
}
8640 8643
8641 8644
/// Find the exclusive handle that owns an addressed place.
8642 -
unsafe fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node {
8643 -
    let mut parent: *ast::Node = undefined;
8645 +
fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node {
8646 +
    let mut parent: ?*ast::Node = nil;
8644 8647
    match node.value {
8645 8648
        case ast::NodeValue::Deref(target) => set parent = target,
8646 8649
        case ast::NodeValue::FieldAccess(access) => set parent = access.parent,
8647 8650
        case ast::NodeValue::Subscript { container, .. } => set parent = container,
8648 -
        else => return nil,
8651 +
        else => {}
8649 8652
    }
8650 -
    if let ty = typeFor(self, parent) {
8653 +
    let parentNode = parent else return nil;
8654 +
    if let ty = typeFor(self, parentNode) {
8651 8655
        match ty {
8652 -
            case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parent,
8656 +
            case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parentNode,
8653 8657
            else => {}
8654 8658
        }
8655 8659
    }
8656 -
    return addressOwner(self, parent);
8660 +
    return addressOwner(self, parentNode);
8657 8661
}
8658 8662
8659 8663
/// Analyze an address-of expression.
8660 8664
unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type
8661 8665
    throws (ResolveError)
lib/std/lang/resolver/tests.rad +3 -0
7842 7842
}
7843 7843
7844 7844
/// Slice append requires the allocator layout and callback signature.
7845 7845
@test unsafe fn testSliceAppendAllocatorRejected() throws (testing::TestError) {
7846 7846
    let programs = &[
7847 +
        "record A {} fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7848 +
        "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7849 +
        "record A(fn(*mut opaque, u32, u32) -> *mut opaque, *mut opaque); fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7847 7850
        "fn run(s: *mut [u8]) { s.append(1, 0); }",
7848 7851
        "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7849 7852
        "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7850 7853
        "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",
7851 7854
        "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }",