From: John Baldwin Date: Thu, 13 Oct 2022 21:05:51 +0000 (-0700) Subject: git subrepo clone https://github.com/CTSRD-CHERI/cheri-compressed-cap gdb/cheri-compr... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2df9f9bf324b3537e8d31e83deb707990a5b906b;p=thirdparty%2Fbinutils-gdb.git git subrepo clone https://github.com/CTSRD-CHERI/cheri-compressed-cap gdb/cheri-compressed-cap subrepo: subdir: "gdb/cheri-compressed-cap" merged: "6762a19ba9f" upstream: origin: "https://github.com/CTSRD-CHERI/cheri-compressed-cap" branch: "master" commit: "6762a19ba9f" git-subrepo: version: "0.4.3" origin: "???" commit: "???" --- diff --git a/gdb/cheri-compressed-cap/.clang-format b/gdb/cheri-compressed-cap/.clang-format new file mode 100644 index 00000000000..e300f3dff55 --- /dev/null +++ b/gdb/cheri-compressed-cap/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 120 +TabWidth: 8 +UseTab: Never +# Force pointers to the type +DerivePointerAlignment: false +PointerAlignment: Left +AllowShortCaseLabelsOnASingleLine: true +AccessModifierOffset: -4 diff --git a/gdb/cheri-compressed-cap/.editorconfig b/gdb/cheri-compressed-cap/.editorconfig new file mode 100644 index 00000000000..6343526cc35 --- /dev/null +++ b/gdb/cheri-compressed-cap/.editorconfig @@ -0,0 +1,10 @@ +root = true +[*] +end_of_line = lf +charset = utf-8 +indent_size = 4 +indent_style = space +insert_final_newline = true +tab_width = 8 +max_line_length = 120 +trim_trailing_whitespace = true diff --git a/gdb/cheri-compressed-cap/.github/workflows/c-cpp.yml b/gdb/cheri-compressed-cap/.github/workflows/c-cpp.yml new file mode 100644 index 00000000000..4041550a90d --- /dev/null +++ b/gdb/cheri-compressed-cap/.github/workflows/c-cpp.yml @@ -0,0 +1,22 @@ +name: C/C++ CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: 'Run CMake and build' + run: | + mkdir -p build + cd build && cmake .. + make -j`nproc` + - name: Run tests + run: cd build && ctest -V -j`nproc` diff --git a/gdb/cheri-compressed-cap/.gitignore b/gdb/cheri-compressed-cap/.gitignore new file mode 100644 index 00000000000..2f88b187144 --- /dev/null +++ b/gdb/cheri-compressed-cap/.gitignore @@ -0,0 +1,2 @@ +/cmake-build-debug +/.idea diff --git a/gdb/cheri-compressed-cap/.gitrepo b/gdb/cheri-compressed-cap/.gitrepo new file mode 100644 index 00000000000..9e05155a699 --- /dev/null +++ b/gdb/cheri-compressed-cap/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/CTSRD-CHERI/cheri-compressed-cap + branch = master + commit = 6762a19ba9fb6a4291011fa690ce9a2647af10b2 + parent = 8ff1bd02227d35386cbcff210be686c68e1865c8 + method = merge + cmdver = 0.4.3 diff --git a/gdb/cheri-compressed-cap/CMakeLists.txt b/gdb/cheri-compressed-cap/CMakeLists.txt new file mode 100644 index 00000000000..1b68e5c1fdf --- /dev/null +++ b/gdb/cheri-compressed-cap/CMakeLists.txt @@ -0,0 +1,179 @@ +cmake_minimum_required(VERSION 3.10) +project(cheri_compressed_cap C CXX) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) +if(APPLE AND NOT (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")) + # XXX: ugly hack, would be nice if llvm could find this + include_directories(SYSTEM /Library/Developer/CommandLineTools/usr/include/c++/v1) +endif() + +include(CheckCXXCompilerFlag) +# CMAKE_REQUIRED_LINK_OPTIONS needs cmake 3.13 +set(CMAKE_REQUIRED_LIBRARIES -fsanitize=undefined) +check_cxx_compiler_flag(-fsanitize=undefined HAVE_UBSAN) +set(CMAKE_REQUIRED_LIBRARIES -fsanitize=address) +check_cxx_compiler_flag(-fsanitize=address HAVE_ASAN) +set(CMAKE_REQUIRED_LIBRARIES -fsanitize=memory) +check_cxx_compiler_flag(-fsanitize=memory HAVE_MSAN) +# XXX: needs instrumented system libraries +set(HAVE_MSAN FALSE) +set(CMAKE_REQUIRED_LIBRARIES -fsanitize=fuzzer) +check_cxx_compiler_flag(-fsanitize=fuzzer HAVE_LIBFUZZER) + +include(CheckCCompilerFlag) +check_c_compiler_flag(-Wunused-but-set-variable HAVE_UNUSED_BUT_SET_VARIABLE) +if (HAVE_UNUSED_BUT_SET_VARIABLE) + add_compile_options(-DHAVE_UNUSED_BUT_SET_VARIABLE) +endif() + +if (HAVE_UBSAN) + add_compile_options(-fsanitize=undefined) + if (${CMAKE_VERSION} VERSION_GREATER 3.12) + add_link_options(-fsanitize=undefined) + else() + link_libraries(-fsanitize=undefined) + endif() +endif() + +add_compile_options(-Wno-gnu-statement-expression) +add_compile_options(-fno-common) + +add_compile_options(-pedantic -Wall -Wextra -Wno-old-style-cast -Wno-c++98-compat-pedantic -Wno-c++98-c++11-compat-binary-literal -Wno-c11-extensions -Wno-padded) +# TODO: -Weverything +add_library(cheri_compressed_cap14 cheri_compressed_cap.c cheri_compressed_cap.h) + +function(add_decompress_cap _format) + add_executable(decompress_cap_${_format} decompress_cap_${_format}.c) + include(GNUInstallDirs) + install(TARGETS decompress_cap_${_format} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endfunction() + +# TODO: add_decompress_cap(64) +add_decompress_cap(128) +add_decompress_cap(128m) + +# Test against sail generated C code: +# This needs GMP (and CMake doesn't include a find module) +find_package(PkgConfig) +pkg_check_modules(PC_GMP gmp) +find_path(GMP_INCLUDE_DIR gmp.h + HINTS ${PC_GMP_INCLUDEDIR} ${PC_GMP_INCLUDE_DIRS} + PATH_SUFFIXES gmp) +message(STATUS "GMP include dir: ${GMP_INCLUDE_DIR}") +find_library(GMP_LIBRARY NAMES gmp + HINTS ${PC_GMPL_LIBDIR} ${PC_GMP_LIBRARY_DIRS}) +message(STATUS "GMP library: ${GMP_LIBRARY}") + +if (NOT GMP_LIBRARY) + message(FATAL_ERROR "Cannot find GMP library") +endif() +if (NOT GMP_INCLUDE_DIR) + message(FATAL_ERROR "Cannot find GMP includes") +endif() +add_library(GMP::GMP UNKNOWN IMPORTED) +set_target_properties(GMP::GMP PROPERTIES + IMPORTED_LOCATION "${GMP_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${GMP_INCLUDE_DIR}" +) + +function(add_sail_wrapper _format _suffix _sanitizer_flags) + add_library(sail_wrapper_${_format}${_suffix} STATIC test/sail_wrapper_${_format}.c test/sail.c test/rts.c test/sail_failure.c) + if (_sanitizer_flags) + target_compile_options(sail_wrapper_${_format}${_suffix} PRIVATE "${_sanitizer_flags}") + target_link_libraries(sail_wrapper_${_format}${_suffix} PRIVATE "${_sanitizer_flags}") + endif() + target_link_libraries(sail_wrapper_${_format}${_suffix} PUBLIC GMP::GMP) # needs GMP, should propagate to all users + target_compile_options(sail_wrapper_${_format}${_suffix} PRIVATE -Wno-unused-function -Wno-unused-parameter -Wno-sign-compare -Wno-unused-label -Wno-extra-semi -Wno-gnu-binary-literal) +endfunction() + +function(add_sail_wrappers _format) + add_sail_wrapper(${_format} "" "") + if (HAVE_ASAN) + add_sail_wrapper(${_format} "-asan-ubsan" "-fsanitize=undefined,address") + endif() + if (HAVE_MSAN) + add_sail_wrapper(${_format} "-msan-ubsan" "-fsanitize=undefined,memory") + endif() +endfunction() + +add_sail_wrappers(64) +add_sail_wrappers(128) +add_sail_wrappers(128m) + +# Add test exes for sail generated code + +function(add_test_sail_wrapper _format) + add_executable(test_sail_wrapper_${_format} decompress_cap_${_format}.c) + target_compile_definitions(test_sail_wrapper_${_format} PRIVATE DECOMPRESS_WITH_SAIL_GENERATED_CODE=1) + target_link_libraries(test_sail_wrapper_${_format} PRIVATE sail_wrapper_${_format}) +endfunction() + +# TODO: add_test_sail_wrapper(64) +add_test_sail_wrapper(128) +add_test_sail_wrapper(128m) + +enable_testing() + +function(add_cc_test _format _target _suffix _sanitizer_flags) + add_executable(${_target}_${_format}${_suffix} test/${_target}_${_format}.cpp) + if (_sanitizer_flags) + target_compile_options(${_target}_${_format}${_suffix} PRIVATE "${_sanitizer_flags}") + target_link_libraries(${_target}_${_format}${_suffix} PRIVATE "${_sanitizer_flags}") + endif() + if (NOT _format STREQUAL "256") + target_link_libraries(${_target}_${_format}${_suffix} PRIVATE sail_wrapper_${_format}${_suffix}) + endif() + add_test(NAME test-${_target}_${_format}${_suffix} COMMAND ${_target}_${_format}${_suffix}) +endfunction() + +function(add_cc_tests_target _format _target) + add_cc_test(${_format} ${_target} "" "") + if (HAVE_ASAN) + add_cc_test(${_format} ${_target} "-asan-ubsan" "-fsanitize=undefined,address") + endif() + if (HAVE_MSAN) + add_cc_test(${_format} ${_target} "-msan-ubsan" "-fsanitize=undefined,memory") + endif() +endfunction() + +function(add_cc_tests _format) + add_cc_tests_target(${_format} simple_test) + if (NOT _format STREQUAL "256") + # TODO: sail_setbounds_128m not yet implemented + if (NOT _format STREQUAL "128m") + add_cc_tests_target(${_format} setbounds_test) + endif() + # TODO: 41047 failed assertions + if (NOT _format STREQUAL "128m") + add_cc_tests_target(${_format} random_inputs_test) + endif() + endif() +endfunction() + +add_cc_tests(64) +add_cc_tests(128) +add_cc_tests(128m) +add_cc_tests(256) + +function(add_fuzz_tests _format) + if (HAVE_LIBFUZZER) + if (HAVE_ASAN) + add_sail_wrapper(${_format} "-fuzzer-asan-ubsan" "-fsanitize=undefined,address,fuzzer") + add_executable(fuzz_decompress_${_format}-asan test/fuzz_decompress_${_format}.cpp) + target_compile_options(fuzz_decompress_${_format}-asan PRIVATE -fsanitize=undefined,address,fuzzer) + target_link_libraries(fuzz_decompress_${_format}-asan PRIVATE -fsanitize=undefined,address,fuzzer) + target_link_libraries(fuzz_decompress_${_format}-asan PRIVATE sail_wrapper_${_format}-fuzzer-asan-ubsan) + endif() + if (HAVE_MSAN) + add_sail_wrapper(${_format} "-fuzzer-msan-ubsan" "-fsanitize=undefined,memory,fuzzer") + add_executable(fuzz_decompress_${_format}-msan test/fuzz_decompress_${_format}.cpp) + target_compile_options(fuzz_decompress_${_format}-msan PRIVATE -fsanitize=undefined,memory,fuzzer) + target_link_libraries(fuzz_decompress_${_format}-msan PRIVATE -fsanitize=undefined,memory,fuzzer) + target_link_libraries(fuzz_decompress_${_format}-msan PRIVATE sail_wrapper_${_format}-fuzzer-msan-ubsan) + endif() + endif() +endfunction() + +add_fuzz_tests(64) +add_fuzz_tests(128) +add_fuzz_tests(128m) diff --git a/gdb/cheri-compressed-cap/LICENSE b/gdb/cheri-compressed-cap/LICENSE new file mode 100644 index 00000000000..2bbc1315194 --- /dev/null +++ b/gdb/cheri-compressed-cap/LICENSE @@ -0,0 +1,29 @@ +SPDX-License-Identifier: BSD-2-Clause + +Copyright (c) 2018 (holder) +All rights reserved. + +This software was developed by SRI International and the University of +Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 +("CTSRD"), as part of the DARPA CRASH research programme. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/gdb/cheri-compressed-cap/README.md b/gdb/cheri-compressed-cap/README.md new file mode 100644 index 00000000000..b29a105058a --- /dev/null +++ b/gdb/cheri-compressed-cap/README.md @@ -0,0 +1,7 @@ +# cheri-compressed-cap ![CI](https://github.com/CTSRD-CHERI/cheri-compressed-cap/workflows/C/C++%20CI/badge.svg) +A C library to compress/decompress CHERI capabilities + +See Section 3.4.4 and 3.4.5 of [the instruction set architecture document](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-927.pdf) for an explanation of how the compression works. + +## Usage instructions +Simply include `cheri_compressed_cap.h` in your C project and you can use the provided functions to compress and decompress your capabilities. diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap.c b/gdb/cheri-compressed-cap/cheri_compressed_cap.c new file mode 100644 index 00000000000..f2eb12ace4b --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap.c @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "cheri_compressed_cap.h" + +// Just check that everything compiles +uint64_t test(void) { + uint64_t pesbt = 0x1234567; + uint64_t cursor = 0x98765431; + cc128_cap_t result; + cc128_decompress_mem(pesbt, cursor, false, &result); + uint64_t new_pesbt = cc128_compress_mem(&result); + return cc128_is_representable_cap_exact(&result) + new_pesbt; +} diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap.h b/gdb/cheri-compressed-cap/cheri_compressed_cap.h new file mode 100644 index 00000000000..35355eadd07 --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap.h @@ -0,0 +1,168 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Lawrence Esswood + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef CHERI_COMPRESSED_CAP_H +#define CHERI_COMPRESSED_CAP_H + +#ifdef CC_IS_MORELLO +#error "Use new cc128m/CC128M definitions instead of defining CC_IS_MORELLO" +#endif + +#include +#include +#include + +#include "cheri_compressed_cap_64.h" +#include "cheri_compressed_cap_128.h" +#include "cheri_compressed_cap_128m.h" + +/* Legacy CHERI256 things: */ + +/* For CHERI256 all permissions are shifted by one since the sealed bit comes first */ +#define CC256_HWPERMS_COUNT (12) +#define CC256_HWPERMS_RESERVED_COUNT (3) +#define CC256_UPERMS_COUNT (16) +#define CC256_MAX_UPERM CC256_UPERMS_COUNT +#define CC256_FLAGS_COUNT (1) +#define CC256_FLAGS_ALL_BITS _CC_BITMASK64(CC256_FLAGS_COUNT) /* 1 bit */ +#define CC256_PERMS_MEM_SHFT CC256_FLAGS_COUNT /* flags bit comes first */ +#define CC256_UPERMS_MEM_SHFT (CC256_PERMS_MEM_SHFT + CC256_HWPERMS_COUNT + CC256_HWPERMS_RESERVED_COUNT) +#define CC256_UPERMS_SHFT (15) +_CC_STATIC_ASSERT(CC128_UPERMS_SHFT >= CC256_HWPERMS_COUNT + CC256_HWPERMS_RESERVED_COUNT, ""); +_CC_STATIC_ASSERT_SAME(CC256_PERMS_MEM_SHFT + CC256_HWPERMS_COUNT + CC256_HWPERMS_RESERVED_COUNT, CC256_UPERMS_MEM_SHFT); +#define CC256_PERMS_ALL_BITS _CC_BITMASK64(CC256_HWPERMS_COUNT) /* 12 bits */ +#define CC256_PERMS_ALL_BITS_UNTAGGED _CC_BITMASK64(CC256_HWPERMS_COUNT + CC256_HWPERMS_RESERVED_COUNT) /* 15 bits */ +#define CC256_UPERMS_ALL_BITS _CC_BITMASK64(CC256_UPERMS_COUNT) /* 19 bits */ +#define CC256_OTYPE_ALL_BITS _CC_BITMASK64(CC256_OTYPE_BITS) +#define CC256_RESERVED_ALL_BITS _CC_BITMASK64(CC256_RESERVED_BITS) +#define CC256_OTYPE_MEM_SHFT (32) +_CC_STATIC_ASSERT_SAME(CC256_UPERMS_MEM_SHFT + CC256_UPERMS_COUNT, CC256_OTYPE_MEM_SHFT); +#define CC256_OTYPE_BITS (24) +#define CC256_RESERVED_MEM_SHFT (CC256_OTYPE_MEM_SHFT + CC256_OTYPE_BITS) +#define CC256_RESERVED_BITS (8) +#define CC256_NULL_LENGTH ((cc128_length_t)UINT64_MAX) +#define CC256_NULL_TOP ((cc128_length_t)UINT64_MAX) +_CC_STATIC_ASSERT_SAME(CC256_FLAGS_COUNT + CC256_HWPERMS_COUNT + CC256_HWPERMS_RESERVED_COUNT + CC256_UPERMS_COUNT + + CC256_OTYPE_BITS + CC256_RESERVED_BITS, + 64); + +#define CC256_SPECIAL_OTYPE(name, subtract) CC256_##name = (CC256_MAX_REPRESENTABLE_OTYPE - subtract##u) +// Reserve 16 otypes +enum CC256_OTypes { + CC256_MAX_REPRESENTABLE_OTYPE = ((1u << CC256_OTYPE_BITS) - 1u), + CC256_SPECIAL_OTYPE(OTYPE_UNSEALED, 0), + CC256_SPECIAL_OTYPE(OTYPE_SENTRY, 1), + CC256_SPECIAL_OTYPE(MAX_RESERVED_OTYPE, 0), + CC256_SPECIAL_OTYPE(MIN_RESERVED_OTYPE, 15), +}; + +#define CC256_LS_SPECIAL_OTYPES(ITEM, ...) \ + ITEM(OTYPE_UNSEALED, __VA_ARGS__) \ + ITEM(OTYPE_SENTRY, __VA_ARGS__) + +typedef struct cc256_cap { + uint64_t cr_cursor; + uint64_t cr_base; + uint64_t cr_length; + uint32_t cr_otype; + uint16_t cr_perms; + uint16_t cr_uperms; + uint8_t cr_tag; + uint8_t cr_flags; + uint8_t cr_reserved; +#ifdef __cplusplus + inline uint64_t base() const { return cr_base; } + inline uint64_t address() const { return cr_cursor; } + inline int64_t offset() const { return cr_cursor - cr_base; } + inline cc128_length_t top() const { return (cc128_length_t)cr_base + cr_length; } + inline cc128_addr_t top64() const { + const cc128_length_t t = top(); + return t > CC128_MAX_ADDR ? CC128_MAX_ADDR : (cc128_addr_t)t; + } + inline uint64_t length() const { return cr_length; } + inline uint64_t length64() const { return cr_length; } + inline uint32_t software_permissions() const { return cr_uperms; } + inline uint32_t permissions() const { return cr_perms; } + inline uint32_t type() const { return cr_otype; } + inline bool is_sealed() const { return type() != CC256_OTYPE_UNSEALED; } + inline uint8_t flags() const { return cr_flags; } + inline uint8_t reserved_bits() const { return cr_reserved; } +#endif +} cc256_cap_t; + +/* Also support decoding of the raw 256-bit capabilities */ +typedef union _inmemory_chericap256 { + uint8_t bytes[32]; + uint32_t u32s[8]; + uint64_t u64s[4]; +} inmemory_chericap256; + +static inline bool cc256_is_cap_sealed(const cc256_cap_t* cp) { return cp->cr_otype != CC256_OTYPE_UNSEALED; } + +static inline void decompress_256cap(inmemory_chericap256 mem, cc256_cap_t* cdp, bool tagged) { + /* See CHERI ISA: Figure 3.1: 256-bit memory representation of a capability */ + uint32_t hwperms_mask = tagged ? CC256_PERMS_ALL_BITS : CC256_PERMS_ALL_BITS_UNTAGGED; + cdp->cr_flags = mem.u64s[0] & CC256_FLAGS_ALL_BITS; + cdp->cr_perms = (mem.u64s[0] >> CC256_PERMS_MEM_SHFT) & hwperms_mask; + cdp->cr_uperms = (mem.u64s[0] >> CC256_UPERMS_MEM_SHFT) & CC256_UPERMS_ALL_BITS; + cdp->cr_otype = ((mem.u64s[0] >> CC256_OTYPE_MEM_SHFT) & CC256_OTYPE_ALL_BITS) ^ CC256_OTYPE_UNSEALED; + cdp->cr_reserved = (mem.u64s[0] >> CC256_RESERVED_MEM_SHFT) & CC256_RESERVED_ALL_BITS; + cdp->cr_base = mem.u64s[2]; + /* Length is xor'ed with -1 to ensure that NULL is all zeroes in memory */ + uint64_t length = mem.u64s[3] ^ CC256_NULL_LENGTH; + cdp->cr_length = length; + cdp->cr_cursor = mem.u64s[1]; + cdp->cr_tag = tagged; + _cc_debug_assert(!(cdp->cr_tag && cdp->cr_reserved) && "Unknown reserved bits set it tagged capability"); +} + +static inline void compress_256cap(inmemory_chericap256* buffer, const cc256_cap_t* csp) { + _cc_debug_assert(!(csp->cr_tag && csp->cr_reserved) && "Unknown reserved bits set it tagged capability"); + bool flags_bits = csp->cr_flags & CC256_FLAGS_ALL_BITS; + // When writing an untagged value, just write back the bits that were loaded (including the reserved HWPERMS) + uint64_t hwperms_mask = csp->cr_tag ? CC256_PERMS_ALL_BITS : CC256_PERMS_ALL_BITS_UNTAGGED; + buffer->u64s[0] = + flags_bits | ((csp->cr_perms & hwperms_mask) << CC256_PERMS_MEM_SHFT) | + ((csp->cr_uperms & CC256_UPERMS_ALL_BITS) << CC256_UPERMS_MEM_SHFT) | + ((uint64_t)((csp->cr_otype ^ CC256_OTYPE_UNSEALED) & CC256_OTYPE_ALL_BITS) << CC256_OTYPE_MEM_SHFT) | + ((uint64_t)(csp->cr_reserved & CC256_RESERVED_ALL_BITS) << CC256_RESERVED_MEM_SHFT); + buffer->u64s[1] = csp->cr_cursor; + buffer->u64s[2] = csp->cr_base; + buffer->u64s[3] = csp->cr_length ^ CC256_NULL_LENGTH; +} + +#endif /* CHERI_COMPRESSED_CAP_H */ diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap_128.h b/gdb/cheri-compressed-cap/cheri_compressed_cap_128.h new file mode 100644 index 00000000000..028caff2727 --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap_128.h @@ -0,0 +1,178 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +// The following macros are expected to be defined +#define CC_FORMAT_LOWER 128 +#define CC_FORMAT_UPPER 128 +/* These should match the definitions in sail! */ +#define CC128_CAP_SIZE 16 +#define CC128_CAP_BITS 128 +#define CC128_ADDR_WIDTH 64 +#define CC128_LEN_WIDTH 65 +/* Max exponent is the largest exponent _required_, not that can be encoded. */ +#define CC128_MANTISSA_WIDTH 14 +#define CC128_MAX_EXPONENT 52 +#define CC128_CURSOR_MASK 0xFFFFFFFFFFFFFFFF +#define CC128_MAX_ADDRESS_PLUS_ONE ((cc128_length_t)1u << CC128_ADDR_WIDTH) +#define CC128_NULL_TOP CC128_MAX_ADDRESS_PLUS_ONE +#define CC128_NULL_LENGTH CC128_MAX_ADDRESS_PLUS_ONE +#define CC128_MAX_LENGTH CC128_MAX_ADDRESS_PLUS_ONE +#define CC128_MAX_TOP CC128_MAX_ADDRESS_PLUS_ONE +#define CC128_MAX_ADDR UINT64_MAX +/* Special otypes are allocated downwards from -1 */ +#define CC128_SPECIAL_OTYPE_VAL(subtract) (CC128_MAX_REPRESENTABLE_OTYPE - subtract##u) +#define CC128_SPECIAL_OTYPE_VAL_SIGNED(subtract) (((int64_t)-1) - subtract##u) + +/* Use __uint128 to represent 65 bit length */ +__extension__ typedef unsigned __int128 cc128_length_t; +__extension__ typedef signed __int128 cc128_offset_t; +typedef uint64_t cc128_addr_t; +#include "cheri_compressed_cap_macros.h" + +/* ignore ISO C restricts enumerator values to range of 'int' */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +enum { + _CC_FIELD(UPERMS, 127, 124), + _CC_FIELD(HWPERMS, 123, 112), + _CC_FIELD(RESERVED, 111, 110), + _CC_FIELD(FLAGS, 109, 109), + _CC_FIELD(OTYPE, 108, 91), + _CC_FIELD(EBT, 90, 64), + + _CC_FIELD(INTERNAL_EXPONENT, 90, 90), + _CC_FIELD(TOP_ENCODED, 89, 78), + _CC_FIELD(BOTTOM_ENCODED, 77, 64), + + // Top/bottom offsets depending in INTERNAL_EXPONENT flag: + // Without internal exponent: + _CC_FIELD(EXP_ZERO_TOP, 89, 78), + _CC_FIELD(EXP_ZERO_BOTTOM, 77, 64), + // With internal exponent: + _CC_FIELD(EXP_NONZERO_TOP, 89, 81), + _CC_FIELD(EXPONENT_HIGH_PART, 80, 78), + _CC_FIELD(EXP_NONZERO_BOTTOM, 77, 67), + _CC_FIELD(EXPONENT_LOW_PART, 66, 64), +}; +#pragma GCC diagnostic pop + +#define CC128_OTYPE_BITS CC128_FIELD_OTYPE_SIZE +#define CC128_BOT_WIDTH CC128_FIELD_EXP_ZERO_BOTTOM_SIZE +#define CC128_BOT_INTERNAL_EXP_WIDTH CC128_FIELD_EXP_NONZERO_BOTTOM_SIZE +#define CC128_EXP_LOW_WIDTH CC128_FIELD_EXPONENT_LOW_PART_SIZE + +#define CC128_PERM_GLOBAL (1 << 0) +#define CC128_PERM_EXECUTE (1 << 1) +#define CC128_PERM_LOAD (1 << 2) +#define CC128_PERM_STORE (1 << 3) +#define CC128_PERM_LOAD_CAP (1 << 4) +#define CC128_PERM_STORE_CAP (1 << 5) +#define CC128_PERM_STORE_LOCAL (1 << 6) +#define CC128_PERM_SEAL (1 << 7) +#define CC128_PERM_CINVOKE (1 << 8) +#define CC128_PERM_UNSEAL (1 << 9) +#define CC128_PERM_ACCESS_SYS_REGS (1 << 10) +#define CC128_PERM_SETCID (1 << 11) + +#define CC128_HIGHEST_PERM CC128_PERM_SETCID + +_CC_STATIC_ASSERT(CC128_HIGHEST_PERM < CC128_FIELD_HWPERMS_MAX_VALUE, "permissions not representable?"); +_CC_STATIC_ASSERT((CC128_HIGHEST_PERM << 1) > CC128_FIELD_HWPERMS_MAX_VALUE, "all permission bits should be used"); + +#define CC128_PERMS_ALL (0xfff) /* [0...11] */ +#define CC128_UPERMS_ALL (0xf) /* [15...18] */ +#define CC128_UPERMS_SHFT (15) +#define CC128_UPERMS_MEM_SHFT (12) +#define CC128_MAX_UPERM (3) + +// We reserve 16 otypes +enum _CC_N(OTypes) { + CC128_FIRST_NONRESERVED_OTYPE = 0, + CC128_MAX_REPRESENTABLE_OTYPE = ((1u << CC128_OTYPE_BITS) - 1u), + _CC_SPECIAL_OTYPE(OTYPE_UNSEALED, 0), + _CC_SPECIAL_OTYPE(OTYPE_SENTRY, 1), + _CC_SPECIAL_OTYPE(OTYPE_INDIRECT_PAIR, 2), + _CC_SPECIAL_OTYPE(OTYPE_INDIRECT_SENTRY, 3), + _CC_SPECIAL_OTYPE(OTYPE_RESERVED_LAST, 15), + /* + * We allocate otypes subtracting from the maximum value, so the smallest is + * actually the one with the largest _CC_SPECIAL_OTYPE() argument. + * With 16 reserved otypes, this is currently -16, i.e. 0x3fff0. + */ + _CC_N(MIN_RESERVED_OTYPE) = _CC_N(OTYPE_RESERVED_LAST), + _CC_N(MAX_RESERVED_OTYPE) = _CC_N(OTYPE_UNSEALED), +}; + +#define CC128_LS_SPECIAL_OTYPES(ITEM, ...) \ + ITEM(OTYPE_UNSEALED, __VA_ARGS__) \ + ITEM(OTYPE_SENTRY, __VA_ARGS__) \ + ITEM(OTYPE_INDIRECT_PAIR, __VA_ARGS__) \ + ITEM(OTYPE_INDIRECT_SENTRY, __VA_ARGS__) + +_CC_STATIC_ASSERT_SAME(CC128_MANTISSA_WIDTH, CC128_FIELD_EXP_ZERO_BOTTOM_SIZE); + +#include "cheri_compressed_cap_common.h" + +// Sanity-check mask is the expected NULL encoding +_CC_STATIC_ASSERT_SAME(CC128_NULL_XOR_MASK, UINT64_C(0x00001ffffc018004)); + +__attribute__((deprecated("Use cc128_compress_raw"))) static inline uint64_t +compress_128cap_without_xor(const cc128_cap_t* csp) { + return cc128_compress_raw(csp); +} + +__attribute__((deprecated("Use cc128_compress_mem"))) static inline uint64_t compress_128cap(const cc128_cap_t* csp) { + return cc128_compress_mem(csp); +} + +__attribute__((deprecated("Use cc128_decompress_raw"))) static inline void +decompress_128cap_already_xored(uint64_t pesbt, uint64_t cursor, cc128_cap_t* cdp) { + cc128_decompress_raw(pesbt, cursor, cdp->cr_tag, cdp); +} + +__attribute__((deprecated("Use cc128_decompress_mem"))) static inline void +decompress_128cap(uint64_t pesbt, uint64_t cursor, _cc_cap_t* cdp) { + cc128_decompress_mem(pesbt, cursor, cdp->cr_tag, cdp); +} + +#define CC128_FIELD(name, last, start) _CC_FIELD(name, last, start) +#define CC128_ENCODE_FIELD(value, name) _CC_ENCODE_FIELD(value, name) +#define CC128_EXTRACT_FIELD(value, name) _CC_EXTRACT_FIELD(value, name) +#define CC128_ENCODE_EBT_FIELD(value, name) _CC_ENCODE_EBT_FIELD(value, name) + +#undef CC_FORMAT_LOWER +#undef CC_FORMAT_UPPER diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap_128m.h b/gdb/cheri-compressed-cap/cheri_compressed_cap_128m.h new file mode 100644 index 00000000000..71426a56876 --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap_128m.h @@ -0,0 +1,194 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Notes on Morello Vs Other CHERI platforms: + * The Morello Psuedo-code thinks of fields as inverted on each use, rather than being + * XOR-ED as they come from memory. To keep the style the same, I am inverting on load + * like we do with other CHERI platforms. + * Morello does NOT invert otype. + * + * An all 1's exponent, REGARDLESS of other bits in top and bottom, means max length + * cap! + * This gives rise to a whole bunch of non-canon max length caps in buildcap =( + * + * Flag bits are the same as top byte ignore bits. + * When adding to the address, representability checks mask the address AND the increment + * For bounds calculation, flags are ignored, and 56-bit addresses are SIGN EXTENDED?!?!? + * To avoid bugs around this, the highest non-flag bit is not allowed to change + * + * This means doing a set bounds on a cap with this bit set is out of bounds??? + * Also, caps with flag bits in the address are out of bounds, if length around 2^56 (unless they match the 58th bit) + * + * Morello embeds the user permissions in the middle of the hardware permissions. + */ +#define CC_IS_MORELLO + +// The following macros are expected to be defined +#define CC_FORMAT_LOWER 128m +#define CC_FORMAT_UPPER 128M +/* These should match the definitions in sail! */ +#define CC128M_CAP_SIZE 16 +#define CC128M_CAP_BITS 128 +#define CC128M_ADDR_WIDTH 64 +#define CC128M_LEN_WIDTH 65 +/* Max exponent is the largest exponent _required_, not that can be encoded. */ +#define CC128M_MANTISSA_WIDTH 16 +#define CC128M_MAX_EXPONENT 50 +#define CC128M_MAX_ENCODABLE_EXPONENT 63 +#define CC128M_CURSOR_MASK 0x00FFFFFFFFFFFFFF +#define CC128M_MAX_ADDRESS_PLUS_ONE ((cc128m_length_t)1u << CC128M_ADDR_WIDTH) +#define CC128M_NULL_TOP CC128M_MAX_ADDRESS_PLUS_ONE +#define CC128M_NULL_LENGTH CC128M_MAX_ADDRESS_PLUS_ONE +#define CC128M_MAX_LENGTH CC128M_MAX_ADDRESS_PLUS_ONE +#define CC128M_MAX_TOP CC128M_MAX_ADDRESS_PLUS_ONE +#define CC128M_MAX_ADDR UINT64_MAX +/* Special otypes are allocated upwards from 0 */ +#define CC128M_SPECIAL_OTYPE_VAL(val) (val##u) +#define CC128M_SPECIAL_OTYPE_VAL_SIGNED(val) (val##u) + +/* Use __uint128 to represent 65 bit length */ +__extension__ typedef unsigned __int128 cc128m_length_t; +__extension__ typedef signed __int128 cc128m_offset_t; +typedef uint64_t cc128m_addr_t; +#include "cheri_compressed_cap_macros.h" + +/* ignore ISO C restricts enumerator values to range of 'int' */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +enum { + // Morello HW perms actually 127..116, and 111...100. But seperating the fields is just a headache, and would make + // other code more complex. Pretend that they are all HW Perms. + _CC_FIELD(HWPERMS, 127, 110), + _CC_FIELD(UPERMS, 111, 112), + // Should _CC_FIELD(UPERMS, 115, 112), if that wouldn't cause a double count because of above. + _CC_FIELD(OTYPE, 109, 95), + _CC_FIELD(EBT, 94, 64), +// This is a bit dodgy. This enum only really works for non-address bits. +// Just provide nonsense values that will make the length of the range 0. +// Should really be `_CC_FIELD(FLAGS, 63, 56)', if this stuff applied to the address +#define MORELLO_FLAG_BITS 8 + _CC_FIELD(FLAGS, 64, 65), + _CC_FIELD(RESERVED, 64, 65), + + _CC_FIELD(INTERNAL_EXPONENT, 94, 94), + _CC_FIELD(TOP_ENCODED, 93, 80), + _CC_FIELD(BOTTOM_ENCODED, 79, 64), + + // Top/bottom offsets depending in INTERNAL_EXPONENT flag: + // Without internal exponent: + _CC_FIELD(EXP_ZERO_TOP, 93, 80), + _CC_FIELD(EXP_ZERO_BOTTOM, 79, 64), + // With internal exponent: + _CC_FIELD(EXP_NONZERO_TOP, 93, 83), + _CC_FIELD(EXPONENT_HIGH_PART, 82, 80), + _CC_FIELD(EXP_NONZERO_BOTTOM, 79, 67), + _CC_FIELD(EXPONENT_LOW_PART, 66, 64), +}; +#pragma GCC diagnostic pop + +#define CC128M_OTYPE_BITS CC128M_FIELD_OTYPE_SIZE +#define CC128M_BOT_WIDTH CC128M_FIELD_EXP_ZERO_BOTTOM_SIZE +#define CC128M_BOT_INTERNAL_EXP_WIDTH CC128M_FIELD_EXP_NONZERO_BOTTOM_SIZE +#define CC128M_EXP_LOW_WIDTH CC128M_FIELD_EXPONENT_LOW_PART_SIZE + +#define CC128M_PERM_GLOBAL (1 << 0) +#define CC128M_PERM_EXECUTIVE (1 << 1) +// Then 4 user types +#define CC128M_PERM_MUTABLE_LOAD (1 << 6) +#define CC128M_PERM_SETCID (1 << 7) +#define CC128M_PERM_BRANCH_SEALED_PAIR (1 << 8) +#define CC128M_PERM_CINVOKE CC128M_PERM_BRANCH_SEALED_PAIR +#define CC128M_PERM_SYSTEM (1 << 9) +#define CC128M_PERM_ACCESS_SYS_REGS CC128M_PERM_SYSTEM +#define CC128M_PERM_UNSEAL (1 << 10) +#define CC128M_PERM_SEAL (1 << 11) +#define CC128M_PERM_STORE_LOCAL (1 << 12) +#define CC128M_PERM_STORE_CAP (1 << 13) +#define CC128M_PERM_LOAD_CAP (1 << 14) +#define CC128M_PERM_EXECUTE (1 << 15) +#define CC128M_PERM_STORE (1 << 16) +#define CC128M_PERM_LOAD (1 << 17) + +#define CC128M_HIGHEST_PERM CC128M_PERM_LOAD + +_CC_STATIC_ASSERT(CC128M_HIGHEST_PERM < CC128M_FIELD_HWPERMS_MAX_VALUE, "permissions not representable?"); +_CC_STATIC_ASSERT((CC128M_HIGHEST_PERM << 1) > CC128M_FIELD_HWPERMS_MAX_VALUE, "all permission bits should be used"); + +#define CC128M_PERMS_ALL (0x3FFFF) /* [0...1,6..17] */ +#define CC128M_UPERMS_ALL (0x0) /* [15...18] */ +#define CC128M_UPERMS_SHFT (0) +#define CC128M_MAX_UPERM (3) + +/* Morello calls the special otypes LB, LPB and RB. + * LPB is "load pair branch". It loads the first two caps pointed to and ccalls them. + * LB is "load branch". It loads, plus an immediate, the cap pointed to and jumps to it. + * RB is a sentry. God knows what it stands for. Restricted Branch? + */ + +// We reserve 3 otypes +enum _CC_N(OTypes) { + CC128M_FIRST_NONRESERVED_OTYPE = 0, + CC128M_MAX_REPRESENTABLE_OTYPE = ((1u << CC128M_OTYPE_BITS) - 1u), + _CC_SPECIAL_OTYPE(OTYPE_UNSEALED, 0), + _CC_SPECIAL_OTYPE(OTYPE_SENTRY, 1), + _CC_SPECIAL_OTYPE(OTYPE_LOAD_PAIR_BRANCH, 2), + _CC_SPECIAL_OTYPE(OTYPE_LOAD_BRANCH, 3), + _CC_N(MIN_RESERVED_OTYPE) = _CC_N(OTYPE_UNSEALED), + _CC_N(MAX_RESERVED_OTYPE) = _CC_N(OTYPE_LOAD_BRANCH), +}; + +#define CC128M_LS_SPECIAL_OTYPES(ITEM, ...) \ + ITEM(OTYPE_UNSEALED, __VA_ARGS__) \ + ITEM(OTYPE_SENTRY, __VA_ARGS__) \ + ITEM(OTYPE_LOAD_PAIR_BRANCH, __VA_ARGS__) \ + ITEM(OTYPE_LOAD_BRANCH, __VA_ARGS__) + +_CC_STATIC_ASSERT_SAME(CC128M_MANTISSA_WIDTH, CC128M_FIELD_EXP_ZERO_BOTTOM_SIZE); + +#include "cheri_compressed_cap_common.h" + +// Sanity-check mask is the expected NULL encoding +_CC_STATIC_ASSERT_SAME(CC128M_NULL_XOR_MASK, UINT64_C(0x0000000040070007)); + +#define CC128M_FIELD(name, last, start) _CC_FIELD(name, last, start) +#define CC128M_ENCODE_FIELD(value, name) _CC_ENCODE_FIELD(value, name) +#define CC128M_EXTRACT_FIELD(value, name) _CC_EXTRACT_FIELD(value, name) +#define CC128M_ENCODE_EBT_FIELD(value, name) _CC_ENCODE_EBT_FIELD(value, name) + +#undef CC_IS_MORELLO +#undef CC_FORMAT_LOWER +#undef CC_FORMAT_UPPER diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap_64.h b/gdb/cheri-compressed-cap/cheri_compressed_cap_64.h new file mode 100644 index 00000000000..3df64823fea --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap_64.h @@ -0,0 +1,147 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +// The following macros are expected to be defined +#define CC_FORMAT_LOWER 64 +#define CC_FORMAT_UPPER 64 +/* These should match the definitions in sail! */ +#define CC64_CAP_SIZE 8 +#define CC64_CAP_BITS 64 +#define CC64_ADDR_WIDTH 32 +#define CC64_LEN_WIDTH 33 +#define CC64_MANTISSA_WIDTH 8 +#define CC64_MAX_EXPONENT 26 +#define CC64_CURSOR_MASK 0xFFFFFFFF + +#define CC64_MAX_ADDRESS_PLUS_ONE ((cc64_length_t)1u << CC64_ADDR_WIDTH) +#define CC64_NULL_TOP CC64_MAX_ADDRESS_PLUS_ONE +#define CC64_NULL_LENGTH CC64_MAX_ADDRESS_PLUS_ONE +#define CC64_MAX_LENGTH CC64_MAX_ADDRESS_PLUS_ONE +#define CC64_MAX_TOP CC64_MAX_ADDRESS_PLUS_ONE +#define CC64_MAX_ADDR UINT32_MAX +/* Special otypes are allocated downwards from -1 */ +#define CC64_SPECIAL_OTYPE_VAL(subtract) (CC64_MAX_REPRESENTABLE_OTYPE - subtract##u) +#define CC64_SPECIAL_OTYPE_VAL_SIGNED(subtract) (((int64_t)-1) - subtract##u) + +/* Use uint64_t to represent 33 bit length */ +typedef uint64_t cc64_length_t; +typedef int64_t cc64_offset_t; +typedef uint32_t cc64_addr_t; +#include "cheri_compressed_cap_macros.h" + +/* ignore ISO C restricts enumerator values to range of 'int' */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +enum { + _CC_FIELD(HWPERMS, 63, 52), + _CC_FIELD(FLAGS, 51, 51), + _CC_FIELD(OTYPE, 50, 47), + _CC_FIELD(EBT, 46, 32), + + _CC_FIELD(INTERNAL_EXPONENT, 46, 46), + _CC_FIELD(TOP_ENCODED, 45, 40), + _CC_FIELD(BOTTOM_ENCODED, 39, 32), + + // Top/bottom offsets depending in INTERNAL_EXPONENT flag: + // Without internal exponent: + _CC_FIELD(EXP_ZERO_TOP, 45, 40), + _CC_FIELD(EXP_ZERO_BOTTOM, 39, 32), + // With internal exponent: + _CC_FIELD(EXP_NONZERO_TOP, 45, 43), + _CC_FIELD(EXPONENT_HIGH_PART, 42, 40), + _CC_FIELD(EXP_NONZERO_BOTTOM, 39, 35), + _CC_FIELD(EXPONENT_LOW_PART, 34, 32), + _CC_FIELD(RESERVED, 31, 32), /* No reserved bits */ + _CC_FIELD(UPERMS, 31, 32), /* No uperms */ +}; +#pragma GCC diagnostic pop +_CC_STATIC_ASSERT_SAME(CC64_FIELD_UPERMS_SIZE, 0); +_CC_STATIC_ASSERT_SAME(CC64_FIELD_RESERVED_SIZE, 0); + +#define CC64_OTYPE_BITS CC64_FIELD_OTYPE_SIZE +#define CC64_BOT_WIDTH CC64_FIELD_EXP_ZERO_BOTTOM_SIZE +#define CC64_BOT_INTERNAL_EXP_WIDTH CC64_FIELD_EXP_NONZERO_BOTTOM_SIZE +#define CC64_EXP_LOW_WIDTH CC64_FIELD_EXPONENT_LOW_PART_SIZE + +#define CC64_PERM_GLOBAL (1 << 0) +#define CC64_PERM_EXECUTE (1 << 1) +#define CC64_PERM_LOAD (1 << 2) +#define CC64_PERM_STORE (1 << 3) +#define CC64_PERM_LOAD_CAP (1 << 4) +#define CC64_PERM_STORE_CAP (1 << 5) +#define CC64_PERM_STORE_LOCAL (1 << 6) +#define CC64_PERM_SEAL (1 << 7) +#define CC64_PERM_CINVOKE (1 << 8) +#define CC64_PERM_UNSEAL (1 << 9) +#define CC64_PERM_ACCESS_SYS_REGS (1 << 10) +#define CC64_PERM_SETCID (1 << 11) +_CC_STATIC_ASSERT(CC64_PERM_SETCID < CC64_FIELD_HWPERMS_MAX_VALUE, "permissions not representable?"); + +#define CC64_PERMS_ALL (0xfff) /* [0...11] */ +#define CC64_UPERMS_ALL (0) /* [15...18] */ +#define CC64_UPERMS_SHFT (15) +#define CC64_MAX_UPERM (0) + +// We reserve 16 otypes +enum _CC_N(OTypes) { + CC64_MAX_REPRESENTABLE_OTYPE = ((1u << CC64_OTYPE_BITS) - 1u), + _CC_SPECIAL_OTYPE(OTYPE_UNSEALED, 0), + _CC_SPECIAL_OTYPE(OTYPE_SENTRY, 1), + _CC_SPECIAL_OTYPE(OTYPE_RESERVED2, 2), + _CC_SPECIAL_OTYPE(OTYPE_RESERVED3, 3), + _CC_N(MIN_RESERVED_OTYPE) = _CC_N(OTYPE_RESERVED3), + _CC_N(MAX_RESERVED_OTYPE) = _CC_N(OTYPE_UNSEALED), +}; + +#define CC64_LS_SPECIAL_OTYPES(ITEM, ...) \ + ITEM(OTYPE_UNSEALED, __VA_ARGS__) \ + ITEM(OTYPE_SENTRY, __VA_ARGS__) + +_CC_STATIC_ASSERT_SAME(CC64_MANTISSA_WIDTH, CC64_FIELD_EXP_ZERO_BOTTOM_SIZE); + +#include "cheri_compressed_cap_common.h" + +// Sanity-check mask is the expected NULL encoding +_CC_STATIC_ASSERT_SAME(CC64_NULL_XOR_MASK, UINT32_C(0x7c302)); + +#define CC64_FIELD(name, last, start) _CC_FIELD(name, last, start) +#define CC64_ENCODE_FIELD(value, name) _CC_ENCODE_FIELD(value, name) +#define CC64_EXTRACT_FIELD(value, name) _CC_EXTRACT_FIELD(value, name) +#define CC64_ENCODE_EBT_FIELD(value, name) _CC_ENCODE_EBT_FIELD(value, name) + +#undef CC_FORMAT_LOWER +#undef CC_FORMAT_UPPER diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap_common.h b/gdb/cheri-compressed-cap/cheri_compressed_cap_common.h new file mode 100644 index 00000000000..1adc85edf3a --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap_common.h @@ -0,0 +1,987 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Lawrence Esswood + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +enum { + // For the reset capability we use an internal exponent and need + // 2^ADDR_WIDTH, which uses the max exponent. + _CC_N(RESET_EXP) = _CC_N(MAX_EXPONENT), + _CC_N(RESET_T) = 1u << (_CC_N(ADDR_WIDTH) - _CC_N(RESET_EXP) - _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE)), +#ifdef CC_IS_MORELLO + // Due to magic constant XOR aversion (i.e. fields are either entirely + // inverted or not at all, rather than select bits within them like in + // normal CHERI Concentrate), NULL is special in Morello. + _CC_N(NULL_EXP) = _CC_N(MAX_ENCODABLE_EXPONENT), + _CC_N(NULL_T) = 0, +#else + // NULL uses identical bounds encoding to the reset capability. + _CC_N(NULL_EXP) = _CC_N(RESET_EXP), + _CC_N(NULL_T) = _CC_N(RESET_T), +#endif + _CC_N(RESET_EBT) = + _CC_ENCODE_EBT_FIELD(1, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(_CC_N(RESET_T), EXP_NONZERO_TOP) | + _CC_ENCODE_EBT_FIELD(0, EXP_NONZERO_BOTTOM) | + _CC_ENCODE_EBT_FIELD(_CC_N(RESET_EXP) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) | + _CC_ENCODE_EBT_FIELD(_CC_N(RESET_EXP) & _CC_N(FIELD_EXPONENT_LOW_PART_MAX_VALUE), EXPONENT_LOW_PART), + _CC_N(NULL_PESBT) = _CC_ENCODE_FIELD(0, UPERMS) | _CC_ENCODE_FIELD(0, HWPERMS) | _CC_ENCODE_FIELD(0, RESERVED) | + _CC_ENCODE_FIELD(0, FLAGS) | _CC_ENCODE_FIELD(1, INTERNAL_EXPONENT) | + _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE) | + _CC_ENCODE_FIELD(_CC_N(NULL_T), EXP_NONZERO_TOP) | _CC_ENCODE_FIELD(0, EXP_NONZERO_BOTTOM) | + _CC_ENCODE_FIELD(_CC_N(NULL_EXP) >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), EXPONENT_HIGH_PART) | + _CC_ENCODE_FIELD(_CC_N(NULL_EXP) & _CC_N(FIELD_EXPONENT_LOW_PART_MAX_VALUE), EXPONENT_LOW_PART), + // We mask on store/load so this invisibly keeps null 0 whatever we choose + // it to be. + _CC_N(NULL_XOR_MASK) = _CC_N(NULL_PESBT), +}; +#pragma GCC diagnostic pop + +#define _cc_length_t _cc_N(length_t) +#define _cc_offset_t _cc_N(offset_t) +#define _cc_addr_t _cc_N(addr_t) + +#define _CC_MANTISSA_WIDTH _CC_N(MANTISSA_WIDTH) +#define _CC_MAX_EXPONENT _CC_N(MAX_EXPONENT) +#define _CC_BOT_INTERNAL_EXP_WIDTH _CC_N(FIELD_EXP_NONZERO_BOTTOM_SIZE) +#define _CC_EXP_LOW_WIDTH _CC_N(FIELD_EXPONENT_LOW_PART_SIZE) +#define _CC_EXP_HIGH_WIDTH _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE) +#define _CC_ADDR_WIDTH _CC_N(ADDR_WIDTH) +#define _CC_LEN_WIDTH _CC_N(LEN_WIDTH) +#define _CC_MAX_ADDR _CC_N(MAX_ADDR) +#define _CC_MAX_TOP _CC_N(MAX_TOP) +#define _CC_CURSOR_MASK _CC_N(CURSOR_MASK) +// Check that the sizes of the individual fields match up +_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_EBT_SIZE) + _CC_N(FIELD_OTYPE_SIZE) + _CC_N(FIELD_FLAGS_SIZE) + + _CC_N(FIELD_RESERVED_SIZE) + _CC_N(FIELD_HWPERMS_SIZE) + _CC_N(FIELD_UPERMS_SIZE), + _CC_ADDR_WIDTH); +_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_INTERNAL_EXPONENT_SIZE) + _CC_N(FIELD_EXP_ZERO_TOP_SIZE) + + _CC_N(FIELD_EXP_ZERO_BOTTOM_SIZE), + _CC_N(FIELD_EBT_SIZE)); +_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_INTERNAL_EXPONENT_SIZE) + _CC_N(FIELD_TOP_ENCODED_SIZE) + + _CC_N(FIELD_BOTTOM_ENCODED_SIZE), + _CC_N(FIELD_EBT_SIZE)); +_CC_STATIC_ASSERT_SAME(_CC_N(FIELD_INTERNAL_EXPONENT_SIZE) + _CC_N(FIELD_EXP_NONZERO_TOP_SIZE) + + _CC_N(FIELD_EXP_NONZERO_BOTTOM_SIZE) + _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE) + + _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), + _CC_N(FIELD_EBT_SIZE)); + +// Sanity-check the min/max otype macros: +_CC_STATIC_ASSERT(_CC_N(MIN_RESERVED_OTYPE) >= 0, "MIN_RESERVED_OTYPE is signed?"); +_CC_STATIC_ASSERT(_CC_N(MIN_RESERVED_OTYPE) < _CC_N(MAX_RESERVED_OTYPE), + "MIN_RESERVED_OTYPE greater than MAX_RESERVED_OTYPE?"); +_CC_STATIC_ASSERT(_CC_N(MIN_RESERVED_OTYPE) <= _CC_N(MAX_REPRESENTABLE_OTYPE), "MIN_RESERVED_OTYPE out of range?"); +_CC_STATIC_ASSERT(_CC_N(MAX_RESERVED_OTYPE) <= _CC_N(MAX_REPRESENTABLE_OTYPE), "MAX_RESERVED_OTYPE out of range?"); + +// Forward-declare the accessors since we use them inside the struct body: +struct _cc_N(cap); +static inline uint8_t _cc_N(get_flags)(const struct _cc_N(cap)* cap); +static inline uint32_t _cc_N(get_otype)(const struct _cc_N(cap)* cap); +static inline uint32_t _cc_N(get_perms)(const struct _cc_N(cap)* cap); +static inline uint8_t _cc_N(get_reserved)(const struct _cc_N(cap)* cap); +static inline uint32_t _cc_N(get_uperms)(const struct _cc_N(cap)* cap); + +// In order to allow vector loads and store from memory we can optionally reverse the first two fields. +struct _cc_N(cap) { + /* offset = cursor - base */ +#ifdef _CC_REVERSE_PESBT_CURSOR_ORDER + /* Original PESBT from the decompressed capability. If you modify + * other fields, you must be sure to either recalculate this field to match */ + _cc_addr_t cr_pesbt; + _cc_addr_t _cr_cursor; /* Capability cursor */ +#else + _cc_addr_t _cr_cursor; + _cc_addr_t cr_pesbt; +#endif + _cc_length_t _cr_top; /* Capability top */ + _cc_addr_t cr_base; /* Capability base addr */ + uint8_t cr_tag; /* Tag */ + uint8_t cr_bounds_valid; /* Set if bounds decode was given an invalid cap */ + uint8_t cr_exp; /* Exponent */ + uint8_t cr_extra; /* Additional data stored by the caller */ +#ifdef __cplusplus + inline _cc_addr_t base() const { return cr_base; } + inline _cc_addr_t address() const { return _cr_cursor; } + inline _cc_offset_t offset() const { return (_cc_offset_t)_cr_cursor - (_cc_offset_t)cr_base; } + inline _cc_length_t top() const { return _cr_top; } + inline _cc_addr_t top64() const { + const _cc_length_t t = top(); + return t > _CC_MAX_ADDR ? _CC_MAX_ADDR : (_cc_addr_t)t; + } + inline _cc_length_t length() const { return _cr_top - cr_base; } + inline _cc_addr_t length64() const { + const _cc_length_t l = length(); + return l > _CC_MAX_ADDR ? _CC_MAX_ADDR : (_cc_addr_t)l; + } + inline uint32_t software_permissions() const { return _cc_N(get_uperms)(this); } + inline uint32_t permissions() const { return _cc_N(get_perms)(this); } + inline uint32_t type() const { return _cc_N(get_otype)(this); } + inline bool is_sealed() const { return type() != _CC_N(OTYPE_UNSEALED); } + inline uint8_t reserved_bits() const { return _cc_N(get_reserved)(this); } + inline uint8_t flags() const { return _cc_N(get_flags)(this); } + inline bool operator==(const _cc_N(cap) & other) const; +#endif +}; +typedef struct _cc_N(cap) _cc_N(cap_t); +#define _cc_cap_t _cc_N(cap_t) + +static inline bool _cc_N(exactly_equal)(const _cc_cap_t* a, const _cc_cap_t* b) { + return a->cr_tag == b->cr_tag && a->_cr_cursor == b->_cr_cursor && a->cr_pesbt == b->cr_pesbt; +} + +static inline bool _cc_N(raw_equal)(const _cc_cap_t* a, const _cc_cap_t* b) { + return a->_cr_cursor == b->_cr_cursor && + a->cr_pesbt == b->cr_pesbt && + a->_cr_top == b->_cr_top && + a->cr_base == b->cr_base && + a->cr_tag == b->cr_tag && + a->cr_bounds_valid == b->cr_bounds_valid && + a->cr_exp == b->cr_exp && + a->cr_extra == b->cr_extra; +} + +/* Returns the index of the most significant bit set in x */ +static inline uint32_t _cc_N(idx_MSNZ)(uint64_t x) { +#if defined(__GNUC__) && !defined(__clang__) +#define CAP_HAVE_BUILTIN_CLZ +#elif defined(__has_builtin) +#if __has_builtin(__builtin_clzll) +#define CAP_HAVE_BUILTIN_CLZ +#endif +#endif + +#ifndef CAP_HAVE_BUILTIN_CLZ +/* floor(log2(x)) != floor(log2(y)) */ +#warning "__builtin_clzll not supported, using slower path" +#define ld_neq(x, y) (((x) ^ (y)) > ((x) & (y))) + uint32_t r = ld_neq(x, x & 0x5555555555555555ull) + (ld_neq(x, x & 0x3333333333333333ull) << 1) + + (ld_neq(x, x & 0x0f0f0f0f0f0f0f0full) << 2) + (ld_neq(x, x & 0x00ff00ff00ff00ffull) << 3) + + (ld_neq(x, x & 0x0000ffff0000ffffull) << 4) + (ld_neq(x, x & 0x00000000ffffffffull) << 5); +#undef ld_neq +#else + _cc_debug_assert(x != 0); + uint32_t r = 63u - (uint32_t)__builtin_clzll(x); +#endif + return r; +} + +/* + * e = idxMSNZ( (rlength + (rlength >> 6)) >> 19 ) + * where (rlength + (rlength >> 6)) needs to be a 65 bit integer + */ +static inline uint32_t _cc_N(compute_e)(_cc_addr_t rlength, uint32_t bwidth) { + if (rlength < (1u << (bwidth - 1))) + return 0; + + return (_cc_N(idx_MSNZ)(rlength) - (bwidth - 2)); +} + +static inline uint32_t _cc_N(get_exponent)(_cc_length_t length) { + const uint32_t bwidth = _CC_MANTISSA_WIDTH; + if (length > _CC_MAX_ADDR) { + return _CC_LEN_WIDTH - (bwidth - 1); + } else { + return _cc_N(compute_e)((_cc_addr_t)length, bwidth); + } +} + +static inline uint64_t _cc_N(getbits)(uint64_t src, uint32_t start, uint32_t size) { + return ((src >> start) & ((UINT64_C(1) << size) - UINT64_C(1))); +} + +// truncates `value`, keeping only the _least_ significant `n` bits. +static inline uint64_t _cc_N(truncate64)(uint64_t value, size_t n) { return value & ((UINT64_C(1) << n) - 1); } + +// truncates `value`, keeping only the _most_ significant `n` bits. +#define TRUNCATE_LSB_FUNC(type_width) \ + static inline uint64_t _CC_CONCAT(_cc_N(truncateLSB_), type_width)(uint64_t value, size_t n) { \ + _CC_STATIC_ASSERT(type_width <= 64, ""); \ + return value >> (type_width - n); \ + } +TRUNCATE_LSB_FUNC(_CC_MANTISSA_WIDTH) +TRUNCATE_LSB_FUNC(32) +TRUNCATE_LSB_FUNC(64) + +#define _cc_truncateLSB_generic(type_width) _CC_CONCAT(_cc_N(truncateLSB_), _CC_EXPAND(type_width)) +#define _cc_truncateLSB(type_width) _cc_N(_CC_CONCAT(truncateLSB_, type_width)) + +struct _cc_N(bounds_bits) { + uint16_t B; // bottom bits (currently 14 bits) + uint16_t T; // top bits (12 bits plus two implied bits) + uint8_t E; // exponent + bool IE; // internal exponent flag +}; +#define _cc_bounds_bits struct _cc_N(bounds_bits) + +#define ALL_WRAPPERS(X, FN, type) \ + static inline _cc_addr_t _cc_N(cap_pesbt_extract_##FN)(_cc_addr_t pesbt) { return _CC_EXTRACT_FIELD(pesbt, X); } \ + static inline _cc_addr_t _cc_N(cap_pesbt_encode_##FN)(type value) { return _CC_ENCODE_FIELD(value, X); } \ + static inline _cc_addr_t _cc_N(cap_pesbt_deposit_##FN)(_cc_addr_t pesbt, type value) { \ + _cc_debug_assert(value <= _CC_N(FIELD_##X##_MAX_VALUE)); \ + return (pesbt & ~_CC_N(FIELD_##X##_MASK64)) | _CC_ENCODE_FIELD(value, X); \ + } \ + static inline type _cc_N(get_##FN)(const _cc_cap_t* cap) { return _cc_N(cap_pesbt_extract_##FN)(cap->cr_pesbt); } \ + static inline void _cc_N(update_##FN)(_cc_cap_t * cap, _cc_addr_t value) { \ + cap->cr_pesbt = _cc_N(cap_pesbt_deposit_##FN)(cap->cr_pesbt, value); \ + } +ALL_WRAPPERS(HWPERMS, perms, uint32_t) +ALL_WRAPPERS(UPERMS, uperms, uint32_t) +ALL_WRAPPERS(OTYPE, otype, uint32_t) +ALL_WRAPPERS(FLAGS, flags, uint8_t) +ALL_WRAPPERS(RESERVED, reserved, uint8_t) +#undef ALL_WRAPPERS + +/// Extract the bits used for bounds and infer the top two bits of T +static inline _cc_bounds_bits _cc_N(extract_bounds_bits)(_cc_addr_t pesbt) { + _CC_STATIC_ASSERT(_CC_MANTISSA_WIDTH == _CC_N(BOT_WIDTH), "Wrong bot width?"); + uint32_t BWidth = _CC_MANTISSA_WIDTH; + uint32_t BMask = (1u << BWidth) - 1; + uint32_t TMask = BMask >> 2; + _cc_bounds_bits result; + _CC_STATIC_ASSERT(sizeof(result.B) * __CHAR_BIT__ >= _CC_MANTISSA_WIDTH, "B field too small"); + _CC_STATIC_ASSERT(sizeof(result.T) * __CHAR_BIT__ >= _CC_MANTISSA_WIDTH, "T field too small"); + _CC_STATIC_ASSERT(sizeof(result.E) * __CHAR_BIT__ >= + _CC_N(FIELD_EXPONENT_LOW_PART_SIZE) + _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE), + "E field too small"); + result.IE = (bool)(uint32_t)_CC_EXTRACT_FIELD(pesbt, INTERNAL_EXPONENT); + uint8_t L_msb; + if (result.IE) { + result.E = (uint8_t)(_CC_EXTRACT_FIELD(pesbt, EXPONENT_LOW_PART) | + (_CC_EXTRACT_FIELD(pesbt, EXPONENT_HIGH_PART) << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE))); + // Do not offset by 1! We also need to encode E=0 even with IE + // Also allow nonsense values over 64 - BWidth + 2: this is expected by sail-generated tests + // E = MIN(64 - BWidth + 2, E); +#ifdef CC_IS_MORELLO + if (result.E == _CC_N(MAX_ENCODABLE_EXPONENT)) { + result.B = 0; + // This isn't top, its T. We just special case again when top is calculated. + result.T = 0; + return result; + } +#endif + result.B = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_NONZERO_BOTTOM) << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE); + result.T = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_NONZERO_TOP) << _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE); + L_msb = 1; + } else { + // So, I cheated by inverting E on memory load (to match the rest of CHERI), which Morello does not do. + // This means parts of B and T are incorrectly inverted. So invert back again. +#ifdef CC_IS_MORELLO + pesbt ^= _CC_N(NULL_XOR_MASK); +#endif + result.E = 0; + L_msb = 0; + result.B = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_ZERO_BOTTOM); + result.T = (uint16_t)_CC_EXTRACT_FIELD(pesbt, EXP_ZERO_TOP); + } + /* + Reconstruct top two bits of T given T = B + len and: + 1) the top two bits of B + 2) most significant two bits of length derived from format above + 3) carry out of B[20..0] + len[20..0] that is implied if T[20..0] < B[20..0] + */ + uint8_t L_carry = result.T < (result.B & TMask) ? 1 : 0; + uint64_t BTop2 = _cc_N(getbits)(result.B, _CC_MANTISSA_WIDTH - 2, 2); + uint8_t T_infer = (BTop2 + L_carry + L_msb) & 0x3; + result.T |= ((uint16_t)T_infer) << (BWidth - 2); + return result; +} + +// Certain bit patterns can result in invalid bounds bits. These values must never be tagged! +static inline bool _cc_N(bounds_bits_valid)(_cc_bounds_bits bounds) { + // https://github.com/CTSRD-CHERI/sail-cheri-riscv/blob/7a308ef3661e43461c8431c391aaece7fba6e992/src/cheri_properties.sail#L104 + _cc_addr_t Bmsb = _cc_N(getbits)(bounds.B, _CC_MANTISSA_WIDTH - 1, 1); + _cc_addr_t Bmsb2 = _cc_N(getbits)(bounds.B, _CC_MANTISSA_WIDTH - 2, 2); + _cc_addr_t Tmsb = _cc_N(getbits)(bounds.T, _CC_MANTISSA_WIDTH - 1, 1); + if (bounds.E >= _CC_MAX_EXPONENT) { + return Tmsb == 0 && Bmsb2 == 0; + } else if (bounds.E == _CC_MAX_EXPONENT - 1) { + return Bmsb == 0; + } else { + return true; + } +} + +static inline bool _cc_N(compute_base_top)(_cc_bounds_bits bounds, _cc_addr_t cursor, _cc_addr_t* base_out, + _cc_length_t* top_out) { +#ifdef CC_IS_MORELLO + if (bounds.E > _CC_MAX_EXPONENT) { + bool valid = bounds.E == _CC_N(MAX_ENCODABLE_EXPONENT); + *base_out = 0; + *top_out = _CC_N(MAX_TOP); + return valid; + } + + // Remove flags bits + cursor = cursor & _CC_CURSOR_MASK; + // Sign extend + if (cursor & ((_CC_CURSOR_MASK >> 1) + 1)) + cursor |= ~_CC_CURSOR_MASK; +#endif + + // For the remaining computations we have to clamp E to max_E + // let E = min(maxE, unsigned(c.E)) in + uint8_t E = _CC_MIN(_CC_MAX_EXPONENT, bounds.E); + /* Extract bits we need to make the top correction and calculate representable limit */ + // let a3 = truncate(a >> (E + mantissa_width - 3), 3) in + // let B3 = truncateLSB(c.B, 3) in + // let T3 = truncateLSB(c.T, 3) in + unsigned a3 = (unsigned)_cc_N(truncate64)(cursor >> (E + _CC_MANTISSA_WIDTH - 3), 3); + unsigned B3 = (unsigned)_cc_truncateLSB(_CC_MANTISSA_WIDTH)(bounds.B, 3); + unsigned T3 = (unsigned)_cc_truncateLSB(_CC_MANTISSA_WIDTH)(bounds.T, 3); + // let R3 = B3 - 0b001 in /* wraps */ + unsigned R3 = (unsigned)_cc_N(truncate64)(B3 - 1, 3); // B3 == 0 ? 7 : B3 - 1; + /* Do address, base and top lie in the R aligned region above the one containing R? */ + // let aHi = if a3 <_u R3 then 1 else 0 in + // let bHi = if B3 <_u R3 then 1 else 0 in + // let tHi = if T3 <_u R3 then 1 else 0 in + int aHi = a3 < R3 ? 1 : 0; + int bHi = B3 < R3 ? 1 : 0; + int tHi = T3 < R3 ? 1 : 0; + + /* Compute region corrections for top and base relative to a */ + // let correction_base = bHi - aHi in + // let correction_top = tHi - aHi in + int correction_base = bHi - aHi; + int correction_top = tHi - aHi; + // Note: shifting by 64 is UB and causes wrong results -> clamp the shift value! + const unsigned a_top_shift = E + _CC_MANTISSA_WIDTH; + // let a_top = (a >> (E + mantissa_width)) in + _cc_addr_t a_top = a_top_shift >= _CC_ADDR_WIDTH ? 0 : cursor >> a_top_shift; + + // base : CapLenBits = truncate((a_top + correction_base) @ c.B @ zeros(E), cap_len_width); + _cc_length_t base = (_cc_addr_t)((int64_t)a_top + correction_base); + base <<= _CC_MANTISSA_WIDTH; + base |= bounds.B; + base <<= E; + base &= ((_cc_length_t)1 << _CC_LEN_WIDTH) - 1; + _cc_debug_assert((_cc_addr_t)(base >> _CC_ADDR_WIDTH) <= 1); // max 65/33 bits + // top : truncate((a_top + correction_top) @ c.T @ zeros(E), cap_len_width); + _cc_length_t top = (_cc_addr_t)((int64_t)a_top + correction_top); + top <<= _CC_MANTISSA_WIDTH; + top |= bounds.T; + top <<= E; + top &= ((_cc_length_t)1 << _CC_LEN_WIDTH) - 1; + _cc_debug_assert((_cc_addr_t)(top >> _CC_ADDR_WIDTH) <= 1); // max 65 bits + + /* If the base and top are more than an address space away from each other, + invert the MSB of top. This corrects for errors that happen when the + representable space wraps the address space. */ + // let base2 : bits(2) = 0b0 @ [base[cap_addr_width - 1]]; + // Note: we ignore the top bit of base here. If we don't we can get cases + // where setbounds/incoffset/etc. break monotonicity. + unsigned base2 = _cc_N(truncate64)(base >> (_CC_ADDR_WIDTH - 1), 1); + // let top2 : bits(2) = top[cap_addr_width .. cap_addr_width - 1]; + unsigned top2 = _cc_N(truncate64)(top >> (_CC_ADDR_WIDTH - 1), 2); + // if (E < (maxE - 1)) & (unsigned(top2 - base2) > 1) then { + // top[cap_addr_width] = ~(top[cap_addr_width]); + // }; + if (E < (_CC_MAX_EXPONENT - 1) && (top2 - base2) > 1) { + top = top ^ ((_cc_length_t)1 << _CC_ADDR_WIDTH); + } + + _cc_debug_assert((_cc_addr_t)(top >> _CC_ADDR_WIDTH) <= 1); // should be at most 1 bit over + // Check that base <= top for valid inputs + if (_cc_N(bounds_bits_valid)(bounds)) { + // Note: base can be > 2^64 for some (untagged) inputs with E near maxE + // It can also be > top for some (untagged) inputs. + _cc_debug_assert((_cc_addr_t)base <= top); + } else { + // _cc_debug_assert(!tagged && "Should not create invalid tagged capabilities"); + } + *base_out = (_cc_addr_t)base; // strip the (invalid) top bit + *top_out = top; + + return true; +} + +static inline void _cc_N(decompress_raw)(_cc_addr_t pesbt, _cc_addr_t cursor, bool tag, _cc_cap_t* cdp) { + cdp->cr_tag = tag; + cdp->_cr_cursor = cursor; + cdp->cr_pesbt = pesbt; + + _cc_bounds_bits bounds = _cc_N(extract_bounds_bits)(pesbt); + bool valid = _cc_N(compute_base_top)(bounds, cursor, &cdp->cr_base, &cdp->_cr_top); + cdp->cr_bounds_valid = valid; + cdp->cr_exp = bounds.E; + if (tag) { + _cc_debug_assert(cdp->cr_base <= _CC_N(MAX_ADDR)); +#ifndef CC_IS_MORELLO + // Morello is perfectly happy using settag to create capabilities with length greater than 2^64. + _cc_debug_assert(cdp->_cr_top <= _CC_N(MAX_TOP)); + _cc_debug_assert(cdp->cr_base <= cdp->_cr_top); +#endif + _cc_debug_assert(_CC_EXTRACT_FIELD(pesbt, RESERVED) == 0); + } +} + +/* + * Decompress a 128-bit capability. + */ +static inline void _cc_N(decompress_mem)(uint64_t pesbt, uint64_t cursor, bool tag, _cc_cap_t* cdp) { + _cc_N(decompress_raw)(pesbt ^ _CC_N(NULL_XOR_MASK), cursor, tag, cdp); +} + +static inline bool _cc_N(is_cap_sealed)(const _cc_cap_t* cp) { return _cc_N(get_otype)(cp) != _CC_N(OTYPE_UNSEALED); } + +// Update ebt bits in pesbt +static inline void _cc_N(update_ebt)(_cc_cap_t* csp, _cc_addr_t new_ebt) { + csp->cr_pesbt = (csp->cr_pesbt & ~_CC_N(FIELD_EBT_MASK64)) | new_ebt; +} + +/* + * Compress a capability to 128 bits. + * Note: if you have not been manually modifying fields, just access csp->cr_pesbt. + * cap_set_decompressed_X will set fields and keep pesbt in sync. + */ +static inline _cc_addr_t _cc_N(compress_raw)(const _cc_cap_t* csp) { + _cc_debug_assert((!csp->cr_tag || _cc_N(get_reserved)(csp) == 0) && + "Unknown reserved bits set it tagged capability"); + return csp->cr_pesbt; +} + +static inline _cc_addr_t _cc_N(compress_mem)(const _cc_cap_t* csp) { + return _cc_N(compress_raw)(csp) ^ _CC_N(NULL_XOR_MASK); +} + +/* + * Define the following to do the is_representable() check by simply + * compressing and decompressing the capability and checking to + * see if it is the same. + */ +// #define SIMPLE_REPRESENT_CHECK + +#ifndef SIMPLE_REPRESENT_CHECK +static inline bool _cc_N(all_ones)(uint64_t offset, uint32_t e, uint32_t bwidth) { + uint64_t Itop; + uint32_t shift = e + bwidth; + + if (shift >= 63) + return false; + Itop = offset >> shift; + return Itop == (0xfffffffffffffffful >> shift); +} + +static inline bool _cc_N(all_zeroes)(uint64_t offset, uint32_t e, uint32_t bwidth) { + uint32_t shift = e + bwidth; + uint64_t Itop; + + if (shift >= 63) + Itop = 0ul; + else + Itop = offset >> shift; + return Itop == 0ul; +} +#endif /* ! SIMPLE_REPRESENT_CHECK */ + +static bool _cc_N(fast_is_representable_new_addr)(bool sealed, _cc_addr_t base, _cc_length_t length, _cc_addr_t cursor, + _cc_addr_t new_cursor); + +/// Check that a capability is representable by compressing and recompressing +static inline bool _cc_N(is_representable_cap_exact)(const _cc_cap_t* cap) { + _cc_addr_t pesbt = _cc_N(compress_raw)(cap); + _cc_cap_t decompressed_cap; + _cc_N(decompress_raw)(pesbt, cap->_cr_cursor, cap->cr_tag, &decompressed_cap); + // These fields must not change: + _cc_debug_assert(decompressed_cap._cr_cursor == cap->_cr_cursor); + _cc_debug_assert(decompressed_cap.cr_pesbt == cap->cr_pesbt); + // If any of these fields changed then the capability is not representable: + if (decompressed_cap.cr_base != cap->cr_base || decompressed_cap._cr_top != cap->_cr_top) { + return false; + } + return true; +} + +static inline uint32_t _cc_N(compute_ebt)(_cc_addr_t req_base, _cc_length_t req_top, _cc_addr_t* alignment_mask, + bool* exact) { +#ifdef CC_IS_MORELLO + if (req_base == 0 && req_top == _CC_N(MAX_TOP)) { + *exact = true; + if (alignment_mask) + *alignment_mask = _CC_MAX_ADDR; + return _CC_N(RESET_EBT); + } +#else + _cc_debug_assert(req_base <= req_top && "Cannot invert base and top"); +#endif + /* + * With compressed capabilities we may need to increase the range of + * memory addresses to be wider than requested so it is + * representable. + */ + _cc_length_t req_length65 = req_top - req_base; + // function setCapBounds(cap, base, top) : (Capability, bits(64), bits(65)) -> (bool, Capability) = { + // /* {cap with base=base; length=(bits(64)) length; offset=0} */ + // let base65 = 0b0 @ base; + // let length = top - base65; + // /* Find an exponent that will put the most significant bit of length + // second from the top as assumed during decoding. We ignore the bottom + // MW - 1 bits because those are handled by the ie = 0 format. */ + // let e = 52 - CountLeadingZeros(length[64..13]); + uint8_t E = (uint8_t)_cc_N(get_exponent)(req_length65); + const uint64_t req_length64 = (uint64_t)req_length65; + // Use internal exponent if e is non-zero or if e is zero but + // but the implied bit of length is not zero (denormal vs. normal case) + // let ie = (e != 0) | length[12]; + // + const bool InternalExponent = E != 0 || _cc_N(getbits)(req_length64, _CC_BOT_INTERNAL_EXP_WIDTH + 1, 1); + if (!InternalExponent) { + // /* The non-ie e == 0 case is easy. It is exact so just extract relevant bits. */ + // Bbits = truncate(base, 14); + // Tbits = truncate(top, 14); + // lostSignificantTop : bool = false; + // lostSignificantBase : bool = false; + // incE : bool = false; + uint32_t ebt_bits = _CC_ENCODE_EBT_FIELD(0, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(req_top, EXP_ZERO_TOP) | + _CC_ENCODE_EBT_FIELD(req_base, EXP_ZERO_BOTTOM); +#ifdef CC_IS_MORELLO + // Due to morello conditionally inverting bits, we need to invert the bits that would be an internal exponent + // here + ebt_bits ^= _CC_ENCODE_EBT_FIELD(~0, EXPONENT_HIGH_PART) | _CC_ENCODE_EBT_FIELD(~0, EXPONENT_LOW_PART); +#endif + if (alignment_mask) + *alignment_mask = _CC_MAX_ADDR; // no adjustment to base required + *exact = true; + return ebt_bits; /* Exactly representable */ + } + // Handle IE case: + // if ie then { + // /* the internal exponent case is trickier */ + // + // /* Extract B and T bits (we lose 3 bits of each to store the exponent) */ + // B_ie = truncate(base >> (e + 3), 11); + // T_ie = truncate(top >> (e + 3), 11); + // + _cc_addr_t bot_ie = _cc_N(truncate64)(req_base >> (E + _CC_EXP_LOW_WIDTH), _CC_BOT_INTERNAL_EXP_WIDTH); + if (alignment_mask) { + *alignment_mask = UINT64_MAX << (E + _CC_EXP_LOW_WIDTH); + } + _cc_addr_t top_ie = _cc_N(truncate64)((_cc_addr_t)(req_top >> (E + _CC_EXP_LOW_WIDTH)), _CC_BOT_INTERNAL_EXP_WIDTH); + // /* Find out whether we have lost significant bits of base and top using a + // mask of bits that we will lose (including 3 extra for exp). */ + // maskLo : bits(65) = zero_extend(replicate_bits(0b1, e + 3)); + // z65 : bits(65) = zeros(); + // lostSignificantBase = (base65 & maskLo) != z65; + // lostSignificantTop = (top & maskLo) != z65; + // TODO: stop using _cc_length_t and just handle bit65 set specially? + const _cc_length_t maskLo = (((_cc_length_t)1u) << (E + _CC_EXP_LOW_WIDTH)) - 1; + const _cc_length_t zero65 = 0; + bool lostSignificantBase = (req_base & maskLo) != zero65; + bool lostSignificantTop = (req_top & maskLo) != zero65; + // if lostSignificantTop then { + // /* we must increment T to make sure it is still above top even with lost bits. + // It might wrap around but if that makes B> (e + 4), 11); + // let incT : range(0,1) = if lostSignificantTop then 1 else 0; + // T_ie = truncate(top >> (e + 4), 11) + incT; + // }; + const _cc_addr_t len_ie = _cc_N(truncate64)(top_ie - bot_ie, _CC_BOT_INTERNAL_EXP_WIDTH); + bool incE = false; + if (_cc_N(getbits)(len_ie, _CC_BOT_INTERNAL_EXP_WIDTH - 1, 1)) { + incE = true; + lostSignificantBase = lostSignificantBase || _cc_N(getbits)(bot_ie, 0, 1); + lostSignificantTop = lostSignificantTop || _cc_N(getbits)(top_ie, 0, 1); + bot_ie = _cc_N(truncate64)(req_base >> (E + _CC_EXP_LOW_WIDTH + 1), _CC_BOT_INTERNAL_EXP_WIDTH); + // If we had to adjust bot_ie (shift by one more) also update alignment_mask + if (alignment_mask) { + *alignment_mask = UINT64_MAX << (E + _CC_EXP_LOW_WIDTH + 1); + } + const bool incT = lostSignificantTop; + top_ie = _cc_N(truncate64)((_cc_addr_t)(req_top >> (E + _CC_EXP_LOW_WIDTH + 1)), _CC_BOT_INTERNAL_EXP_WIDTH); + if (incT) { + top_ie = _cc_N(truncate64)(top_ie + 1, _CC_BOT_INTERNAL_EXP_WIDTH); + } + } + // + // Bbits = B_ie @ 0b000; + // Tbits = T_ie @ 0b000; + const _cc_addr_t Bbits = bot_ie << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE); + const _cc_addr_t Tbits = top_ie << _CC_N(FIELD_EXPONENT_LOW_PART_SIZE); + const uint8_t newE = E + (incE ? 1 : 0); + + // }; + // let exact = not(lostSignificantBase | lostSignificantTop); + *exact = !lostSignificantBase && !lostSignificantTop; + // Split E between T and B + const _cc_addr_t expHighBits = + _cc_N(getbits)(newE >> _CC_N(FIELD_EXPONENT_LOW_PART_SIZE), 0, _CC_N(FIELD_EXPONENT_HIGH_PART_SIZE)); + const _cc_addr_t expLowBits = _cc_N(getbits)(newE, 0, _CC_N(FIELD_EXPONENT_LOW_PART_SIZE)); + const _cc_addr_t Te = Tbits | expHighBits; + const _cc_addr_t Be = Bbits | expLowBits; + return _CC_ENCODE_EBT_FIELD(1, INTERNAL_EXPONENT) | _CC_ENCODE_EBT_FIELD(Te, TOP_ENCODED) | + _CC_ENCODE_EBT_FIELD(Be, BOTTOM_ENCODED); +} + +/* + * Check to see if a memory region is representable by a compressed + * capability. It is representable if: + * + * representable = (inRange && inLimits) || (E >= 44) + * + * where: + * + * E = compression exponent (see _cc_N(compute_e)() above) + * + * inRange = -s < i < s where i is the increment (or offset) + * (In other words, all the bits of i<63, E+20> are the same.) + * + * inLimits = (i < 0) ? (Imid >= (R - Amid)) && (R != Amid) : (R - Amid - 1) + * where Imid = i, Amid = a, R = B - 2^12 and a = + * base + offset. + */ +static inline bool _cc_N(is_representable_new_addr)(bool sealed, _cc_addr_t base, _cc_length_t length, + _cc_addr_t cursor, _cc_addr_t new_cursor) { + _cc_length_t top = (_cc_length_t)base + length; + + cursor &= _CC_CURSOR_MASK; + new_cursor &= _CC_CURSOR_MASK; + + // in-bounds capabilities are always representable + if (__builtin_expect(new_cursor >= base && new_cursor < top, true)) { + return true; + } + +#if defined(_CC_USE_FAST_REP_CHECK) + const bool slow_representable_check = false; +#else + const bool slow_representable_check = true; +#endif + + if (slow_representable_check) { + /* Simply compress and uncompress to check. */ + _cc_cap_t c; + memset(&c, 0, sizeof(c)); + c.cr_base = base; + c._cr_top = top; + c._cr_cursor = cursor; + // important to set as compress assumes this is in bounds + c.cr_pesbt = _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) | _CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) | + _CC_ENCODE_FIELD(sealed ? 42 : _CC_N(OTYPE_UNSEALED), OTYPE); + /* Get an EBT */ + bool exact_input = false; + _cc_N(update_ebt)(&c, _cc_N(compute_ebt)(base, top, NULL, &exact_input)); + // Looks like this assert gets hit by negative length capabilities. Probably the "exact input" return is wrong. + if (top > (_cc_length_t)base) + _cc_debug_assert(exact_input && + "Input capability bounds not representable? They should have been rounded before!"); + /* Check with new cursor */ + c._cr_cursor = new_cursor; + return _cc_N(is_representable_cap_exact)(&c); + } else { + return _cc_N(fast_is_representable_new_addr)(sealed, base, length, cursor, new_cursor); + } +} + +static inline _cc_addr_t _cc_N(cap_bounds_address)(const _cc_cap_t* cap) { + // Remove flags bits + _cc_addr_t cursor = cap->_cr_cursor & _CC_CURSOR_MASK; + // Sign extend + if (cursor & ((_CC_CURSOR_MASK >> 1) + 1)) + cursor |= ~_CC_CURSOR_MASK; + return cursor; +} + +// This should only be used on decompressed caps, as it relies on the exp field +static inline bool _cc_N(cap_bounds_uses_value)(const _cc_cap_t* cap) { + return cap->cr_exp < (sizeof(_cc_addr_t) * 8) - _CC_N(FIELD_BOTTOM_ENCODED_SIZE); +} + +static inline bool _cc_N(cap_sign_change)(_cc_addr_t addr1, _cc_addr_t addr2) { +#ifdef CC_IS_MORELLO + return ((addr1 ^ addr2) & (1ULL << (63 - MORELLO_FLAG_BITS))); +#else + (void)addr1; + (void)addr2; + return false; +#endif +} + +static inline bool _cc_N(cap_sign_change_causes_unrepresentability)(const _cc_cap_t* cap, _cc_addr_t addr1, + _cc_addr_t addr2) { + return _cc_N(cap_sign_change)(addr1, addr2) && _cc_N(cap_bounds_uses_value)(cap); +} + +static inline bool _cc_N(is_representable_with_addr)(const _cc_cap_t* cap, _cc_addr_t new_addr) { + new_addr &= _CC_CURSOR_MASK; +#ifdef CC_IS_MORELLO + // If the top bit is changed on morello this can change bounds + if (_cc_N(cap_sign_change_causes_unrepresentability)(cap, new_addr, cap->_cr_cursor)) { + return false; + } + if (!cap->cr_bounds_valid) + return false; +#endif + // in-bounds capabilities are otherwise always representable + if (__builtin_expect(new_addr >= cap->cr_base && new_addr < cap->_cr_top, true)) { + return true; + } + const _cc_length_t length = cap->_cr_top - cap->cr_base; + return _cc_N(is_representable_new_addr)(_cc_N(is_cap_sealed)(cap), cap->cr_base, length, cap->_cr_cursor, new_addr); +} + +static bool _cc_N(fast_is_representable_new_addr)(bool sealed, _cc_addr_t base, _cc_length_t length, _cc_addr_t cursor, + _cc_addr_t new_cursor) { + (void)sealed; + uint32_t bwidth = _CC_MANTISSA_WIDTH; + + cursor &= _CC_CURSOR_MASK; + new_cursor &= _CC_CURSOR_MASK; + + uint32_t highest_exp = (_CC_ADDR_WIDTH - bwidth + 2); + + _cc_length_t top = base + length; + + if (top == _CC_MAX_TOP && base == 0) { + return true; // 1 << 65 is always representable + } + if (length == 0) { + return true; // length 0 is always representable + } + + uint32_t e = _cc_N(get_exponent)(length); + + int64_t b, r, Imid, Amid; + bool inRange, inLimits; + int64_t inc = (int64_t)(new_cursor - cursor); + +#define MOD_MASK ((UINT64_C(1) << bwidth) - UINT64_C(1)) + + /* Check for the boundary cases. */ + + b = (int64_t)((base >> e) & MOD_MASK); + Imid = (int64_t)((uint64_t)(inc >> e) & MOD_MASK); + Amid = (int64_t)(((cursor) >> e) & MOD_MASK); + + r = (int64_t)(((uint64_t)((b >> (bwidth - 3)) - 1) << (bwidth - 3)) & MOD_MASK); + + /* inRange, test if bits are all the same */ + inRange = _cc_N(all_ones)((uint64_t)inc, e, bwidth) || _cc_N(all_zeroes)((uint64_t)inc, e, bwidth); + + /* inLimits */ + if (inc >= 0) { + inLimits = ((uint64_t)Imid < (((uint64_t)(r - Amid - 1l)) & MOD_MASK)); + } else { + inLimits = ((uint64_t)Imid >= (((uint64_t)(r - Amid)) & MOD_MASK)) && (r != Amid); + } +#undef MOD_MASK + + return ((inRange && inLimits) || (e >= highest_exp - 2)); +} + +/* @return whether the operation was able to set precise bounds precise or not */ +static inline bool _cc_N(setbounds_impl)(_cc_cap_t* cap, _cc_addr_t req_base, _cc_length_t req_top, + _cc_addr_t* alignment_mask) { +#ifdef CC_IS_MORELLO + if (!cap->cr_bounds_valid) { + cap->cr_tag = 0; + } + bool from_large = !_cc_N(cap_bounds_uses_value)(cap); +#else + // Morello allows setbounds to do weird things and will just result in untagged results + _cc_debug_assert((cap->cr_tag) && "Cannot be used on untagged capabilities"); + _cc_debug_assert((!_cc_N(is_cap_sealed)(cap)) && "Cannot be used on sealed capabilities"); +#endif + _cc_debug_assert(req_base <= req_top && "Cannot invert base and top"); + /* + * With compressed capabilities we may need to increase the range of + * memory addresses to be wider than requested so it is + * representable. + */ + const _cc_addr_t cursor = cap->_cr_cursor; +#ifndef CC_IS_MORELLO + _cc_debug_assert(((cap->_cr_top - cap->cr_base) >> _CC_ADDR_WIDTH) <= 1 && "Length must be smaller than 1 << 65"); + _cc_debug_assert((req_top >> _CC_ADDR_WIDTH) <= 1 && "New top must be smaller than 1 << 65"); + _cc_debug_assert(req_base >= cap->cr_base && "Cannot decrease base"); + _cc_debug_assert(req_top <= cap->_cr_top && "Cannot increase top"); + assert((cursor < cap->_cr_top || (cursor == cap->_cr_top && req_base == cap->_cr_top && req_base == req_top)) && + "Must be used on inbounds caps or request zero-length cap at top"); + assert((cursor >= cap->cr_base) && "Must be used on inbounds caps"); +#endif + _CC_STATIC_ASSERT(_CC_EXP_LOW_WIDTH == 3, "expected 3 bits to be used by"); // expected 3 bits to + _CC_STATIC_ASSERT(_CC_EXP_HIGH_WIDTH == 3, "expected 3 bits to be used by"); // expected 3 bits to + bool exact = false; + uint32_t new_ebt = _cc_N(compute_ebt)(req_base, req_top, alignment_mask, &exact); + + // TODO: find a faster way to compute top and bot: + const _cc_addr_t pesbt = _CC_ENCODE_FIELD(0, UPERMS) | _CC_ENCODE_FIELD(0, HWPERMS) | + _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE) | _CC_ENCODE_FIELD(new_ebt, EBT); + _cc_cap_t new_cap; + _cc_N(decompress_raw)(pesbt, cursor, cap->cr_tag, &new_cap); + _cc_addr_t new_base = new_cap.cr_base; + _cc_length_t new_top = new_cap._cr_top; + + if (exact) { +#ifndef CC_IS_MORELLO + // Morello considers a setbounds that takes a capability from "large" (non-sign extended bounds) to "small" + // to still be exact, even if this results in a change in requested bounds. The exact assert would be tedious + // to express so I have turned it off for morello. + _cc_debug_assert(new_base == req_base && "Should be exact"); + _cc_debug_assert(new_top == req_top && "Should be exact"); +#endif + } else { + _cc_debug_assert((new_base != req_base || new_top != req_top) && + "Was inexact, but neither base nor top different?"); + } + + _cc_debug_assert(new_top >= new_base); + _cc_debug_assert((!cap->cr_tag || _cc_N(get_reserved)(cap) == 0) && + "Unknown reserved bits set in tagged capability"); + cap->cr_base = new_base; + cap->_cr_top = new_top; + cap->cr_exp = new_cap.cr_exp; + _cc_N(update_ebt)(cap, new_ebt); +#ifdef CC_IS_MORELLO + cap->cr_bounds_valid = new_cap.cr_bounds_valid; + bool to_small = _cc_N(cap_bounds_uses_value)(cap); + // On morello we may end up with a length that could have been exact, but has changed the flag bits. + if ((from_large && to_small) && ((new_base ^ req_base) >> (64 - MORELLO_FLAG_BITS))) { + cap->cr_tag = 0; + } +#endif + + // let newCap = {cap with address=base, E=to_bits(6, if incE then e + 1 else e), B=Bbits, T=Tbits, internal_e=ie}; + // (exact, newCap) + return exact; +} + +/* @return whether the operation was able to set precise bounds precise or not */ +static inline bool _cc_N(setbounds)(_cc_cap_t* cap, _cc_addr_t req_base, _cc_length_t req_top) { + return _cc_N(setbounds_impl)(cap, req_base, req_top, NULL); +} + +/* @return the mask that needs to be applied to base in order to get a precisely representable capability */ +static inline _cc_addr_t _cc_N(get_alignment_mask)(_cc_addr_t req_length) { + if (req_length == 0) { + // With a lenght of zero we know it is precise so we can just return an + // all ones mask. + // This avoids undefined behaviour when counting most significant bit later. + return _CC_MAX_ADDR; + } + // To compute the mask we set bounds on a maximum permissions capability and + // return the mask that was used to adjust the length + _cc_cap_t tmpcap; + memset(&tmpcap, 0, sizeof(tmpcap)); + tmpcap.cr_tag = 1; + tmpcap._cr_top = _CC_MAX_TOP; + _cc_N(update_otype)(&tmpcap, _CC_N(OTYPE_UNSEALED)); + _cc_N(update_ebt)(&tmpcap, _CC_N(RESET_EBT)); + _cc_addr_t mask = 0; + _cc_N(setbounds_impl)(&tmpcap, 0, req_length, &mask); + return mask; +} + +static inline _cc_cap_t _cc_N(make_max_perms_cap)(_cc_addr_t base, _cc_addr_t cursor, _cc_length_t top) { + _cc_cap_t creg; + memset(&creg, 0, sizeof(creg)); + assert(base <= top && "Invalid arguments"); + creg.cr_base = base; + creg._cr_cursor = cursor; + creg.cr_bounds_valid = true; + creg._cr_top = top; + creg.cr_pesbt = _CC_ENCODE_FIELD(_CC_N(UPERMS_ALL), UPERMS) | _CC_ENCODE_FIELD(_CC_N(PERMS_ALL), HWPERMS) | + _CC_ENCODE_FIELD(_CC_N(OTYPE_UNSEALED), OTYPE); + creg.cr_tag = true; + creg.cr_exp = _CC_N(NULL_EXP); + bool exact_input = false; + _cc_N(update_ebt)(&creg, _cc_N(compute_ebt)(creg.cr_base, creg._cr_top, NULL, &exact_input)); + assert(exact_input && "Invalid arguments"); + assert(_cc_N(is_representable_cap_exact)(&creg)); + return creg; +} + +static inline _cc_addr_t _cc_N(get_required_alignment)(_cc_addr_t req_length) { + // To get the required alignment from the CRAM mask we can just invert + // the bits and add one to get a power-of-two + return ~_cc_N(get_alignment_mask)(req_length) + 1; +} + +static inline _cc_addr_t _cc_N(get_representable_length)(_cc_addr_t req_length) { + _cc_addr_t mask = _cc_N(get_alignment_mask)(req_length); + return (req_length + ~mask) & mask; +} + +/// Provide a C++ class with the same function names +/// to simplify writing code that handles both 128 and 64-bit capabilities +#ifdef __cplusplus +inline bool _cc_N(cap)::operator==(const _cc_N(cap) & other) const { return _cc_N(exactly_equal)(this, &other); } + +class _CC_CONCAT(CompressedCap, CC_FORMAT_LOWER) { +public: + using length_t = _cc_length_t; + using offset_t = _cc_offset_t; + using addr_t = _cc_addr_t; + using cap_t = _cc_cap_t; + using bounds_bits = _cc_bounds_bits; + + static inline addr_t compress_raw(const cap_t* csp) { return _cc_N(compress_raw)(csp); } + static inline void decompress_raw(addr_t pesbt, addr_t cursor, bool tag, cap_t* cdp) { + _cc_N(decompress_raw)(pesbt, cursor, tag, cdp); + } + static inline addr_t compress_mem(const cap_t* csp) { return _cc_N(compress_mem)(csp); } + static inline void decompress_mem(addr_t pesbt, addr_t cursor, bool tag, cap_t* cdp) { + _cc_N(decompress_mem)(pesbt, cursor, tag, cdp); + } + static inline bounds_bits extract_bounds_bits(addr_t pesbt) { return _cc_N(extract_bounds_bits)(pesbt); } + static inline bool setbounds(cap_t* cap, addr_t req_base, length_t req_top) { + return _cc_N(setbounds)(cap, req_base, req_top); + } + static inline bool is_representable_cap_exact(const cap_t* cap) { return _cc_N(is_representable_cap_exact)(cap); } + static inline cap_t make_max_perms_cap(addr_t base, addr_t cursor, length_t top) { + return _cc_N(make_max_perms_cap)(base, cursor, top); + } + static inline addr_t representable_length(addr_t len) { return _cc_N(get_representable_length)(len); } + static inline addr_t representable_mask(addr_t len) { return _cc_N(get_alignment_mask)(len); } +}; +#define CompressedCapCC _CC_CONCAT(CompressedCap, CC_FORMAT_LOWER) +#endif diff --git a/gdb/cheri-compressed-cap/cheri_compressed_cap_macros.h b/gdb/cheri-compressed-cap/cheri_compressed_cap_macros.h new file mode 100644 index 00000000000..74324ef4d18 --- /dev/null +++ b/gdb/cheri-compressed-cap/cheri_compressed_cap_macros.h @@ -0,0 +1,99 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018-2020 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the + * DARPA SSITH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef _cc_debug_assert +#ifdef cheri_debug_assert +#define _cc_debug_assert(cond) cheri_debug_assert(cond) +#else +#include +#define _cc_debug_assert(cond) assert(cond) +#endif +#endif + +#ifndef _CC_CONCAT +#define _CC_CONCAT1(x, y) x##y +#define _CC_CONCAT(x, y) _CC_CONCAT1(x, y) +#define _CC_EXPAND1(x) x +#define _CC_EXPAND(x) _CC_EXPAND1(x) + +#ifdef __cplusplus +// Some versions of GCC dont't like _Static_assert() in C++ mode +#define _CC_STATIC_ASSERT(cond, msg) static_assert(cond, msg) +#else +#define _CC_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) +#endif + +#define _cc_N(name) _CC_CONCAT(_CC_CONCAT(_CC_CONCAT(cc, CC_FORMAT_LOWER), _), name) +#define _CC_N(name) _CC_CONCAT(_CC_CONCAT(_CC_CONCAT(CC, CC_FORMAT_UPPER), _), name) + +#define _CC_BITMASK64(nbits) ((UINT64_C(1) << (nbits)) - UINT64_C(1)) + +// NB: Do not use GNU statement expressions as this is used by LLVM which warns +// on any uses during its build. These are therefore unsafe if any arguments +// have side-effects. +#define _CC_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define _CC_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define _CC_FIELD(name, last, start) \ + _CC_N(FIELD_##name##_START) = (start - _CC_N(ADDR_WIDTH)), \ + _CC_N(FIELD_##name##_LAST) = (last - _CC_N(ADDR_WIDTH)), \ + _CC_N(FIELD_##name##_SIZE) = _CC_N(FIELD_##name##_LAST) - _CC_N(FIELD_##name##_START) + 1, \ + _CC_N(FIELD_##name##_MASK_NOT_SHIFTED) = _CC_BITMASK64(_CC_N(FIELD_##name##_SIZE)), \ + _CC_N(FIELD_##name##_MASK64) = (uint64_t)_CC_N(FIELD_##name##_MASK_NOT_SHIFTED) << _CC_N(FIELD_##name##_START), \ + _CC_N(FIELD_##name##_MAX_VALUE) = _CC_N(FIELD_##name##_MASK_NOT_SHIFTED) + +#define _CC_ENCODE_FIELD(value, name) \ + ((uint64_t)((value)&_CC_N(FIELD_##name##_MAX_VALUE)) << _CC_N(FIELD_##name##_START)) + +#define _CC_EXTRACT_FIELD(value, name) _cc_N(getbits)((value), _CC_N(FIELD_##name##_START), _CC_N(FIELD_##name##_SIZE)) + +#define _CC_ENCODE_EBT_FIELD(value, name) \ + ((uint64_t)((value)&_CC_N(FIELD_##name##_MAX_VALUE)) << (_CC_N(FIELD_##name##_START) + _CC_N(FIELD_EBT_START))) + +#define _CC_SPECIAL_OTYPE(name, val) \ + _CC_N(name) = (_CC_N(SPECIAL_OTYPE_VAL)(val)), _CC_N(name##_SIGNED) = (_CC_N(SPECIAL_OTYPE_VAL_SIGNED)(val)) + +#ifdef __cplusplus +template static constexpr bool check_same() { + static_assert(a == b, ""); + return true; +} +#define _CC_STATIC_ASSERT_SAME(a, b) static_assert(check_same(), "") +#else +#define _CC_STATIC_ASSERT_SAME(a, b) _Static_assert((a) == (b), "") +#endif + +#endif // _CC_CONCAT diff --git a/gdb/cheri-compressed-cap/decompress_cap_128.c b/gdb/cheri-compressed-cap/decompress_cap_128.c new file mode 100644 index 00000000000..975175cc335 --- /dev/null +++ b/gdb/cheri-compressed-cap/decompress_cap_128.c @@ -0,0 +1,110 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cheri_compressed_cap.h" +// #include "cheri_compressed_cap.h" +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE +#include "test/sail_wrapper.h" +#endif + +static const char* otype_suffix(uint32_t otype) { + switch (otype) { +#define OTYPE_CASE(Name, ...) \ + case CC128_##Name: return " (CC128_" #Name ")"; + CC128_LS_SPECIAL_OTYPES(OTYPE_CASE, ) + default: return ""; + } +} + +static void dump_cap_fields(const cc128_cap_t* result) { + fprintf(stderr, "Permissions: 0x%" PRIx32 "\n", cc128_get_perms(result)); // TODO: decode perms + fprintf(stderr, "User Perms: 0x%" PRIx32 "\n", cc128_get_uperms(result)); + fprintf(stderr, "Base: 0x%016" PRIx64 "\n", result->cr_base); + fprintf(stderr, "Offset: 0x%016" PRIx64 "\n", result->_cr_cursor - result->cr_base); + fprintf(stderr, "Cursor: 0x%016" PRIx64 "\n", result->_cr_cursor); + cc128_length_t length = result->_cr_top - result->cr_base; + fprintf(stderr, "Length: 0x%" PRIx64 "%016" PRIx64 " %s\n", (uint64_t)(length >> 64), (uint64_t)length, + length > UINT64_MAX ? " (greater than UINT64_MAX)" : ""); + cc128_length_t top_full = result->_cr_top; + fprintf(stderr, "Top: 0x%" PRIx64 "%016" PRIx64 " %s\n", (uint64_t)(top_full >> 64), (uint64_t)top_full, + top_full > UINT64_MAX ? " (greater than UINT64_MAX)" : ""); + fprintf(stderr, "Sealed: %d\n", cc128_is_cap_sealed(result) ? 1 : 0); + uint32_t otype = cc128_get_otype(result); + fprintf(stderr, "OType: 0x%" PRIx32 "%s\n", otype, otype_suffix(otype)); + fprintf(stderr, "Flags: 0x%" PRIx8 "\n", cc128_get_flags(result)); + fprintf(stderr, "Reserved: 0x%" PRIx8 "\n", cc128_get_reserved(result)); + fprintf(stderr, "Valid decompress: %s", result->cr_bounds_valid ? "yes" : "no"); + fprintf(stderr, "\n"); +} + +int main(int argc, char** argv) { + //fprintf(stderr, "CC128_NULL_XOR_MASK=0x%llx\n", (long long)CC128_NULL_XOR_MASK); + //fprintf(stderr, "CC128_NULL_PESBT =0x%llx\n", (long long)CC128_NULL_PESBT); + if (argc < 3) { + fprintf(stderr, "Usage: %s PESBT CURSOR\n", argv[0]); + return EXIT_FAILURE; + } + errno = 0; + char* end; + uint64_t pesbt = strtoull(argv[1], &end, 16); + if (errno != 0 || !end || *end != '\0') { + err(EX_DATAERR, "pesbt not a valid hex number: %s", argv[1]); + } + uint64_t cursor = strtoull(argv[2], &end, 16); + if (errno != 0 || !end || *end != '\0') { + err(EX_DATAERR, "cursor not a valid hex number: %s", argv[2]); + } + cc128_cap_t result; + memset(&result, 0, sizeof(result)); + printf("Decompressing pesbt = %016" PRIx64 ", cursor = %016" PRIx64 "\n", pesbt, cursor); +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE + sail_decode_128_mem(pesbt, cursor, false, &result); +#else + cc128_decompress_mem(pesbt, cursor, false, &result); +#endif + dump_cap_fields(&result); +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE + uint64_t rt_pesbt = sail_compress_128_mem(&result); +#else + uint64_t rt_pesbt = cc128_compress_mem(&result); +#endif + printf("Re-compressed pesbt = %016" PRIx64 "%s\n", rt_pesbt, pesbt == rt_pesbt ? "" : " - WAS DESTRUCTIVE"); + return EXIT_SUCCESS; +} diff --git a/gdb/cheri-compressed-cap/decompress_cap_128m.c b/gdb/cheri-compressed-cap/decompress_cap_128m.c new file mode 100644 index 00000000000..8b9c1aadade --- /dev/null +++ b/gdb/cheri-compressed-cap/decompress_cap_128m.c @@ -0,0 +1,110 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Alex Richardson + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cheri_compressed_cap.h" +// #include "cheri_compressed_cap.h" +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE +#include "test/sail_wrapper.h" +#endif + +static const char* otype_suffix(uint32_t otype) { + switch (otype) { +#define OTYPE_CASE(Name, ...) \ + case CC128M_##Name: return " (CC128M_" #Name ")"; + CC128M_LS_SPECIAL_OTYPES(OTYPE_CASE, ) + default: return ""; + } +} + +static void dump_cap_fields(const cc128m_cap_t* result) { + fprintf(stderr, "Permissions: 0x%" PRIx32 "\n", cc128m_get_perms(result)); // TODO: decode perms + fprintf(stderr, "User Perms: 0x%" PRIx32 "\n", cc128m_get_uperms(result)); + fprintf(stderr, "Base: 0x%016" PRIx64 "\n", result->cr_base); + fprintf(stderr, "Offset: 0x%016" PRIx64 "\n", result->_cr_cursor - result->cr_base); + fprintf(stderr, "Cursor: 0x%016" PRIx64 "\n", result->_cr_cursor); + cc128m_length_t length = result->_cr_top - result->cr_base; + fprintf(stderr, "Length: 0x%" PRIx64 "%016" PRIx64 " %s\n", (uint64_t)(length >> 64), (uint64_t)length, + length > UINT64_MAX ? " (greater than UINT64_MAX)" : ""); + cc128m_length_t top_full = result->_cr_top; + fprintf(stderr, "Top: 0x%" PRIx64 "%016" PRIx64 " %s\n", (uint64_t)(top_full >> 64), (uint64_t)top_full, + top_full > UINT64_MAX ? " (greater than UINT64_MAX)" : ""); + fprintf(stderr, "Sealed: %d\n", cc128m_is_cap_sealed(result) ? 1 : 0); + uint32_t otype = cc128m_get_otype(result); + fprintf(stderr, "OType: 0x%" PRIx32 "%s\n", otype, otype_suffix(otype)); + fprintf(stderr, "Flags: 0x%" PRIx8 "\n", cc128m_get_flags(result)); + fprintf(stderr, "Reserved: 0x%" PRIx8 "\n", cc128m_get_reserved(result)); + fprintf(stderr, "Valid decompress: %s", result->cr_bounds_valid ? "yes" : "no"); + fprintf(stderr, "\n"); +} + +int main(int argc, char** argv) { + //fprintf(stderr, "CC128M_NULL_XOR_MASK=0x%llx\n", (long long)CC128M_NULL_XOR_MASK); + //fprintf(stderr, "CC128M_NULL_PESBT =0x%llx\n", (long long)CC128M_NULL_PESBT); + if (argc < 3) { + fprintf(stderr, "Usage: %s PESBT CURSOR\n", argv[0]); + return EXIT_FAILURE; + } + errno = 0; + char* end; + uint64_t pesbt = strtoull(argv[1], &end, 16); + if (errno != 0 || !end || *end != '\0') { + err(EX_DATAERR, "pesbt not a valid hex number: %s", argv[1]); + } + uint64_t cursor = strtoull(argv[2], &end, 16); + if (errno != 0 || !end || *end != '\0') { + err(EX_DATAERR, "cursor not a valid hex number: %s", argv[2]); + } + cc128m_cap_t result; + memset(&result, 0, sizeof(result)); + printf("Decompressing pesbt = %016" PRIx64 ", cursor = %016" PRIx64 "\n", pesbt, cursor); +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE + sail_decode_128m_mem(pesbt, cursor, false, &result); +#else + cc128m_decompress_mem(pesbt, cursor, false, &result); +#endif + dump_cap_fields(&result); +#ifdef DECOMPRESS_WITH_SAIL_GENERATED_CODE + uint64_t rt_pesbt = sail_compress_128m_mem(&result); +#else + uint64_t rt_pesbt = cc128m_compress_mem(&result); +#endif + printf("Re-compressed pesbt = %016" PRIx64 "%s\n", rt_pesbt, pesbt == rt_pesbt ? "" : " - WAS DESTRUCTIVE"); + return EXIT_SUCCESS; +} diff --git a/gdb/cheri-compressed-cap/test/FuzzedDataProvider.h b/gdb/cheri-compressed-cap/test/FuzzedDataProvider.h new file mode 100644 index 00000000000..f7018d71e2b --- /dev/null +++ b/gdb/cheri-compressed-cap/test/FuzzedDataProvider.h @@ -0,0 +1,299 @@ +//===- FuzzedDataProvider.h - Utility header for fuzz targets ---*- C++ -* ===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// A single header library providing an utility class to break up an array of +// bytes. Whenever run on the same input, provides the same output, as long as +// its methods are called in the same order, with the same arguments. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ +#define LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// In addition to the comments below, the API is also briefly documented at +// https://github.com/google/fuzzing/blob/master/docs/split-inputs.md#fuzzed-data-provider +class FuzzedDataProvider { +public: + // |data| is an array of length |size| that the FuzzedDataProvider wraps to + // provide more granular access. |data| must outlive the FuzzedDataProvider. + FuzzedDataProvider(const uint8_t *data, size_t size) + : data_ptr_(data), remaining_bytes_(size) {} + ~FuzzedDataProvider() = default; + + // Returns a std::vector containing |num_bytes| of input data. If fewer than + // |num_bytes| of data remain, returns a shorter std::vector containing all + // of the data that's left. Can be used with any byte sized type, such as + // char, unsigned char, uint8_t, etc. + template std::vector ConsumeBytes(size_t num_bytes) { + num_bytes = std::min(num_bytes, remaining_bytes_); + return ConsumeBytes(num_bytes, num_bytes); + } + + // Similar to |ConsumeBytes|, but also appends the terminator value at the end + // of the resulting vector. Useful, when a mutable null-terminated C-string is + // needed, for example. But that is a rare case. Better avoid it, if possible, + // and prefer using |ConsumeBytes| or |ConsumeBytesAsString| methods. + template + std::vector ConsumeBytesWithTerminator(size_t num_bytes, + T terminator = 0) { + num_bytes = std::min(num_bytes, remaining_bytes_); + std::vector result = ConsumeBytes(num_bytes + 1, num_bytes); + result.back() = terminator; + return result; + } + + // Returns a std::string containing |num_bytes| of input data. Using this and + // |.c_str()| on the resulting string is the best way to get an immutable + // null-terminated C string. If fewer than |num_bytes| of data remain, returns + // a shorter std::string containing all of the data that's left. + std::string ConsumeBytesAsString(size_t num_bytes) { + static_assert(sizeof(std::string::value_type) == sizeof(uint8_t), + "ConsumeBytesAsString cannot convert the data to a string."); + + num_bytes = std::min(num_bytes, remaining_bytes_); + std::string result( + reinterpret_cast(data_ptr_), + num_bytes); + Advance(num_bytes); + return result; + } + + // Returns a number in the range [min, max] by consuming bytes from the + // input data. The value might not be uniformly distributed in the given + // range. If there's no input data left, always returns |min|. |min| must + // be less than or equal to |max|. + template T ConsumeIntegralInRange(T min, T max) { + static_assert(std::is_integral::value, "An integral type is required."); + static_assert(sizeof(T) <= sizeof(uint64_t), "Unsupported integral type."); + + if (min > max) + abort(); + + // Use the biggest type possible to hold the range and the result. + uint64_t range = static_cast(max) - min; + uint64_t result = 0; + size_t offset = 0; + + while (offset < sizeof(T) * CHAR_BIT && (range >> offset) > 0 && + remaining_bytes_ != 0) { + // Pull bytes off the end of the seed data. Experimentally, this seems to + // allow the fuzzer to more easily explore the input space. This makes + // sense, since it works by modifying inputs that caused new code to run, + // and this data is often used to encode length of data read by + // |ConsumeBytes|. Separating out read lengths makes it easier modify the + // contents of the data that is actually read. + --remaining_bytes_; + result = (result << CHAR_BIT) | data_ptr_[remaining_bytes_]; + offset += CHAR_BIT; + } + + // Avoid division by 0, in case |range + 1| results in overflow. + if (range != std::numeric_limits::max()) + result = result % (range + 1); + + return static_cast(min + result); + } + + // Returns a std::string of length from 0 to |max_length|. When it runs out of + // input data, returns what remains of the input. Designed to be more stable + // with respect to a fuzzer inserting characters than just picking a random + // length and then consuming that many bytes with |ConsumeBytes|. + std::string ConsumeRandomLengthString(size_t max_length) { + // Reads bytes from the start of |data_ptr_|. Maps "\\" to "\", and maps "\" + // followed by anything else to the end of the string. As a result of this + // logic, a fuzzer can insert characters into the string, and the string + // will be lengthened to include those new characters, resulting in a more + // stable fuzzer than picking the length of a string independently from + // picking its contents. + std::string result; + + // Reserve the anticipated capaticity to prevent several reallocations. + result.reserve(std::min(max_length, remaining_bytes_)); + for (size_t i = 0; i < max_length && remaining_bytes_ != 0; ++i) { + char next = ConvertUnsignedToSigned(data_ptr_[0]); + Advance(1); + if (next == '\\' && remaining_bytes_ != 0) { + next = ConvertUnsignedToSigned(data_ptr_[0]); + Advance(1); + if (next != '\\') + break; + } + result += next; + } + + result.shrink_to_fit(); + return result; + } + + // Returns a std::vector containing all remaining bytes of the input data. + template std::vector ConsumeRemainingBytes() { + return ConsumeBytes(remaining_bytes_); + } + + // Returns a std::string containing all remaining bytes of the input data. + // Prefer using |ConsumeRemainingBytes| unless you actually need a std::string + // object. + std::string ConsumeRemainingBytesAsString() { + return ConsumeBytesAsString(remaining_bytes_); + } + + // Returns a number in the range [Type's min, Type's max]. The value might + // not be uniformly distributed in the given range. If there's no input data + // left, always returns |min|. + template T ConsumeIntegral() { + return ConsumeIntegralInRange(std::numeric_limits::min(), + std::numeric_limits::max()); + } + + // Reads one byte and returns a bool, or false when no data remains. + bool ConsumeBool() { return 1 & ConsumeIntegral(); } + + // Returns a copy of the value selected from the given fixed-size |array|. + template + T PickValueInArray(const T (&array)[size]) { + static_assert(size > 0, "The array must be non empty."); + return array[ConsumeIntegralInRange(0, size - 1)]; + } + + template + T PickValueInArray(std::initializer_list list) { + // TODO(Dor1s): switch to static_assert once C++14 is allowed. + if (!list.size()) + abort(); + + return *(list.begin() + ConsumeIntegralInRange(0, list.size() - 1)); + } + + // Returns an enum value. The enum must start at 0 and be contiguous. It must + // also contain |kMaxValue| aliased to its largest (inclusive) value. Such as: + // enum class Foo { SomeValue, OtherValue, kMaxValue = OtherValue }; + template T ConsumeEnum() { + static_assert(std::is_enum::value, "|T| must be an enum type."); + return static_cast(ConsumeIntegralInRange( + 0, static_cast(T::kMaxValue))); + } + + // Returns a floating point number in the range [0.0, 1.0]. If there's no + // input data left, always returns 0. + template T ConsumeProbability() { + static_assert(std::is_floating_point::value, + "A floating point type is required."); + + // Use different integral types for different floating point types in order + // to provide better density of the resulting values. + using IntegralType = + typename std::conditional<(sizeof(T) <= sizeof(uint32_t)), uint32_t, + uint64_t>::type; + + T result = static_cast(ConsumeIntegral()); + result /= static_cast(std::numeric_limits::max()); + return result; + } + + // Returns a floating point value in the range [Type's lowest, Type's max] by + // consuming bytes from the input data. If there's no input data left, always + // returns approximately 0. + template T ConsumeFloatingPoint() { + return ConsumeFloatingPointInRange(std::numeric_limits::lowest(), + std::numeric_limits::max()); + } + + // Returns a floating point value in the given range by consuming bytes from + // the input data. If there's no input data left, returns |min|. Note that + // |min| must be less than or equal to |max|. + template T ConsumeFloatingPointInRange(T min, T max) { + if (min > max) + abort(); + + T range = .0; + T result = min; + constexpr T zero(.0); + if (max > zero && min < zero && max > min + std::numeric_limits::max()) { + // The diff |max - min| would overflow the given floating point type. Use + // the half of the diff as the range and consume a bool to decide whether + // the result is in the first of the second part of the diff. + range = (max / 2.0) - (min / 2.0); + if (ConsumeBool()) { + result += range; + } + } else { + range = max - min; + } + + return result + range * ConsumeProbability(); + } + + // Reports the remaining bytes available for fuzzed input. + size_t remaining_bytes() { return remaining_bytes_; } + +private: + FuzzedDataProvider(const FuzzedDataProvider &) = delete; + FuzzedDataProvider &operator=(const FuzzedDataProvider &) = delete; + + void Advance(size_t num_bytes) { + if (num_bytes > remaining_bytes_) + abort(); + + data_ptr_ += num_bytes; + remaining_bytes_ -= num_bytes; + } + + template + std::vector ConsumeBytes(size_t size, size_t num_bytes_to_consume) { + static_assert(sizeof(T) == sizeof(uint8_t), "Incompatible data type."); + + // The point of using the size-based constructor below is to increase the + // odds of having a vector object with capacity being equal to the length. + // That part is always implementation specific, but at least both libc++ and + // libstdc++ allocate the requested number of bytes in that constructor, + // which seems to be a natural choice for other implementations as well. + // To increase the odds even more, we also call |shrink_to_fit| below. + std::vector result(size); + std::memcpy(result.data(), data_ptr_, num_bytes_to_consume); + Advance(num_bytes_to_consume); + + // Even though |shrink_to_fit| is also implementation specific, we expect it + // to provide an additional assurance in case vector's constructor allocated + // a buffer which is larger than the actual amount of data we put inside it. + result.shrink_to_fit(); + return result; + } + + template TS ConvertUnsignedToSigned(TU value) { + static_assert(sizeof(TS) == sizeof(TU), "Incompatible data types."); + static_assert(!std::numeric_limits::is_signed, + "Source type must be unsigned."); + + // TODO(Dor1s): change to `if constexpr` once C++17 becomes mainstream. + if (std::numeric_limits::is_modulo) + return static_cast(value); + + // Avoid using implementation-defined unsigned to signer conversions. + // To learn more, see https://stackoverflow.com/questions/13150449. + if (value <= std::numeric_limits::max()) { + return static_cast(value); + } else { + constexpr auto TS_min = std::numeric_limits::min(); + return TS_min + static_cast(value - TS_min); + } + } + + const uint8_t *data_ptr_; + size_t remaining_bytes_; +}; + +#endif // LLVM_FUZZER_FUZZED_DATA_PROVIDER_H_ diff --git a/gdb/cheri-compressed-cap/test/catch.hpp b/gdb/cheri-compressed-cap/test/catch.hpp new file mode 100644 index 00000000000..1077080b134 --- /dev/null +++ b/gdb/cheri-compressed-cap/test/catch.hpp @@ -0,0 +1,17702 @@ +/* + * Catch v2.12.1 + * Generated: 2020-04-21 19:29:20.964532 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 12 +#define CATCH_VERSION_PATCH 1 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +#if defined(__cpp_lib_uncaught_exceptions) +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template