On 11.09.24 16:37, Danilo Krummrich wrote: > On Wed, Sep 11, 2024 at 01:32:31PM +0000, Benno Lossin wrote: >> On 11.09.24 14:31, Danilo Krummrich wrote: >>> On Fri, Aug 30, 2024 at 12:25:27AM +0200, Danilo Krummrich wrote: >>>> On Thu, Aug 29, 2024 at 07:14:18PM +0000, Benno Lossin wrote: >>>>> On 16.08.24 02:11, Danilo Krummrich wrote: >>>>>> + >>>>>> + if layout.size() == 0 { >>>>>> + // SAFETY: `src` has been created by `Self::alloc_store_data`. >>>>> >>>>> This is not true, consider: >>>>> >>>>> let ptr = alloc(size = 0); >>>>> free(ptr) >>>>> >>>>> Alloc will return a dangling pointer due to the first if statement and >>>>> then this function will pass it to `free_read_data`, even though it >>>>> wasn't created by `alloc_store_data`. >>>>> This isn't forbidden by the `Allocator` trait function's safety >>>>> requirements. >>>>> >>>>>> + unsafe { Self::free_read_data(src) }; >>>>>> + >>>>>> + return Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0)); >>>>>> + } >>>>>> + >>>>>> + let dst = Self::alloc(layout, flags)?; >>>>>> + >>>>>> + // SAFETY: `src` has been created by `Self::alloc_store_data`. >>>>>> + let data = unsafe { Self::data(src) }; >>>>> >>>>> Same issue here, if the allocation passed in is zero size. I think you >>>>> have no other choice than to allocate even for zero size requests... >>>>> Otherwise how would you know that they are zero-sized. >>>> >>>> Good catch - gonna fix it. >>> >>> Almost got me. :) I think the code is fine, callers are not allowed to pass >>> pointers to `realloc` and `free`, which haven't been allocated with the same >>> corresponding allocator or are dangling. >> >> But what about the example above (ie the `alloc(size = 0)` and then >> `free`)? > > This never has been valid for the `Allocator` trait. Look at `Kmalloc`, > `Vmalloc` and `KVmalloc`, they don't allow this either. That is true. > We've discussed this already in previous versions of this series, where for this > purpose, you asked for `old_layout` for `free`. Such that `free` can check if > the `size` was zero and therefore return without doing anything. Yes, but that was only about the old_layout parameter (at least that's what I thought). >> I guess this all depends on how one interprets the term >> "existing, valid memory allocation". To me that describes anything an >> `Allocator` returns via `alloc` and `realloc`, including zero-sized >> allocations. > > I argue that the dangling pointer returned for `size == 0` does not point to any > allocation in the sense of those allocators. It's just a dangling `[u8]` > pointer. Sure, but to me the concept of zero-sized allocations does exist. >> But if you argue that those are not valid allocations from that >> allocator, then that is not properly documented in the safety >> requirements of `Allocator`. > > The safety requirements of `Allocator` where proposed by you and I thought they > consider this aspect? No, they did not consider this aspect. I was under the impression, that we would still allow zero-sized allocations (in retrospect, this is stupid, since dangling pointers shouldn't be passed to `krealloc` etc.). > `realloc` has: > > "If `ptr == Some(p)`, then `p` must point to an existing and valid memory > allocation created by this allocator." > > `free` has: > > "`ptr` must point to an existing and valid memory allocation created by this > `Allocator` and must not be a dangling pointer." > > We can add the part about the dangling pointer to `realloc` if you want. So I think we should do the following: (1) Add a paragraph to the `Allocator` trait that explains that zero-sized allocations are not supported. (2) Add a check to `realloc` for zero-sized allocations + null pointer (ie a new allocation request) that prints a warning and returns an error (3) Instead of writing "existing and valid memory allocation created by this allocator", I think "valid non-zero-sized memory allocation created by this allocator" fits better. --- Cheers, Benno