Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHERI, take 2021.10 #402

Merged
merged 21 commits into from
Oct 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,28 @@ jobs:
system-processor: arm
triple: arm-linux-gnueabihf
rtld: ld-linux-armhf.so.3
ld-flavour: lld
- name: arm64
system-processor: aarch64
triple: aarch64-linux-gnu
rtld: ld-linux-aarch64.so.1
ld-flavour: lld
- name: ppc64el
system-processor: powerpc64le
triple: powerpc64le-linux-gnu
rtld: ld64.so.2
ld-flavour: lld
- name: riscv64
system-processor: riscv64
triple: riscv64-linux-gnu
rtld: ld-linux-riscv64-lp64d.so.1
extra-packages: binutils-riscv64-linux-gnu
ld-flavour: bfd
ld: /usr/bin/riscv64-linux-gnu-ld.bfd
# Don't abort runners if a single one fails
fail-fast: false
runs-on: ubuntu-latest
name: Cross-build for ${{ matrix.arch.triple }}
name: ${{matrix.build-type}} cross-build for ${{ matrix.arch.triple }}
steps:
- uses: actions/checkout@v2
- name: Install cross-compile toolchain and QEMU
Expand All @@ -141,6 +151,7 @@ jobs:
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main"
sudo apt update
sudo apt install libstdc++-9-dev-${{ matrix.arch.name }}-cross qemu-user ninja-build clang-13 lld-13
sudo apt install ${{matrix.arch.extra-packages}}
# The default PowerPC qemu configuration uses the wrong page size.
# Wrap it in a script that fixes this.
sudo update-binfmts --disable qemu-ppc64le
Expand All @@ -161,6 +172,8 @@ jobs:
-DSNMALLOC_QEMU_WORKAROUND=ON
-DSNMALLOC_STATIC_LIBRARY=OFF
-DCMAKE_TOOLCHAIN_FILE=ci/Toolchain.cmake
-DSNMALLOC_LINKER=${{matrix.arch.ld}}
-DSNMALLOC_LINKER_FLAVOUR=${{matrix.arch.ld-flavour}}
- name: Build
working-directory: ${{github.workspace}}/build
run: NINJA_STATUS="%p [%f:%s/%t] %o/s, %es" ninja
Expand Down
19 changes: 15 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,21 @@ if(NOT SNMALLOC_HEADER_ONLY_LIBRARY)
set(${result} ${dirlist} PARENT_SCOPE)
endfunction()

set(CMAKE_REQUIRED_LINK_OPTIONS -fuse-ld=lld)
check_cxx_source_compiles("int main() { return 1; }" LLD_WORKS)
if (LLD_WORKS)
message(STATUS "Using LLD to link snmalloc shims")
if(NOT (DEFINED SNMALLOC_LINKER_FLAVOUR) OR ("${SNMALLOC_LINKER_FLAVOUR}" MATCHES "^$"))
# Linker not specified externally; probe to see if we can make lld work
set(CMAKE_REQUIRED_LINK_OPTIONS -fuse-ld=lld)
check_cxx_source_compiles("int main() { return 1; }" LLD_WORKS)
if (LLD_WORKS)
message(STATUS "Using LLD to link snmalloc shims")
endif()
elseif(SNMALLOC_LINKER_FLAVOUR STREQUAL "lld")
# Linker specified externally to be lld; assume it works and that the flags
# have also been set for us
set(LLD_WORKS TRUE)
else()
# Linker specified externally as something other than lld; presume it
# doesn't work and don't add its flags, below
set(LLD_WORKS FALSE)
endif()

function(add_shim name type)
Expand Down
6 changes: 5 additions & 1 deletion ci/Toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ set(CMAKE_C_COMPILER clang-13)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++-13)
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(CROSS_LINKER_FLAGS "-fuse-ld=lld -Wl,--dynamic-linker=/usr/${triple}/lib/$ENV{RTLD_NAME},-rpath,/usr/${triple}/lib")

set(CROSS_LINKER_FLAGS "-fuse-ld=${SNMALLOC_LINKER_FLAVOUR} -Wl,--dynamic-linker=/usr/${triple}/lib/$ENV{RTLD_NAME},-rpath,/usr/${triple}/lib")
if((DEFINED SNMALLOC_LINKER) AND NOT ("${SNMALLOC_LINKER}" MATCHES "^$"))
string(APPEND CROSS_LINKER_FLAGS " --ld-path=${SNMALLOC_LINKER}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires Clang 12, but I assume the variable will only be set when that requirement is met?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea; despite the generic name, AFAICT this file is used only in cross-build jobs, where we're already requiring clang-13.

endif()
set(CMAKE_EXE_LINKER_FLAGS ${CROSS_LINKER_FLAGS})
set(CMAKE_SHARED_LINKER_FLAGS ${CROSS_LINKER_FLAGS})
set(CMAKE_MODULE_LINKER_FLAGS ${CROSS_LINKER_FLAGS})
40 changes: 21 additions & 19 deletions src/aal/aal.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
# define PLATFORM_IS_SPARC
#endif

#if defined(__riscv)
# define PLATFORM_IS_RISCV
#endif

namespace snmalloc
{
/**
Expand Down Expand Up @@ -195,27 +199,15 @@ namespace snmalloc
static SNMALLOC_FAST_PATH CapPtr<T, BOut>
capptr_bound(CapPtr<U, BIn> a, size_t size) noexcept
{
// Impose constraints on bounds annotations.
static_assert(BIn::spatial >= capptr::dimension::Spatial::Chunk);
static_assert(capptr_is_spatial_refinement<BIn, BOut>());
static_assert(
BIn::spatial > capptr::dimension::Spatial::Alloc,
"Refusing to re-bound Spatial::Alloc CapPtr");
static_assert(
capptr::is_spatial_refinement<BIn, BOut>(),
"capptr_bound must preserve non-spatial CapPtr dimensions");

UNUSED(size);
return CapPtr<T, BOut>(a.template as_static<T>().unsafe_capptr);
}

/**
* For architectures which do not enforce StrictProvenance, there's nothing
* to be done, so just return the pointer unmodified with new annotation.
*/
template<
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) BOut,
SNMALLOC_CONCEPT(capptr::ConceptBound) BIn>
static SNMALLOC_FAST_PATH CapPtr<T, BOut>
capptr_rebound(CapPtr<void, BOut> a, CapPtr<T, BIn> r) noexcept
{
UNUSED(a);
return CapPtr<T, BOut>(r.unsafe_capptr);
return CapPtr<T, BOut>(a.template as_static<T>().unsafe_ptr());
}
};
} // namespace snmalloc
Expand All @@ -230,11 +222,21 @@ namespace snmalloc
# include "aal_powerpc.h"
#elif defined(PLATFORM_IS_SPARC)
# include "aal_sparc.h"
#elif defined(PLATFORM_IS_RISCV)
# include "aal_riscv.h"
#endif

#if defined(__CHERI_PURE_CAPABILITY__)
# include "aal_cheri.h"
#endif

namespace snmalloc
{
#if defined(__CHERI_PURE_CAPABILITY__)
using Aal = AAL_Generic<AAL_CHERI<AAL_Arch>>;
#else
using Aal = AAL_Generic<AAL_NoStrictProvenance<AAL_Arch>>;
#endif

template<AalFeatures F, SNMALLOC_CONCEPT(ConceptAAL) AAL = Aal>
constexpr static bool aal_supports = (AAL::aal_features & F) == F;
Expand Down
52 changes: 52 additions & 0 deletions src/aal/aal_cheri.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once

#include "../ds/defines.h"

#include <stddef.h>

namespace snmalloc
{
/**
* A mixin AAL that applies CHERI to a `Base` architecture. Gives
* architectural teeth to the capptr_bound primitive.
*/
template<typename Base>
class AAL_CHERI : public Base
nwf marked this conversation as resolved.
Show resolved Hide resolved
{
public:
/**
* CHERI pointers are not integers and come with strict provenance
* requirements.
*/
static constexpr uint64_t aal_features =
(Base::aal_features & ~IntegerPointers) | StrictProvenance;

/**
* On CHERI-aware compilers, ptraddr_t is an integral type that is wide
* enough to hold any address that may be contained within a memory
* capability. It does not carry provenance: it is not a capability, but
* merely an address.
*/
typedef ptraddr_t address_t;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks as if it comes from stddef.h, which isn't included here. Given that the base AAL tells you the address width, it might be a better idea to use that rather than depend on a non-standard declaration from a standard header - the glibc version will undoubtedly hide this behind some feature-check macro and so our headers will be broken by random build-system flags.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct place for ptraddr_t is <stddef.h>, CheriBSD also provides it in sys/sys/_stdint.h and in sys/sys/stddef.h. I take the view that any CHERI-aware <stddef.h> must provide ptraddr_t when building in __CHERI_PURE_CAPABILITY__ mode, and I would hope that the GLibc port does that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm include-ing <stddef.h> now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixed-with integer type would also be slightly wrong, as it's not necessarily the same type as ptraddr_t. On Darwin, uint64_t is unsigned long long but size_t is unsigned long, so ptraddr_t would likely also be unsigned long.


template<
typename T,
SNMALLOC_CONCEPT(capptr::ConceptBound) BOut,
SNMALLOC_CONCEPT(capptr::ConceptBound) BIn,
typename U = T>
static SNMALLOC_FAST_PATH CapPtr<T, BOut>
capptr_bound(CapPtr<U, BIn> a, size_t size) noexcept
{
static_assert(
BIn::spatial > capptr::dimension::Spatial::Alloc,
"Refusing to re-bound Spatial::Alloc CapPtr");
static_assert(
capptr::is_spatial_refinement<BIn, BOut>(),
"capptr_bound must preserve non-spatial CapPtr dimensions");
SNMALLOC_ASSERT(__builtin_cheri_tag_get(a.unsafe_ptr()));

void* pb = __builtin_cheri_bounds_set_exact(a.unsafe_ptr(), size);
return CapPtr<T, BOut>(static_cast<T*>(pb));
}
};
} // namespace snmalloc
6 changes: 0 additions & 6 deletions src/aal/aal_concept.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ namespace snmalloc
{ AAL::template capptr_bound<void, capptr::bounds::Chunk>(auth, sz) }
noexcept
-> ConceptSame<capptr::Chunk<void>>;

/**
* Construct a copy of auth with its target set to that of ret.
*/
{ AAL::capptr_rebound(auth, ret) } noexcept
-> ConceptSame<capptr::Chunk<void>>;
};

template<typename AAL>
Expand Down
1 change: 1 addition & 0 deletions src/aal/aal_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ namespace snmalloc
X86,
X86_SGX,
Sparc,
RISCV
};
} // namespace snmalloc
54 changes: 54 additions & 0 deletions src/aal/aal_riscv.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once
nwf marked this conversation as resolved.
Show resolved Hide resolved

#if __riscv_xlen == 64
# define SNMALLOC_VA_BITS_64
#elif __riscv_xlen == 32
# define SNMALLOC_VA_BITS_32
#endif

namespace snmalloc
{
/**
* RISC-V architecture layer, phrased as generically as possible. Specific
* implementations may need to adjust some of these.
*/
class AAL_RISCV
{
public:
static constexpr uint64_t aal_features = IntegerPointers;

static constexpr size_t smallest_page_size = 0x1000;

static constexpr AalName aal_name = RISCV;

static void inline pause()
{
/*
* The "Zihintpause" extension claims to be the right thing to do here,
* and it is expected to be used in analogous places, e.g., Linux's
* cpu_relax(), but...
*
* its specification is somewhat unusual, in that it talks about the rate
* at which a HART's instructions retire rather than the rate at which
* they are dispatched (Intel's PAUSE instruction explicitly promises
* that it "de-pipelines" the spin-wait loop, for example) or anything
* about memory semantics (Intel's PAUSE docs talk about a possible
* memory order violation and pipeline flush upon loop exit).
*
* we don't yet have examples of what implementations have done.
*
* it's not yet understood by C frontends or assembler, meaning we'd have
* to spell it out by hand, as
* __asm__ volatile(".byte 0xF; .byte 0x0; .byte 0x0; .byte 0x1");
*
* All told, we just leave this function empty for the moment. The good
* news is that, if and when we do add a PAUSE, the instruction is encoded
* by stealing some dead space of the FENCE instruction and so should be
* available everywhere even if it doesn't do anything on a particular
* microarchitecture.
*/
}
};

using AAL_Arch = AAL_RISCV;
}
8 changes: 3 additions & 5 deletions src/backend/address_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,10 @@ namespace snmalloc
SNMALLOC_ASSERT(size >= sizeof(void*));

