https://gcc.gnu.org/g:c93eb81c9edbf9bec6a12a2125037ec29d1cd4d2

commit r15-5613-gc93eb81c9edbf9bec6a12a2125037ec29d1cd4d2
Author: Lewis Hyatt <lhy...@gmail.com>
Date:   Mon Oct 28 17:57:41 2024 -0400

    libcpp: Fix potential unaligned access in cpp_buffer
    
    libcpp makes use of the cpp_buffer pfile->a_buff to store things while it is
    handling macros. It uses it to store pointers (cpp_hashnode*, for macro
    arguments) and cpp_macro objects. This works fine because a cpp_hashnode*
    and a cpp_macro have the same alignment requirement on either 32-bit or
    64-bit systems (namely, the same alignment as a pointer.)
    
    When 64-bit location_t is enabled on a 32-bit sytem, the alignment
    requirement may cease to be the same, because the alignment requirement of a
    cpp_macro object changes to that of a uint64_t, which be larger than that of
    a pointer. It's not the case for x86 32-bit, but for example, on sparc, a
    pointer has 4-byte alignment while a uint64_t has 8. In that case,
    intermixing the two within the same cpp_buffer leads to a misaligned
    access. The code path that triggers this is the one in _cpp_commit_buff in
    which a hash table with its own allocator (i.e. ggc) is not being used, so
    it doesn't happen within the compiler itself, but it happens in the other
    libcpp clients, such as genmatch.
    
    Fix that up by ensuring _cpp_commit_buff commits a fully aligned chunk of 
the
    buffer, so it's ready for anything it may be used for next.
    
    Also modify CPP_ALIGN so that it guarantees to return an alignment at least
    the size of location_t. Currently it returns the max of a pointer and a
    double. I am not aware of any platform where a double may have smaller
    alignment than a uint64_t, but it does not hurt to add location_t here to be
    sure.
    
    libcpp/ChangeLog:
    
            * lex.cc (_cpp_commit_buff): Make sure that the buffer is properly
            aligned for the next allocation.
            * internal.h (struct dummy): Make sure alignment is large enough for
            a location_t, just in case.

Diff:
---
 libcpp/internal.h |  1 +
 libcpp/lex.cc     | 10 ++++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/libcpp/internal.h b/libcpp/internal.h
index ad4e5909cfae..5f4d9cb1fa73 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -85,6 +85,7 @@ struct dummy
   {
     double d;
     int *p;
+    location_t l;
   } u;
 };
 
diff --git a/libcpp/lex.cc b/libcpp/lex.cc
index 849447eb4d79..858970b5d171 100644
--- a/libcpp/lex.cc
+++ b/libcpp/lex.cc
@@ -4997,7 +4997,8 @@ _cpp_aligned_alloc (cpp_reader *pfile, size_t len)
 void *
 _cpp_commit_buff (cpp_reader *pfile, size_t size)
 {
-  void *ptr = BUFF_FRONT (pfile->a_buff);
+  const auto buff = pfile->a_buff;
+  void *ptr = BUFF_FRONT (buff);
 
   if (pfile->hash_table->alloc_subobject)
     {
@@ -5006,7 +5007,12 @@ _cpp_commit_buff (cpp_reader *pfile, size_t size)
       ptr = copy;
     }
   else
-    BUFF_FRONT (pfile->a_buff) += size;
+    {
+      BUFF_FRONT (buff) += size;
+      /* Make sure the remaining space is maximally aligned for whatever this
+        buffer holds next.  */
+      BUFF_FRONT (buff) += BUFF_ROOM (buff) % DEFAULT_ALIGNMENT;
+    }
 
   return ptr;
 }

Reply via email to