Skip to content

Idea: Allocator trait should exclude Copy trait, require Drop trait, and get mutably borrowed on allocations #136

@Zero-Tang

Description

@Zero-Tang

Neither does current Allocator trait exclude Copy trait nor does it require Drop trait.

Scenario 1: No implementation of Copy

Only one allocation is allowed in this scenario. The following example won't compile.

let a=AltAlloc::default();
// Compiler will allow this allocation.
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
// Compiler will reject this allocation.
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);

Scenario 2: No implementation of Drop, but Copy is implemented

This can cause some issues, though the following example will compile.

let a=AltAlloc::default();
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);

Waste of Resource if AltAlloc is implemented by slicing huge internal array

Assume AltAlloc is an array of u8:

#[derive(Copy,Clone)] struct AltAlloc
{
    internal:[u8;0x10000],    // Each allocator has 64K bytes.
    // Internal members that records allocations.
    /**/
}

This AltAlloc might not need a real Drop trait implementation. But the Copy trait will make this allocator to be created more than once. In above example, there are two allocators actually created. In other words, 128K was allocated, rather than 64K.

// Allocated 64K.
let a=AltAlloc::default();
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
// Allocated another 64K.
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);

Use After Free if AltAlloc is implemented by slicing referenced internal array

Assume AltAlloc includes a pointer to u8:

#[derive(Copy,Clone)] struct AltAlloc
{
    internal:*mut u8,    // Pointer received from `VirtualAlloc`, `mmap`, etc.
    // Records of allocations are included inside `internal`.
}

In this case, using Copy trait is fine because it only copies a pointer. It won't cause repetitive huge memory allocations. However, this AltAlloc requires something like Drop trait. Since Copy trait is implemented, Drop trait can't be implemented. Assume a destroy method is implemented:

impl AltAlloc
{
	pub unsafe fn destroy(self)
	{
		/* Internal implementation that calls munmap, VirtualFree, etc. */
	}
}

Then it would cause Use-After-Free while dropping:

let a=AltAlloc::default();
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);
a.destroy();
// Dropping ab1 and ab2 requires using data in `a.internal`.
// This causes `Use-After-Free` fault.

The only workaround is via scoping:

let a=AltAlloc::default();
{
    let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
    let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);
    // ab1 and ab2 are dropped right before this scope ends.
}
a.destroy();

If Drop trait is implemented, then destroy method won't be required.

let a=AltAlloc::default();
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,a);
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,a);
// Ideally, Rust will drop things in the order of: ab2, ab1, a.

However, Copy trait can't co-exist with Drop trait. That's because it would cause doubled-free if AltAlloc contains only a pointer.

Idea: Allocators should get borrowed

If allocators are borrowed, then Copy trait won't be needed. I think Allocator trait should exclude Copy trait.

pub fn new_in(x: T, alloc: &mut A) -> Self
let mut a=AltAlloc::default();
let ab1:Box<u32,AltAlloc>=Box::new_in(1234,&mut a);
let ab2:Box<u32,AltAlloc>=Box::new_in(4321,&mut a);
// Rust will drop things in the order of: ab2, ab1, a.
// This works fine even if `AltAlloc` is implemented via slicing an internal huge array on stack.

Same principle can be applied on String, Vec, etc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions