diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 30253d794..f1f348e13 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -334,7 +334,7 @@ jobs: - script: | set -eo pipefail ninja clangformat - git diff --exit-code $(Build.SourceVersion) + git diff --exit-code workingDirectory: build failOnStderr: true diff --git a/src/aal/aal.h b/src/aal/aal.h index 55b14778a..e939f78fd 100644 --- a/src/aal/aal.h +++ b/src/aal/aal.h @@ -36,6 +36,12 @@ namespace snmalloc * This architecture cannot access cpu cycles counters. */ NoCpuCycleCounters = (1 << 1), + /** + * This architecture enforces strict pointer provenance; we bound the + * pointers given out on malloc() and friends and must, therefore retain + * internal high-privilege pointers for recycling memory on free(). + */ + StrictProvenance = (1 << 2), }; /** diff --git a/src/ds/cdllist.h b/src/ds/cdllist.h index 1bc39aad4..7fe479576 100644 --- a/src/ds/cdllist.h +++ b/src/ds/cdllist.h @@ -7,13 +7,8 @@ namespace snmalloc { - /** - * Special class for cyclic doubly linked non-empty linked list - * - * This code assumes there is always one element in the list. The client - * must ensure there is a sentinal element. - */ - class CDLLNode + template + class CDLLNodeBase { /** * to_next is used to handle a zero initialised data structure. @@ -22,30 +17,78 @@ namespace snmalloc */ ptrdiff_t to_next = 0; - // TODO: CHERI will need a real pointer too - // CDLLNode* next = nullptr; - CDLLNode* prev = nullptr; - - void set_next(CDLLNode* c) + protected: + void set_next(T* c) { - // TODO: CHERI will need a real pointer too - // next = c; to_next = pointer_diff_signed(this, c); } public: + SNMALLOC_FAST_PATH bool is_empty() + { + return to_next == 0; + } + + SNMALLOC_FAST_PATH T* get_next() + { + return pointer_offset_signed(static_cast(this), to_next); + } + }; + + template + class CDLLNodeBaseNext + { /** - * Single element cyclic list. This is the empty case. + * Like to_next in the pointer-less case, this version still works with + * zero-initialized data structure. To make `is_empty` work in this case, + * next is set to `nullptr` rather than `this` when the list is empty. + * */ - CDLLNode() + + T* next = nullptr; + + protected: + void set_next(T* c) { - set_next(this); - prev = this; + next = (c == static_cast(this)) ? nullptr : c; } + public: SNMALLOC_FAST_PATH bool is_empty() { - return to_next == 0; + return next == nullptr; + } + + SNMALLOC_FAST_PATH T* get_next() + { + return next == nullptr ? static_cast(this) : next; + } + }; + + template + using CDLLNodeParent = std::conditional_t< + aal_supports, + CDLLNodeBaseNext, + CDLLNodeBase>; + + /** + * Special class for cyclic doubly linked non-empty linked list + * + * This code assumes there is always one element in the list. The client + * must ensure there is a sentinal element. + */ + class CDLLNode : public CDLLNodeParent + { + CDLLNode* prev = nullptr; + + public: + /** + * Single element cyclic list. This is the empty case. + */ + CDLLNode() + { + this->set_next(this); + prev = this; } /** @@ -53,7 +96,7 @@ namespace snmalloc */ SNMALLOC_FAST_PATH void remove() { - SNMALLOC_ASSERT(!is_empty()); + SNMALLOC_ASSERT(!this->is_empty()); debug_check(); get_next()->prev = prev; prev->set_next(get_next()); @@ -67,13 +110,6 @@ namespace snmalloc #endif } - SNMALLOC_FAST_PATH CDLLNode* get_next() - { - // TODO: CHERI will require a real pointer - // return next; - return pointer_offset_signed(this, to_next); - } - SNMALLOC_FAST_PATH CDLLNode* get_prev() { return prev;