ivmai / bdwgc

The Boehm-Demers-Weiser conservative C/C++ Garbage Collector (bdwgc, also known as bdw-gc, boehm-gc, libgc)
https://www.hboehm.info/gc/
Other
3.01k stars 408 forks source link

Support CHERI extension #627

Open ivmai opened 8 months ago

ivmai commented 8 months ago

The purpose of this is not only support the protected mode but also better test bdwgc.

The patches in https://github.com/capablevms/bdwgc (master) should be useful.

Related issue: #461

ivmai commented 3 months ago

Current status: I've done a lot of refactoring to simplify porting to CHERI, and backported some changes from https://github.com/capablevms/bdwgc/ (see c29365b). Still many CHERI-specific changes to be backported.

ivmai commented 3 months ago

And below is the list of code places worth attention during enabling of CHERI in master:

ivmai commented 3 months ago

A number of changes are not ported yet (as of commit be1e3a3), here's a diff:

diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index c93710be..18d7a321 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -2103,6 +2132,26 @@ GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t, void *),
           LOAD_TAGGED_VALUE(v, tag, p); \
           if (tag != 0) continue; \
         }
+#elif defined(CHERI_PURECAP)
+  /* Load value, check tag and permissions of the target memory.   */
+# define LOAD_WORD_OR_CONTINUE(v, p) \
+        { \
+            v = *(void **)(p);                                               \
+            if (!cheri_tag_get(v)) continue;                                 \
+            size_t in_bounds = (v >= (word)cheri_base_get(v))                \
+                                && (v < (word)cheri_base_get(v)              \
+                                        + (word)cheri_length_get(v));        \
+            size_t has_rwx = cheri_perms_get(v) & (CHERI_PERM_LOAD           \
+                                                   | CHERI_PERM_STORE        \
+                                                   | CHERI_PERM_EXECUTE);    \
+            if (!in_bounds || !has_rwx) continue;                            \
+        }
 #else
 # define LOAD_WORD_OR_CONTINUE(v, p) (void)(v = *(word *)(p))
 #endif /* !E2K */
diff --git a/mark.c b/mark.c
index ae8f314f..69133827 100644
--- a/mark.c
+++ b/mark.c
@@ -548,7 +548,12 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
 {
   signed_word credit = HBLKSIZE;  /* Remaining credit for marking work. */
   ptr_t current_p;      /* Pointer to current candidate ptr.            */
+# ifdef CHERI_PURECAP
+  void **current;       /* Candidate pointer.                           */
+  word has_rwx;         /* permissions on capability                    */
+# else
   word current;         /* Candidate pointer.                           */
+# endif
   ptr_t limit = 0;      /* (Incl) limit of current candidate range.     */
   word descr;
   ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr;
@@ -728,16 +733,17 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
 #     define PREF_DIST 4

 #     if !defined(SMALL_CONFIG) && !defined(USE_PTR_HWTAG)
+#       ifndef CHERI_PURECAP
           word deferred;

@@ -763,6 +769,50 @@ GC_INNER mse * GC_mark_from(mse *mark_stack_top, mse *mark_stack,
             }
             if ((word)current_p > (word)limit) goto next_object;
           }
