public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc(refs/vendors/ARM/heads/morello)] Update mt_allocator for CHERI
@ 2022-10-19 11:01 Matthew Malcomson
  0 siblings, 0 replies; only message in thread
From: Matthew Malcomson @ 2022-10-19 11:01 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:7c3edbbe0452d99192b97c88fdd65fb321e85e76

commit 7c3edbbe0452d99192b97c88fdd65fb321e85e76
Author: Matthew Malcomson <matthew.malcomson@arm.com>
Date:   Wed Oct 19 11:57:44 2022 +0100

    Update mt_allocator for CHERI
    
    The implementation basics this allocator are as follows:
      - There are N "bins" for each power-of-two size.  N is determined
        based on the maximum size beyond which we do use `new` directly.
      - Each bin contains a linked list of "chunks" that have been
        allocated.  This list is only used at cleanup-time so that we can
        call `delete` on each one.
      - Each bin then contains a linked-list of "records" that represent
        available allocations of the correct size.
      - When there are no available "records" we generate some more by
        obtaining an `_M_chunk_size` block from `new` and splitting it up
        into `(_M_chunk_size - address metadata) / sizeof(record)` records.
    
    ----
    The existing errors that we see in our testsuite were all to do with
    misalignment of capabilities in our allocations.  This was happening
    when we were attempting to store our linked-list pointers in misaligned
    locations.  There is an `_M_align` configuration member which determines
    the alignment required for a record.  This must also be `>=
    sizeof(_Block_record)` since the alignment also corresponds to the size
    left for each record metadata.
    
    The existing alignment was 8.  We update it to use the larger of 8 and
    `__SIZEOF_POINTER__` where possible given that the size of a
    `_Block_record` is a pointer.  This avoids all alignment problems that
    we see in the testcase.
    
    Considering if extra padding and alignment is necessary for large
    allocations, we see that the maximum allocation size before falling back
    to `new` is configurable.  However, as the comments describe this has a
    maximum of 32768.  It happens that the alignment requirement to allow
    CHERI to precisely bound an allocation of 32768 bytes is 16, which is
    the same alignment requirement we already have in order to store
    capabilities in the metadata.  Hence we do not have to worry about
    padding and alignment for CHERI bounds.
    
    ----
    Given that the metadata recorded in these allocations contains a pointer
    that would give the user more permissions (especially around permissions
    to access other objects) than are necessary, we zero out this metadata
    area before returning it to the user.  This information does not need to
    be maintained over the period that the user has access to it.
    
    ----
    Restricting the bounds of returned allocations is more tricky.  The act
    of applying bounds is relatively simple -- we have to call the
    appropriate bounding function everywhere that a "record" is made.  The
    problem occurs during deallocation when the allocation given out must be
    reclaimed by adding metadata to the space.  In mt_alloc the metadata is
    stored just before the allocation returned to the user.  This stands in
    contrast to the pool_allocator which stores metadata at the start of the
    allocation.  This means that a basic implementation of narrowing bounds
    would need to include 16 bytes before the allocation returned as well as
    the total allocation returned.
    
    On top of this, since this allocator is a power-of-two allocator (as
    opposed to a multiple-of-alignment sized allocator like pool_allocator)
    the space at the end of an allocation that would need to be accessible
    in order for any reclaimed allocations to have bounds spanning the
    entire range they need may be quite large.  E.g. an allocation of 8193
    bytes would be satisfied from the free list of blocks of 16384 (2^14)
    bytes.  Without any mechanism to widen the bounds of a deallocated
    pointer we would have to give the user a capability with bounds spanning
    the entire 16384 bytes.
    
    This implies that the trade-off in forgoing tight bounds in order to not
    change the implementation of this allocator is much more dramatic than
    the corresponding trade-off in pool_allocator.  However the decision is
    still in much the same vein -- tight-bounds requires some extra data
    structure to maintain extra permissions for use in increasing bounds of
    pointers that are being deallocated, while lax bounds miss more
    out-of-bounds accesses but still do not provide access to any other
    objects that the user did not have access to before (only provides
    access to padding at the end of an allocation).
    
    Based on the fact that this allocator keeps a record of allocations that
    it has made, these allocations could be used to widen the bounds of any
    block returned to the allocator by the user.  However this would require
    either a new data structure to look these things up (e.g. hash table) or
    a linked-list traverse on the existing data structure.
    
    Given this particular allocator does not seem to be used in many places
    (a debian code search for `__mt_alloc` only found few packages outside
    of compilers), we have decided to forego adjusting this particular
    allocator to have the tightest bounds available.

Diff:
---
 libstdc++-v3/include/ext/mt_allocator.h            |  9 ++++--
 libstdc++-v3/src/c++98/mt_allocator.cc             | 15 +++++++++-
 .../ext/mt_allocator/check_read_end_of_bounds.cc   | 30 +++++++++++++++++++
 .../ext/mt_allocator/check_read_out_of_bounds.cc   | 35 ++++++++++++++++++++++
 .../ext/mt_allocator/check_reallocate_and_read.cc  | 33 ++++++++++++++++++++
 5 files changed, 118 insertions(+), 4 deletions(-)

diff --git a/libstdc++-v3/include/ext/mt_allocator.h b/libstdc++-v3/include/ext/mt_allocator.h
index 0857390e665..36067831e12 100644
--- a/libstdc++-v3/include/ext/mt_allocator.h
+++ b/libstdc++-v3/include/ext/mt_allocator.h
@@ -1,6 +1,6 @@
 // MT-optimized allocator -*- C++ -*-
 
-// Copyright (C) 2003-2020 Free Software Foundation, Inc.
+// Copyright (C) 2003-2022 Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
@@ -58,9 +58,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct _Tune
     {
       // Compile time constants for the default _Tune values.
-      enum { _S_align = 8 };
+      enum { _S_align = sizeof (void*) > 8 ? sizeof (void*) : 8 };
+      enum { _S_min_bin = sizeof (void*) > 8 ? sizeof (void*) : 8 };
       enum { _S_max_bytes = 128 };
-      enum { _S_min_bin = 8 };
       enum { _S_chunk_size = 4096 - 4 * sizeof(void*) };
       enum { _S_max_threads = 4096 };
       enum { _S_freelist_headroom = 10 };
@@ -729,6 +729,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  __bin._M_first[__thread_id] = __block->_M_next;
 	  
 	  __pool._M_adjust_freelist(__bin, __block, __thread_id);
+#ifdef __CHERI_PURE_CAPABILITY__
+	  __block->_M_next = (_Block_record *)0;
+#endif
 	  __c = reinterpret_cast<char*>(__block) + __pool._M_get_align();
 	}
       else
diff --git a/libstdc++-v3/src/c++98/mt_allocator.cc b/libstdc++-v3/src/c++98/mt_allocator.cc
index 5741d8e73a2..a24c6e12c69 100644
--- a/libstdc++-v3/src/c++98/mt_allocator.cc
+++ b/libstdc++-v3/src/c++98/mt_allocator.cc
@@ -1,6 +1,6 @@
 // Allocator details.
 
-// Copyright (C) 2004-2020 Free Software Foundation, Inc.
+// Copyright (C) 2004-2022 Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
@@ -36,6 +36,13 @@
 // uintptr_t.
 #include <stdint.h>
 
+#ifdef __CHERI_PURE_CAPABILITY__
+#define maybe_set_cheri_bounds(p, n) \
+	__builtin_cheri_bounds_set_exact ((p), (n))
+#else
+#define maybe_set_cheri_bounds(p, n) (p)
+#endif
+
 namespace
 {
 #ifdef __GTHREADS
@@ -151,12 +158,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     char* __c = static_cast<char*>(__v) + sizeof(_Block_address);
     _Block_record* __block = reinterpret_cast<_Block_record*>(__c);
+    __block = maybe_set_cheri_bounds(__block, __bin_size);
     __bin._M_first[__thread_id] = __block;
     while (--__block_count > 0)
       {
 	__c += __bin_size;
 	__block->_M_next = reinterpret_cast<_Block_record*>(__c);
 	__block = __block->_M_next;
+	__block = maybe_set_cheri_bounds(__block, __bin_size);
       }
     __block->_M_next = 0;
 
@@ -396,12 +405,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    char* __c = static_cast<char*>(__v) + sizeof(_Block_address);
 	    __block = reinterpret_cast<_Block_record*>(__c);
 	    __bin._M_free[__thread_id] = __block_count;
+	    __block = maybe_set_cheri_bounds(__block, __bin_size);
 	    __bin._M_first[__thread_id] = __block;
 	    while (--__block_count > 0)
 	      {
 		__c += __bin_size;
 		__block->_M_next = reinterpret_cast<_Block_record*>(__c);
 		__block = __block->_M_next;
+		__block = maybe_set_cheri_bounds(__block, __bin_size);
 	      }
 	    __block->_M_next = 0;
 	  }
@@ -440,12 +451,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 	char* __c = static_cast<char*>(__v) + sizeof(_Block_address);
 	__block = reinterpret_cast<_Block_record*>(__c);
+	__block = maybe_set_cheri_bounds(__block, __bin_size);
  	__bin._M_first[0] = __block;
 	while (--__block_count > 0)
 	  {
 	    __c += __bin_size;
 	    __block->_M_next = reinterpret_cast<_Block_record*>(__c);
 	    __block = __block->_M_next;
+	    __block = maybe_set_cheri_bounds(__block, __bin_size);
 	  }
 	__block->_M_next = 0;
       }
diff --git a/libstdc++-v3/testsuite/ext/mt_allocator/check_read_end_of_bounds.cc b/libstdc++-v3/testsuite/ext/mt_allocator/check_read_end_of_bounds.cc
new file mode 100644
index 00000000000..a269dbaa724
--- /dev/null
+++ b/libstdc++-v3/testsuite/ext/mt_allocator/check_read_end_of_bounds.cc
@@ -0,0 +1,30 @@
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 20.4.1.1 allocator members
+
+#include <cstdlib>
+#include <ext/mt_allocator.h>
+#include <replacement_memory_operators.h>
+
+int main()
+{
+  // Uses new, but delete only sometimes.
+  typedef __gnu_cxx::__mt_alloc<unsigned int> allocator_type;
+  __gnu_test::check_read_end_of_bounds<allocator_type, false>();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/ext/mt_allocator/check_read_out_of_bounds.cc b/libstdc++-v3/testsuite/ext/mt_allocator/check_read_out_of_bounds.cc
new file mode 100644
index 00000000000..874cae72138
--- /dev/null
+++ b/libstdc++-v3/testsuite/ext/mt_allocator/check_read_out_of_bounds.cc
@@ -0,0 +1,35 @@
+// { dg-shouldfail-purecap "out of bounds" }
+
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 20.4.1.1 allocator members
+
+#include <cstdlib>
+#include <ext/mt_allocator.h>
+#include <replacement_memory_operators.h>
+
+int main()
+{
+  typedef __gnu_cxx::__mt_alloc<unsigned int> allocator_type;
+  // Ensure that the test will return `0` if all operations are executed, and
+  // the compiler does not optimise away the reads at higher optimisation
+  // levels.
+  unsigned int x = __gnu_test::check_read_out_of_bounds<allocator_type, false>();
+  asm volatile ("" : "=r" (x) : "r" (x) : );
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/ext/mt_allocator/check_reallocate_and_read.cc b/libstdc++-v3/testsuite/ext/mt_allocator/check_reallocate_and_read.cc
new file mode 100644
index 00000000000..2bddabfdd17
--- /dev/null
+++ b/libstdc++-v3/testsuite/ext/mt_allocator/check_reallocate_and_read.cc
@@ -0,0 +1,33 @@
+// Copyright (C) 2022 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// 20.4.1.1 allocator members
+
+#include <cstdlib>
+#include <ext/mt_allocator.h>
+#include <replacement_memory_operators.h>
+
+int main()
+{
+  typedef __gnu_cxx::__mt_alloc<unsigned int> allocator_type;
+  // Ensure that the test will return `0` if all operations are executed, and
+  // the compiler does not optimise away the reads at higher optimisation
+  // levels.
+  unsigned int x = __gnu_test::check_reallocate_and_read<allocator_type, true>();
+  asm volatile ("" : "=r" (x) : "r" (x) : );
+  return 0;
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-19 11:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-19 11:01 [gcc(refs/vendors/ARM/heads/morello)] Update mt_allocator for CHERI Matthew Malcomson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).