-
Notifications
You must be signed in to change notification settings - Fork 19
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
ACP: Replace GlobalAlloc::realloc
with GlobalAlloc::resize
#61
Comments
GlobalAlloc::realloc
with GlobalAlloc::resize
GlobalAlloc::realloc
with GlobalAlloc::resize
Why not stabilize just the |
Because we're still not completely certain that
|
A month or so ago I wrote https://hackmd.io/ld8wv8kHQaKF_YsU6r8CKQ?view about my experience trying to use the Allocator trait in anger in my game engine over a year ago. The experience made me feel that the current design needs more work and should take inspiration from the RawWaker design, perhaps. It's rough and I've been meaning to clean it up, but since it's being discussed I may as well just drop the link.
It should be a separate API, IMO. I think I've filed issues to this effect in |
We discussed this in the libs meeting and decided that simply updating the requirements in the documentation as proposed in rust-lang/rust#108630 is the best way forward for |
Proposal
Problem statement
The
GlobalAlloc::realloc
function (and the freestd::alloc::realloc
function) takes anew_size: usize
argument. The current documentation of the function requires thatNo mention is currently made anywhere in the
std::alloc
documentation about the fact that rust created allocations are required to be no more thanisize::MAX
bytes.Effectively, this means that
GlobalAlloc::realloc
has an undocumented requirement thatnew_size
(when rounded up) does not exceedisize::MAX
(notusize::MAX
, as is currently documented). Neither callers nor implementers have been told that this requirement exists.Experience in the stdlib has shown that even the most highly reviewed Rust code still forgets to manually enforce the
isize::MAX
limit in edge cases (e.g. rust-lang/rust#95334). For this reason, rust-lang/rust#95295 is currently FCP-merge to automatically enforce that layout sizes respect theisize::MAX
limit withLayout
.This ensures that all Rust allocation APIs cannot request an object larger than
isize::MAX
... except forGlobalAlloc::realloc
, which takes a plainusize
for the new size, and not aLayout
.Motivation, use-cases
One way or another,
GlobalAlloc::realloc
needs to be changed so that the correctisize::MAX
limit is respected. There are two available solutions that do not change stable API: caller enforced (change the documentation to readisize::MAX
) or callee enforced (allGlobalAlloc
implementers must check and prevent too-large allocations from succeeding).A new function which takes
Layout
is also desirable, as this would realignrealloc
with the other allocation functions in automatically enforcing the size limit via theLayout
type invariants. In addition, by passing a requested new alignment, this enables alignment changing realloc.Allocators will often serve all allocations with an alignment of at least some minimum alignment. On x86, this is often 8 bytes; on x86_64, 16. So while Rust's allocation APIs strictly require deallocating with the same alignment used to allocate, in practice it's often fine to allocate at low alignment and deallocate at the basic alignment.
An alignment changing realloc allows users to communicate that this is what they are doing to the compiler. By calling an aligning realloc with the correct size and alignment, it is possible to do conversions like
Vec<u8>
intoVec<u32>
without copying and without falling afoul of mismatched alloc/dealloc alignment.Solution sketches
caller enforced, deprecate, and replace
This is the author's preferred option.
Document that
GlobalAlloc::realloc
has a safety requirement that the rounded size must not exceedisize::MAX
. Implementers may continue to service realloc requests exactly as they come in.Additionally, deprecate
GlobalAlloc::realloc
and encourage users to call a replacement function instead:A new free function
std::alloc::resize
is also provided which callsGlobal::resize
,std::alloc::realloc
is deprecated in favor ofstd::alloc::resize
, and#[global_allocator]
is updated to plumbGlobalAlloc::resize
in addition to the existingGlobalAlloc
methods.The System allocator implementation is updated to implement
resize
where both old and new alignment are below the minimum guaranteed alignment via the system realloc. When either alignment exceeds this, the fallback realloc is used.Windows implementation
Possible alternative names to
resize
includealigned_realloc
,realloc_s
, andrealign
. Alternatively, separategrow
/shrink
could be provided, as is done in theAllocator
trait.Why provide a new
GlobalAlloc
function?Allocator
stabilizing is still a distant future thing, as at a minimum it is blocked on fixing MIR to not treatBox
as a primitive pointer when it contains an allocator. Providing a newGlobalAlloc
function is a lower effort solution which still takes the burden of remembering to check the size limit off of callers. The ability to change the alignment at the same time is an additional bonus, but not a core motivator for providing the new API.Unfortunately, the result of forgetting to check the size limit and allocating an object of more than
isize::MAX
bytes is likely to appear to just work. This is a subtle soundness hole, and one reasonably unlikely to hit miscompilations... until it does, likely resulting in some completely inscrutable failure far removed from the actual problem.caller enforced
Document that
GlobalAlloc::realloc
has a safety requirement that the rounded size must not exceedisize::MAX
. Implementers may continue to service realloc requests exactly as they come in.Eventually,
GlobalAlloc
will be deprecated in favor of theAllocator
trait, and#[global_allocator]
will also be redefined to work onAllocator
instead ofGlobalAlloc
. At this point, reallocation will take a newLayout
and automatically enforce theisize::MAX
limit.Until that point, callers of
realloc
will need to manually enforce that the rounded size does not exceedisize::MAX
.callee enforced
Document that
GlobalAlloc::realloc
must be implemented to fail to allocate more thanisize::MAX
bytes.Instead of putting the burden on developers calling the raw allocation functions, we instead tell roughly every
GlobalAlloc
implementer (that doesn't just wrap another implementer) that oops! they're actually subtly unsound. At least the cost of checking the limit is minimized, since the allocator necessarily is already checking the size... except no, not really; anyGlobalAlloc
which calls an existing allocator which does not guarantee that allocations of more thanisize::MAX
bytes fail will have to separately check that limit before calling into the actual allocator implementation.Eventually,
GlobalAlloc
will be deprecated in favor of theAllocator
trait, and#[global_allocator]
will also be redefined to work onAllocator
instead ofGlobalAlloc
. At this point, reallocation will take a newLayout
and automatically enforce theisize::MAX
limit.Links and related work
GlobalAlloc
GlobalAlloc
isize::MAX
limit inLayout
comes almost entirely for free since it already needs to check thatusize
overflow cannot happen.What happens now?
This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.
The text was updated successfully, but these errors were encountered: