LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 45099 - std::any uses mismatched allocation and deallocation
Summary: std::any uses mismatched allocation and deallocation
Status: RESOLVED FIXED
Alias: None
Product: libc++
Classification: Unclassified
Component: All Bugs (show other bugs)
Version: unspecified
Hardware: All All
: P normal
Assignee: Louis Dionne
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-03-04 06:20 PST by Thomas Koeppe
Modified: 2020-09-15 09:42 PDT (History)
3 users (show)

See Also:
Fixed By Commit(s): 39c8795141703a7d8313b2448d9d34e856df0b85


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas Koeppe 2020-03-04 06:20:38 PST
The constructors of std::any that create an object of type T use std::allocator<T> to allocate memory and "::new ((T*)p)" placement new to construct the object. (That is, memory allocation subject only to specializations of std::allocator, and construction only to overloads of global operator new for user pointer types.)

By contrast, destruction uses "delete (T*)p". This means that overloaded operators T::operator delete will be used.

(In the above, the cast notation "(T*)" doesn't appear literally in the code, but is just my way of indicating the argument type. Specifically, note that there is no cast to void* in the placement new.)

This mismatch results in undefined behaviour whenever a suitable amount of user customization is present for T. Examples can be constructed to exercise all aspects of this (specializing std::allocator, adding a ::operator new(std::size_t, T*) overload, or adding overloaded operators T::operator delete).

One example:

================

#include <any>
#include <cstddef>
#include <cstdlib>

struct U {
  int big[128];
  static void* operator new(std::size_t n) { return std::malloc(n); }
  static void operator delete(void* p) { std::free(p); }
};

int main() {
    std::any a(std::in_place_type<U>);
    return std::any_cast<U*>(a) != nullptr;  // avoid optimizing everything away
}

================

The generated code clearly shows one call to "operator new" and one to "free".


One solution would be to make the construction use an unqualified new expression "new _Tp(...)"; this means that standard library code will call user-defined functions for internal bookkeeping. An alternative solution would be to also use std::allocator to deallocate the allocation.
Comment 1 Marshall Clow (home) 2020-06-03 08:04:02 PDT
There's more to this than the reported issue.

The any implementation uses placement new to construct the objects.
This should really be allocator_traits<allocator<T>>::construct(...).

And T::~T should be allocator_traits<allocator<T>>::destroy(...).

But yes, the issue as reported is correct.
Comment 2 Marshall Clow (home) 2020-06-04 07:47:42 PDT
reading up on this some more, I note that (https://wg21.link/allocator.members):

std::allocator<T>::allocate is required to get its storage from `::operator new`, and 
std::allocator<T>::delete is required to use `::operator delete`

but there's certainly some wiggle room in those specifications. (Deliberately, I'm sure)
Comment 3 Thomas Koeppe 2020-06-04 08:17:18 PDT
(In reply to Marshall Clow (home) from comment #2)
> reading up on this some more, I note that
> (https://wg21.link/allocator.members):
> 
> std::allocator<T>::allocate is required to get its storage from `::operator
> new`, and 
> std::allocator<T>::delete is required to use `::operator delete`
> 
> but there's certainly some wiggle room in those specifications.
> (Deliberately, I'm sure)

Yes, this is true, but the way I read this is merely as a guarantee that, say, replacements of "::operator new" are observed by (any specialization of) std::allocator. It doesn't preclude specializations from doing additional, custom work (so the allocator should still be called in matching pairs, and not freely interchanged with direct calls to "::operator new/delete").
Comment 4 Louis Dionne 2020-09-15 07:55:32 PDT
Took over potential solution in https://reviews.llvm.org/D81133.
Comment 5 Louis Dionne 2020-09-15 09:42:18 PDT
Fixed by 39c8795141703a7d8313b2448d9d34e856df0b85