https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124786

            Bug ID: 124786
           Summary: cannot create std::deque larger than max_size when
                    using mmap allocator
           Product: gcc
           Version: 15.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: pali at kernel dot org
  Target Milestone: ---

Define own c++ allocator which directly uses the mmap() and munmap() syscalls
and always allocate just one page (if the requested allocation size does not
fit into one page then throw a c++ exception). Then use this allocator for
std::deque variable.

Example code:

    #include <memory>
    #include <limits>
    #include <deque>
    #include <cstdio>

    #include <unistd.h>
    #include <sys/mman.h>

    template <class T>
    class MmapAllocator : public std::allocator<T>
    {
    private:
        long page_size = sysconf(_SC_PAGE_SIZE);

    public:
        typedef std::size_t size_type;
        typedef T value_type;
        MmapAllocator() = default;
        template <class U> constexpr MmapAllocator(const MmapAllocator<U>&)
noexcept{}

        template <typename U> struct rebind { typedef MmapAllocator<U> other;
};
        using std::allocator<T>::allocator;

        T* allocate(std::size_t n)
        {
            if (n > std::numeric_limits<std::size_t>::max() / sizeof(T))
                throw std::bad_array_new_length();

            if (n * sizeof(T) > std::size_t(page_size))
                throw std::bad_array_new_length();

            void* mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
                             MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
            if (mem == MAP_FAILED)
                throw std::bad_alloc();

            return static_cast<T*>(mem);
        }

        void deallocate(T* p, std::size_t) noexcept
        {
            munmap(static_cast<void*>(p), page_size);
        }

    #ifndef WITHOUT_MMAP_MAX_SIZE
        constexpr size_type max_size() const
        {
            return page_size;
        }
    #endif
    };

    int main() {
        std::deque<int, MmapAllocator<int>> d;
        printf("max_size=%zu\n", d.max_size());
        for (int i = 0; i < 10000; i++)
        {
            try
            {
                d.push_front(i);
            }
            catch (const std::exception& e)
            {
                printf("push error at size=%zu\n", d.size());
                throw;
            }
        }
        printf("all OK, size=%zu\n", d.size());
        return 0;
    }

When compiled without any option and run it fails on error:

    max_size=4096
    push error at size=4096
    terminate called after throwing an instance of 'std::length_error'
      what():  cannot create std::deque larger than max_size()

When compiled with -DWITHOUT_MMAP_MAX_SIZE which explicitly does not define
allocator's max_size function then it runs without any error:

    max_size=2305843009213693951
    all OK, size=10000

It looks like that gcc's std::deque is incorrectly using the allocator's
max_size. It is able to allocate a memory and push a new element into
std::deque container with the same allocator when it is not reporting the
max_size at all. The allocator is checking that the requested size is not more
than the page size.

This looks like a bug in gcc's libstdc++ std::deque implementation.

Reply via email to