/*
* For sufficiently large allocations with platforms that support
* aligned allocations and architectures that don't require
* StrictProvenance, try asking the platform first.
* For sufficiently large allocations with platforms that support aligned
* allocations, try asking the platform directly.
*/
if constexpr (
pal_supports<AlignedAllocation, PAL> && !aal_supports<StrictProvenance>)
if constexpr (pal_supports<AlignedAllocation, PAL>)
{
if (size >= PAL::minimum_alloc_size)
{
Expand Down
17 changes: 15 additions & 2 deletions src/backend/address_space_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ namespace snmalloc
*
* It cannot unreserve memory, so this does not require the
* usual complexity of a buddy allocator.
*
* TODO: This manages pieces of memory smaller than (1U << MIN_CHUNK_BITS) to
* source Metaslab and LocalCache objects. On CHERI, where ASLR and guard
* pages are not needed, it may be worth switching to a design where we
* bootstrap allocators with at least two embedded Metaslab-s that can be used
* to construct slabs for LocalCache and, of course, additional Metaslab
* objects. That would let us stop splitting memory below that threshold
* here, and may reduce address space fragmentation or address space committed
* to Metaslab objects in perpetuity; it could also make {set,get}_next less
* scary.
*/
class AddressSpaceManagerCore
{
Expand Down Expand Up @@ -77,13 +87,16 @@ namespace snmalloc
{
if (align_bits >= MIN_CHUNK_BITS)
{
// The pagemap stores MetaEntrys, abuse the metaslab field to be the
// The pagemap stores `MetaEntry`s; abuse the metaslab field to be the
// next block in the stack of blocks.
//
// The pagemap entries here have nullptr (i.e., fake_large_remote) as
// their remote, and so other accesses to the pagemap (by
// external_pointer, for example) will not attempt to follow this
// "Metaslab" pointer.
//
// dealloc() can reject attempts to free such MetaEntry-s due to the
// zero sizeclass.
MetaEntry t(reinterpret_cast<Metaslab*>(next.unsafe_ptr()), nullptr, 0);
Pagemap::set_metaentry(local_state, address_cast(base), 1, t);
return;
Expand Down Expand Up @@ -112,7 +125,7 @@ namespace snmalloc
const MetaEntry& t = Pagemap::template get_metaentry<false>(
local_state, address_cast(base));
return capptr::Chunk<FreeChunk>(
reinterpret_cast<FreeChunk*>(t.get_metaslab()));
reinterpret_cast<FreeChunk*>(t.get_metaslab_no_remote()));
}

return base->next;
Expand Down
2 changes: 2 additions & 0 deletions src/backend/backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ namespace snmalloc
return {p, nullptr};
}

meta->meta_common.chunk = p;

MetaEntry t(meta, remote, sizeclass);
Pagemap::set_metaentry(local_state, address_cast(p), size, t);
return {p, meta};
Expand Down
2 changes: 1 addition & 1 deletion src/backend/pagemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ namespace snmalloc
void set(address_t p, T t)
{
#ifdef SNMALLOC_TRACING
std::cout << "Pagemap.Set " << (void*)p << std::endl;
std::cout << "Pagemap.Set " << (void*)(uintptr_t)p << std::endl;
#endif
if constexpr (has_bounds)
{
Expand Down
7 changes: 2 additions & 5 deletions src/ds/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,8 @@
namespace snmalloc
{
/**
* The type used for an address. Currently, all addresses are assumed to be
* provenance-carrying values and so it is possible to cast back from the
* result of arithmetic on an address_t. Eventually, this will want to be
* separated into two types, one for raw addresses and one for addresses that
* can be cast back to pointers.
* The type used for an address. On CHERI, this is not a provenance-carrying
* value and so cannot be converted back to a pointer.
*/
using address_t = Aal::address_t;

Expand Down
Loading