+#       else //?? it is better to avoid code duplication
+          void **deferred;
+  
+          /* Check each pointer for validity before dereferencing         */
+          /* to prevent capability exceptions.                            */
+          /* Utilise the pointer meta-data to speed-up the loop.          */
+          /* If the loop is below the pointer bounds, skip the rest of    */
+          /* marking for that chunk.                                      */
+          /* If the *limit* capability restricts us to reading fewer than */
+          /* sizeof(ptr_t),                                               */
+          /*  a. there can't possibly be a pointer at limit's pointer     */
+          /*  b. reading at that location will raise a capability         */
+          /*     exception                                                */
+          if (ADDR(limit) + sizeof(ptr_t) - 1
+                >= (cheri_base_get(limit) + cheri_length_get(limit))) {
+            /* Decrement limit so that it's within current_p's bounds */
+            limit = cheri_address_set( current_p, ((cheri_base_get(limit)
+                                      + cheri_length_get(limit) - sizeof(ptr_t))
+                                      & ~(sizeof(ptr_t)-1)));
+            if ((word)current_p > (word)limit) goto next_object;
+          }
+          for(;;) {
+            GC_ASSERT((word)limit >= (word)current_p);
+            if (ADDR(limit) < cheri_base_get(limit)) goto next_object;
+
+            has_rwx = cheri_perms_get(limit) & (CHERI_PERM_LOAD
+                                                | CHERI_PERM_STORE
+                                                | CHERI_PERM_EXECUTE);
+            if ((cheri_tag_get(limit) == 0) || !has_rwx) {
+              limit -= ALIGNMENT;
+            } else {
+              deferred = *(void **)limit;
+              FIXUP_POINTER(deferred);
+              limit -= ALIGNMENT;
+              has_rwx = cheri_perms_get(deferred) & (CHERI_PERM_LOAD
+                                                     | CHERI_PERM_STORE
+                                                     | CHERI_PERM_EXECUTE);
+              if (((cheri_tag_get(deferred) != 0) && has_rwx)
+                    && (deferred >= (word)least_ha && deferred < (word)greatest_ha))
+                break;
+            }
+            if ((word)current_p > (word)limit) goto next_object;
+          }
+#       endif
 #     endif

       for (; (word)current_p <= (word)limit; current_p += ALIGNMENT) {
@@ -1502,7 +1557,13 @@ GC_API void GC_CALL GC_print_trace(word gc_no)
 GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD
 GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top)
 {
+# ifdef CHERI_PURECAP
+    REGISTER void ** current_p; // it is better to use ptr_t instead of void**
+# else
     REGISTER ptr_t current_p;
+# endif
     REGISTER word *lim;
     REGISTER ptr_t greatest_ha = (ptr_t)GC_greatest_plausible_heap_addr;
     REGISTER ptr_t least_ha = (ptr_t)GC_least_plausible_heap_addr;
@@ -1512,6 +1573,18 @@ GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top)
     if (top == 0) return;

     /* Check all pointers in range and push if they appear to be valid. */
+# ifdef CHERI_PURECAP
+    lim = t - 1;
+    if ((intptr_t)lim >= (cheri_base_get(b) + cheri_length_get(b)))
+      lim = cheri_base_get(b) + cheri_length_get(b) - sizeof(ptr_t);
+
+    for (current_p = b; (word)current_p <= (word)lim; current_p++) {
+      REGISTER void *q = *current_p;
+
+      LOAD_WORD_OR_CONTINUE(q, current_p);
+      GC_PUSH_ONE_STACK(q, current_p);
+    }
+# else
     lim = (word *)(((word)top) & ~(ALIGNMENT-1)) - 1;
     for (current_p = (ptr_t)(((word)bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
          (word)current_p <= (word)lim; current_p += ALIGNMENT) {
@@ -1520,6 +1593,7 @@ GC_API void GC_CALL GC_push_all_eager(void *bottom, void *top)
       LOAD_WORD_OR_CONTINUE(q, current_p);
       GC_PUSH_ONE_STACK(q, current_p);
     }
+# endif
 #   undef GC_greatest_plausible_heap_addr
 #   undef GC_least_plausible_heap_addr
 }
ivmai commented 1 month ago

As of commit e1fb227, bdwgc could be built using:

./configure --disable-dynamic-loading --enable-cplusplus --enable-gc-assertions --enable-werror
make check CFLAGS_EXTRA="-Wno-cheri-capability-misuse -Wno-capability-to-integer-cast"

All tests fail currently, because of missing some patches from https://github.com/capablevms/bdwgc

ivmai commented 1 month ago

Note: some suppressed warnings could be eliminated by modifying GC_CAST_AWAY_CONST_PVOID() and CAST_THRU_UINTPTR() - remove intermediate GC_uintptr_t if __CHERI_PURE_CAPABILITY__.

ivmai commented 3 weeks ago

As of commit 5d619ee, bdwgc could be built using:

./autogen.sh
./configure --enable-cplusplus --enable-gc-assertions --enable-werror
make check

Or, using make -f Makefile.direct check or using cmake. All tests fail currently (as noted above).

ivmai commented 3 weeks ago

As of commit 5d619ee, bdwgc could be built using:

./autogen.sh
./configure --enable-cplusplus --enable-gc-assertions --enable-werror
make check

Or, using make -f Makefile.direct check or using cmake. All tests fail currently (as noted above).

ivmai commented 2 weeks ago

GCJ support is not working because it expects granule to be at least twice bigger than pointer currently. Should be fixed by #652. For now, we set granule size to 2 pointers (32 bytes).