From 0e853157b86f8ad9494e984e1cb271ca42914f1b Mon Sep 17 00:00:00 2001 From: Laurence Tratt Date: Wed, 7 Oct 2020 10:09:39 +0100 Subject: [PATCH 01/98] Add licence files. --- COPYRIGHT | 17 +++++++++++++++++ LICENSE-APACHE | 10 ++++++++++ LICENSE-MIT | 17 +++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 COPYRIGHT create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/COPYRIGHT b/COPYRIGHT new file mode 100644 index 0000000..d502b60 --- /dev/null +++ b/COPYRIGHT @@ -0,0 +1,17 @@ +Except as otherwise noted (below and/or in individual files), this project is +licensed under the Apache License, Version 2.0 + or the MIT license +, at your option. + +Copyright is retained by contributors and/or the organisations they +represent(ed) -- this project does not require copyright assignment. Please see +version control history for a full list of contributors. Note that some files +may include explicit copyright and/or licensing notices. + +The following contributors wish to explicitly make it known that the copyright +of their contributions is retained by an organisation: + + Aleks Bonin : + copyright retained by King's College London + Laurence Tratt : copyright retained by + King's College London diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..3d127b7 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,10 @@ +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..410e04e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 84c66e47b117f2bd48bbc396f92e2e121ea9af4b Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Fri, 9 Oct 2020 15:29:45 +0100 Subject: [PATCH 02/98] Add CI config. Integrate buildbot and bors into the repo. --- .buildbot.sh | 11 +++++++++++ bors.toml | 8 ++++++++ 2 files changed, 19 insertions(+) create mode 100644 .buildbot.sh create mode 100644 bors.toml diff --git a/.buildbot.sh b/.buildbot.sh new file mode 100644 index 0000000..8fd3391 --- /dev/null +++ b/.buildbot.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# vim: expandtab sts=0 sw=4 smarttab +set -e + +mkdir -p ./bin +for example in *.c; do + if [ "${example}" == "seal.c" ]; then + continue + fi + make bin/"${example%%.*}" +done diff --git a/bors.toml b/bors.toml new file mode 100644 index 0000000..805b481 --- /dev/null +++ b/bors.toml @@ -0,0 +1,8 @@ +status = ["buildbot/cheri-build-script"] + +timeout_sec = 600 # 10 minutes + +# Have bors delete auto-merged branches +delete_merged_branches = true + +cut_body_after = "" From e1413c37569d8550ffe3423e31079d211573a255 Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 3 Nov 2020 10:13:06 +0000 Subject: [PATCH 03/98] Switch to purecap sysroot. It was recommended to us to use the purecap VM in Slack. As it is the new default. The hybrid version was used because it was initially assumed that the purecap version is broken. --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 41b3332..ad9c296 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang -CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g -O0 -SSHPORT=10017 +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O0 +SSHPORT=10021 export cfiles := $(wildcard *.c) @@ -12,9 +12,10 @@ all: $(examples) bin/%: %.c $(CC) $(CFLAGS) $< -o $@ + scp -P $(SSHPORT) $< root@127.0.0.1:/root run-%: bin/% - scp -P $(SSHPORT) bin/$( Date: Tue, 3 Nov 2020 10:16:31 +0000 Subject: [PATCH 04/98] Use the CHERI intrinsics from the sysroot not from clang and be consistent with it. --- hello.c | 2 -- include/common.h | 30 +++++++++++++++--------------- mmap.c | 3 ++- set_bounds.c | 1 - setjmp.c | 5 +++-- stackscan.c | 6 +++--- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/hello.c b/hello.c index 70e71c8..deee1c6 100644 --- a/hello.c +++ b/hello.c @@ -1,6 +1,4 @@ #include "include/common.h" -//#include -#include #include #include #include diff --git a/include/common.h b/include/common.h index 51d9494..6fd4a81 100644 --- a/include/common.h +++ b/include/common.h @@ -1,23 +1,23 @@ #include #include -#include +#include +#include + #include void inspect_pointer(void *ptr) { - - uint64_t length = cheri_length_get(ptr); - uint64_t address = cheri_address_get(ptr); - uint64_t base = cheri_base_get(ptr); - uint64_t flags = cheri_flags_get(ptr); - uint64_t perms = cheri_perms_get(ptr); - uint64_t type = cheri_type_get(ptr); - bool tag = cheri_tag_get(ptr); - bool valid = cheri_is_valid(ptr); - - uint64_t offset = __builtin_cheri_offset_get(ptr); - printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, Perms: %04lx, Type: %04lx, Tag: %d, Valid: %d\n", - address, base, base + length, flags, length, offset, perms, type, tag, valid); + uint64_t length = cheri_getlength(ptr); + uint64_t address = cheri_getaddress(ptr); + uint64_t base = cheri_getbase(ptr); + uint64_t flags = cheri_getflags(ptr); + uint64_t perms = cheri_getperm(ptr); + uint64_t type = cheri_gettype(ptr); + bool tag = cheri_gettag(ptr); + + uint64_t offset = cheri_getoffset(ptr); + printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, Perms: %04lx, Type: %04lx, Tag: %d\n", + address, base, base + length, flags, length, offset, perms, type, tag); } @@ -32,7 +32,7 @@ void error(char* string) { // We don't want to inline it because __builtin_frame_address will change it's meaning. // __builtin_frame_address gives you the stack bottom of the current function. // This will be the stack top of the calling function. -__attribute__((noinline)) void* cheri_csp_get() +__attribute__((noinline)) void* cheri_getcsp() { return __builtin_frame_address(0); } diff --git a/mmap.c b/mmap.c index bae39d0..4650be8 100644 --- a/mmap.c +++ b/mmap.c @@ -8,6 +8,7 @@ #include #include #include +#include uint32_t *get_executable_block() { @@ -107,7 +108,7 @@ int main() code = get_executable_block(); code = generate_purecap(code); - inspect_pointer(cheri_pcc_get()); + inspect_pointer(cheri_getpcc()); inspect_pointer(code); int (*code_function)() = (int (*)())code; diff --git a/set_bounds.c b/set_bounds.c index 6c380b1..b55201a 100644 --- a/set_bounds.c +++ b/set_bounds.c @@ -1,6 +1,5 @@ #include "include/common.h" #include -#include #include #include diff --git a/setjmp.c b/setjmp.c index dc4a8ec..6dc87bf 100644 --- a/setjmp.c +++ b/setjmp.c @@ -3,6 +3,7 @@ #include #include #include +#include int main() { @@ -18,12 +19,12 @@ int main() // buffer[14..31] = ??? for (uint32_t idx; idx < (length / 16); idx++) { - if (cheri_is_valid(((void **)buffer)[idx])) + if (cheri_gettag(((void **)buffer)[idx])) { void *csp = cheri_csp_get(); uint64_t base = cheri_base_get(csp); uint64_t length = cheri_length_get(csp); - uint64_t address = cheri_address_get(((void **)buffer)[idx]); + uint64_t address = cheri_getaddress(((void **)buffer)[idx]); if (address >= base && address <= (base + length)) { diff --git a/stackscan.c b/stackscan.c index 3983fd5..f42e9ab 100644 --- a/stackscan.c +++ b/stackscan.c @@ -15,11 +15,11 @@ void inspect_stack(void *stack) bool is_stack_pointer(void *ptr) { - if (cheri_is_valid(ptr)) + if (cheri_gettag(ptr)) { uint64_t base = cheri_base_get(stack_top); uint64_t length = cheri_length_get(stack_top); - uint64_t address = cheri_address_get(ptr); + uint64_t address = cheri_getaddress(ptr); if (address >= base && address <= (base + length)) { @@ -31,7 +31,7 @@ bool is_stack_pointer(void *ptr) bool is_pointer(void *ptr) { - if (cheri_is_valid(ptr)) + if (cheri_gettag(ptr)) { return true; } From d1fffbd7698de195682de72588d794f4ae6763e8 Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 3 Nov 2020 10:21:54 +0000 Subject: [PATCH 05/98] Add the option to build c++ examples. --- Makefile | 8 +++++++- hello-cxx.cpp | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 hello-cxx.cpp diff --git a/Makefile b/Makefile index ad9c296..16d5b50 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,12 @@ CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang +CXX=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang++ CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O0 SSHPORT=10021 export cfiles := $(wildcard *.c) -examples := $(patsubst %.c,bin/%,$(cfiles)) +cppfiles := $(wildcard *.cpp) +examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) .PHONY: all run clean @@ -14,6 +16,10 @@ bin/%: %.c $(CC) $(CFLAGS) $< -o $@ scp -P $(SSHPORT) $< root@127.0.0.1:/root +bin/%: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + scp -P $(SSHPORT) $< root@127.0.0.1:/root + run-%: bin/% scp -P $(SSHPORT) bin/$( +#include +#include +#include "include/common.h" + +int main() { + std::vector list = { 1, 2, 3, 4 }; + for(unsigned int i = 0; i <= 16; i++) { + inspect_pointer(&list[i]); + std::cout << "Value: " << list[i] << std::endl; + } + +} From 1f141ccda135a2e4ba35f9476178791958f97ea7 Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 3 Nov 2020 14:31:10 +0000 Subject: [PATCH 06/98] Migrate intrinsic usage from clang to charibsd. Fix the Makefile, so it can work on buildbot. Fix all the places with invalid call to clang intrinsics. Fix stackscan.c and setjmp.c to benefit from using the cheribsd intrinsics. --- Makefile | 7 +++---- hello.c | 2 +- mmap.c | 8 ++++---- set_bounds.c | 2 +- setjmp.c | 8 +++----- stackscan.c | 13 ++++++------- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 16d5b50..c2f8cef 100644 --- a/Makefile +++ b/Makefile @@ -14,14 +14,13 @@ all: $(examples) bin/%: %.c $(CC) $(CFLAGS) $< -o $@ - scp -P $(SSHPORT) $< root@127.0.0.1:/root bin/%: %.cpp $(CXX) $(CFLAGS) $< -o $@ - scp -P $(SSHPORT) $< root@127.0.0.1:/root -run-%: bin/% - scp -P $(SSHPORT) bin/$(= base && address <= (base + length)) + if (cheri_is_address_inbounds(csp, address)) { printf("[STACK POINTER] "); } diff --git a/stackscan.c b/stackscan.c index f42e9ab..e6666ff 100644 --- a/stackscan.c +++ b/stackscan.c @@ -1,5 +1,6 @@ #include "include/common.h" +#include #include #include #include @@ -10,18 +11,16 @@ void *stack_top; void inspect_stack(void *stack) { - printf("offset: %lu\n", cheri_length_get(stack) - cheri_offset_get(stack)); + printf("offset: %lu\n", cheri_getlength(stack) - cheri_getoffset(stack)); } bool is_stack_pointer(void *ptr) { if (cheri_gettag(ptr)) { - uint64_t base = cheri_base_get(stack_top); - uint64_t length = cheri_length_get(stack_top); uint64_t address = cheri_getaddress(ptr); - if (address >= base && address <= (base + length)) + if (cheri_is_address_inbounds(stack_top, address)) { return true; } @@ -40,7 +39,7 @@ bool is_pointer(void *ptr) bool is_exec(void *ptr) { - if (((cheri_perms_get(ptr) & 0b10) >> 1) == 1) + if (((cheri_getperm(ptr) & 0b10) >> 1) == 1) { return true; } @@ -133,8 +132,8 @@ int main() { stack_top = __builtin_frame_address(0); - uint64_t base = cheri_base_get(csp); - uint64_t length = cheri_length_get(csp); + uint64_t base = cheri_getbase(csp); + uint64_t length = cheri_getlength(csp); uint32_t *values = test(); From e254f1614801bde4a32a8736e2af664536f2c6d2 Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 3 Nov 2020 09:54:15 +0000 Subject: [PATCH 07/98] Add a sentry example. The main idea behind sentries (sealed entries) is that they protect the function pointers from being modified, read to/from. This allows emulation of most sealed capability modes. This example shows some of the use cases of sentry capabilities that I could think of. It is important to note that every function pointer on CHERI is using sentry capabilities. --- sentry.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 sentry.c diff --git a/sentry.c b/sentry.c new file mode 100644 index 0000000..14b6907 --- /dev/null +++ b/sentry.c @@ -0,0 +1,169 @@ +/* + * An example showing how a sentry can be used for more advanced + * security model. In CHERIv8 every function is actually a sentry. + * However sentries can allow for additional security when used with + * a level of indirection. In this example an assembly trampoline is + * used to load additional data, that isn't available to the user. + */ + +#include +#include +#include +#include +#include + +#include "include/common.h" +#include "include/instructions.h" +#include "include/regs.h" + +#include + +struct data +{ + int a; + int b; + char c[256]; +}; + +void *setup_sentry(void *, void (*f)(uint32_t *, void *)); +void oop_sentry(struct data *, int); +void simple_sentry(int); + +// This is the function that will be called through a sentry and +// the first argument is passed by the trampoline assembly. +void oop_sentry(struct data *this, int arg) +{ + printf("OOP: %p %d \n", this, arg); +} + +// This function will also be called through a sentry but no +// arguments are added. +void simple_sentry(int arg) +{ + printf("Simple: %d \n", arg); +} + +// A function to generate the code that will put the +// this structure as first argument and move the rest of the +// arguments to the next registers. +void gen_oop(uint32_t *code, void *function) +{ + intptr_t *code_data = (intptr_t *)code; + + // The additional data needs to be stored somewhere in memory + // where it can not be accessed by the user of the function. + // This is why it is stored in the same place where the code will be. + // As the code is not modifiable and not readable this data is also + // protected. + code_data[255] = (intptr_t)function; + code_data[254] = (intptr_t)malloc(sizeof(struct data)); + + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + // Move the arguments to the next register. + // This will break if we have more then 6 arguments but that should be fine. + code[idx++] = addi(a1, a0, 0); + code[idx++] = addi(a2, a1, 0); + code[idx++] = addi(a3, a2, 0); + code[idx++] = addi(a4, a3, 0); + code[idx++] = addi(a5, a4, 0); + code[idx++] = addi(a6, a5, 0); + code[idx++] = addi(a7, a6, 0); + code[idx++] = clc_128(ca0, cs2, ((-32) + (2 << 20))); + code[idx++] = cjalr(cnull, cs3); +} + +void gen_simple(uint32_t *code, void *function) +{ + intptr_t *code_data = (intptr_t *)code; + code_data[255] = (intptr_t)function; + + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + code[idx++] = cjalr(cnull, cs3); +} + +void gen_override(uint32_t *code, void *function) +{ + intptr_t *code_data = (intptr_t *)code; + code_data[255] = (intptr_t)function; + + // Here we again generate code for calling the passed function, + // but the code modifies the first argument to be different then + // the one passed. I am not sure how this is useful. + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + code[idx++] = addi(a0, cnull, 42); + code[idx++] = cjalr(cnull, cs3); +} + +// Here we allocate the memory for the code that will be generated. +void *setup_sentry(void *function, void (*generate_code)(uint32_t *, void *)) +{ + // As the function is returned as a sentry the pointer to it cannot be read or written to. + // This means that there is no need to remove write protection as only the function itself + // can change its own code. + uint32_t *code = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + + generate_code(code, function); + + // We set the flag to run the code in CHERI mode and seal it so it can only be called. + return cheri_sealentry(cheri_setflags(code, 1)); +} + +void *setup_normal(void *function, void (*generate_code)(uint32_t *, void *)) +{ + uint32_t *code = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + + generate_code(code, function); + + // Here the resulting capability isn't sealed to show that unsealed capabilities can be + // called from the same code. + return cheri_setflags(code, 1); +} + +int main() +{ + puts("0: Simple sentry"); + puts("1: OOP sentry"); + puts("2: Override sentry"); + puts("3: Normal function"); + puts("4: Fail"); + printf("Mode: "); + uint32_t mode = 0; + if (scanf("%u", &mode) == 0) + { + error("Invalid input"); + } + + void (*fpointer)(int); + switch (mode) + { + case 0: + fpointer = setup_sentry(simple_sentry, gen_simple); + break; + case 1: + fpointer = setup_sentry(oop_sentry, gen_oop); + break; + case 2: + fpointer = setup_sentry(simple_sentry, gen_override); + break; + case 3: + fpointer = setup_normal(simple_sentry, gen_simple); + break; + case 4: // This case should fail + fpointer = setup_sentry(simple_sentry, gen_simple); + fpointer = cheri_incoffset(fpointer, 16); + break; + default: + err(1, "Invalid option selected\n"); + } + + fpointer(99); + printf("END\n"); +} From ae17b0b43671ff1b317f08e0a894d52f22d4e02d Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 1 Dec 2020 11:52:26 +0000 Subject: [PATCH 08/98] Add example showing the usage of .so files. This example is needed to present the implications of using shared libraries as a proxy for deciding what is public and what is private by means of what is being exported and how does it integrate with the changes made on the linker (rtld) as needed by CHERI. --- shared_objects/.gitignore | 3 ++ shared_objects/Makefile | 16 ++++++++ shared_objects/include/lib1.h | 8 ++++ shared_objects/include/lib2.h | 19 ++++++++++ shared_objects/lib1.c | 25 +++++++++++++ shared_objects/lib2.c | 70 +++++++++++++++++++++++++++++++++++ shared_objects/main.c | 32 ++++++++++++++++ 7 files changed, 173 insertions(+) create mode 100644 shared_objects/.gitignore create mode 100644 shared_objects/Makefile create mode 100644 shared_objects/include/lib1.h create mode 100644 shared_objects/include/lib2.h create mode 100644 shared_objects/lib1.c create mode 100644 shared_objects/lib2.c create mode 100644 shared_objects/main.c diff --git a/shared_objects/.gitignore b/shared_objects/.gitignore new file mode 100644 index 0000000..d2c3e96 --- /dev/null +++ b/shared_objects/.gitignore @@ -0,0 +1,3 @@ +*.so +.cache +*.json diff --git a/shared_objects/Makefile b/shared_objects/Makefile new file mode 100644 index 0000000..fda44fd --- /dev/null +++ b/shared_objects/Makefile @@ -0,0 +1,16 @@ +CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g +SSHPORT=10021 + +main: main.c + $(CC) $(CFLAGS) -L. -l1 -l2 -g $< -o main + +lib1.so: lib1.c + $(CC) $(CFLAGS) -g -fPIC -shared $< -o lib1.so + +lib2.so: lib2.c + $(CC) $(CFLAGS) -g -fPIC -shared $< -o lib2.so + +run: + scp -P $(SSHPORT) * root@127.0.0.1:/root/demo/ + ssh -p $(SSHPORT) root@127.0.0.1 -t 'setenv LD_LIBRARY_PATH /root/demo; /root/demo/main' diff --git a/shared_objects/include/lib1.h b/shared_objects/include/lib1.h new file mode 100644 index 0000000..fefa99c --- /dev/null +++ b/shared_objects/include/lib1.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include + +void increment(); + +int get_count(); diff --git a/shared_objects/include/lib2.h b/shared_objects/include/lib2.h new file mode 100644 index 0000000..d53776c --- /dev/null +++ b/shared_objects/include/lib2.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include + +struct Car_priv { + int maxSpeed; + void (*crash)(); +}; + +struct Car { + int speed; + void (*honk)(); + char name[]; +}; + +void init(); + +struct Car* new_car(); diff --git a/shared_objects/lib1.c b/shared_objects/lib1.c new file mode 100644 index 0000000..f0c3336 --- /dev/null +++ b/shared_objects/lib1.c @@ -0,0 +1,25 @@ +/** + * A basic example showing how CHERI sealed entries can be used to protect data. In this case the + * cont variable will be inaccessible from the things that link with this shared library. + */ + +#include "include/lib1.h" + +static int32_t count = -5; + +void increment() +{ + count += 1; +} + +int get_count() +{ + if (count > 0) + { + return count; + } + else + { + return 0; + } +} diff --git a/shared_objects/lib2.c b/shared_objects/lib2.c new file mode 100644 index 0000000..f0e86fd --- /dev/null +++ b/shared_objects/lib2.c @@ -0,0 +1,70 @@ +/** + * A more complicated example of sealed entries displaying how they can be used to protect private + * data that will and can be given to the user of the library as a pointer. In this case Car_priv is + * the private fields and methods of the object that is given to the user, and it is protected by + * exploiting the properties of the sealed entries (The fact that the capability can only be jumped + * to not modified in any way). + */ + +#include +#include +#include +#include +#include + +#include "../include/instructions.h" +#include "../include/regs.h" +#include "include/lib2.h" + +static struct Car *arena; +static uint32_t size; + +void init() {} + +void crash() +{ + printf("CRASH!\n"); +} + +void honk(struct Car_priv *priv, struct Car *car) +{ + printf("%p %p\n", priv, car); + printf("HONK!\n"); + if (car->speed > priv->maxSpeed) + { + priv->crash(); + } +} + +struct Car *new_car() +{ + uint32_t *mem = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + + void **ptrs = mem; + + const uint32_t vtable_start_index = 2; + ptrs[vtable_start_index + 0] = honk; + + const ptrdiff_t vtable_offset = ((char*)(ptrs + vtable_start_index) - (char*)ptrs); + const size_t vtable_size = 1 * 16; + + uint32_t idx = 0; + mem[idx++] = auipcc(cs2, 0); + mem[idx++] = clc_128(cs3, cs2, vtable_offset); + mem[idx++] = cincoffsetimm(ca0, cs2, vtable_offset + vtable_size); + mem[idx++] = cincoffsetimm(ca1, cs2, vtable_offset + vtable_size + sizeof(struct Car_priv)); + mem[idx++] = cjalr(cnull, cs3); + + uint32_t functions_size = vtable_offset + vtable_size; + struct Car *public_car = + (struct Car *)(((char *)mem) + sizeof(struct Car_priv) + functions_size); + struct Car_priv *private_car = (struct Car_priv *)(((char *)mem) + functions_size); + + private_car->maxSpeed = 10; + private_car->crash = &crash; + + public_car->honk = cheri_sealentry(cheri_setflags(mem, 1)); + + return public_car; +} diff --git a/shared_objects/main.c b/shared_objects/main.c new file mode 100644 index 0000000..8d58549 --- /dev/null +++ b/shared_objects/main.c @@ -0,0 +1,32 @@ +/** + * Displays how lib1 and lib2 will look like from the point of view of the users of the libraries. + */ + +#include +#include +#include + +#include "include/lib1.h" +#include "include/lib2.h" + +int main() +{ + struct Car *car = new_car(); + car->speed = 1; + car->honk(); + + car->speed = 999; + car->honk(); + + increment(); + increment(); + increment(); + + printf("Count: %d\n", get_count()); + + increment(); + increment(); + increment(); + + printf("Count: %d\n", get_count()); +} From a6b7c0893630ed703088444547d105c26ce15607 Mon Sep 17 00:00:00 2001 From: Aleks Bonin Date: Tue, 3 Nov 2020 13:12:42 +0000 Subject: [PATCH 09/98] Add examples for checking boundaries (mask, length). --- check_length.c | 17 +++++++++++++++++ check_mask.c | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 check_length.c create mode 100644 check_mask.c diff --git a/check_length.c b/check_length.c new file mode 100644 index 0000000..4b4202a --- /dev/null +++ b/check_length.c @@ -0,0 +1,17 @@ +/*** + * Testing to see what the representable + * lengths function returns for different + * values. + */ + +#include +#include +#include + +int main() +{ + for (uint64_t length = 0; length <= (4096 * 2); length += 1) + { + printf("%lu\n", cheri_representable_length(length)); + } +} diff --git a/check_mask.c b/check_mask.c new file mode 100644 index 0000000..9e9f46c --- /dev/null +++ b/check_mask.c @@ -0,0 +1,25 @@ +/*** + * A example to see what the representable alignment mask is and + * how it changes the alignment based on how high in memory it is. + */ + +#include +#include +#include +#include +#include + +int main() +{ + void *base = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + + uint64_t base_address = cheri_address_get(base); + for (uint64_t alignment = 0; alignment < (4096 * 10); alignment++) + { + uint64_t mask = cheri_representable_alignment_mask(base_address + alignment); + uint64_t mask1 = cheri_representable_alignment_mask(alignment); + printf("%lx %lx %lx | %lx %lx %lx\n", mask, base_address + alignment, + (base_address + alignment) & mask, mask1, alignment, alignment & mask1); + } +} From 780027f6289d4cb2a9f901ac2e3d4a8ec980d264 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 15 Oct 2020 16:12:38 +0100 Subject: [PATCH 10/98] Bump pointer allocation example from Jeremy --- example_allocators/bump_allocator/Makefile | 28 +++++++ example_allocators/bump_allocator/README.md | 9 +++ .../bump_allocator/bad_client.c | 45 +++++++++++ .../bump_allocator/bump_alloc.c | 78 +++++++++++++++++++ .../bump_allocator/bump_alloc.h | 18 +++++ .../bump_allocator/good_client.c | 47 +++++++++++ 6 files changed, 225 insertions(+) create mode 100644 example_allocators/bump_allocator/Makefile create mode 100644 example_allocators/bump_allocator/README.md create mode 100644 example_allocators/bump_allocator/bad_client.c create mode 100644 example_allocators/bump_allocator/bump_alloc.c create mode 100644 example_allocators/bump_allocator/bump_alloc.h create mode 100644 example_allocators/bump_allocator/good_client.c diff --git a/example_allocators/bump_allocator/Makefile b/example_allocators/bump_allocator/Makefile new file mode 100644 index 0000000..78d8295 --- /dev/null +++ b/example_allocators/bump_allocator/Makefile @@ -0,0 +1,28 @@ +CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang + +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g -O0 -I../.. +SSHPORT=10003 +export + +cfiles := $(wildcard *.c) +examples := $(patsubst %.c,bin/%,$(cfiles)) + + +good_client: good_client.c bump_alloc.c + +bad_client: bad_client.c bump_alloc.c + +test: good_client bad_client + scp -P $(SSHPORT) good_client bad_client root@127.0.0.1:/root + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/good_client' + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/bad_client' + +bin/%: %.c + $(CC) $(CFLAGS) $< -o $@ + +run-%: bin/% + scp -P $(SSHPORT) bin/$( + +#include "bump_alloc.h" +#include "include/common.h" + +/* should program generate debugging output? 1 for Yes, 0 for No */ +#define DEBUG_PRINTF 1 + +/* size of buffer to allocate (in words) */ +#define NUM_WORDS 100 + +int main() +{ + int i; + int *p; + + init_alloc(NUM_WORDS * sizeof(int)); + + p = (int *)bump_alloc(1 * sizeof(int)); + if (DEBUG_PRINTF) + inspect_pointer(p); + if (p) + { + *p = 42; + /* first bad op: write outside the pointer bounds */ + *(p + 1) = 0xbaadf00d; + /* second bad op: read outside the pointer bounds */ + printf("out-of-bounds read: %d\n", *(p + 1)); + } + + return 0; +} diff --git a/example_allocators/bump_allocator/bump_alloc.c b/example_allocators/bump_allocator/bump_alloc.c new file mode 100644 index 0000000..0a2d29a --- /dev/null +++ b/example_allocators/bump_allocator/bump_alloc.c @@ -0,0 +1,78 @@ +/********************************** + * bump_alloc.c + * Jeremy.Singer@glasgow.ac.uk + * + * This is a simple bump-pointer allocator. + * It mmaps a large buffer of SIZE bytes, + * then allocates this space in word-sized + * chunks to client code (in main fn). + * + * Initial simple memory allocator test. + * Explore capability narrowing operations + * and intrinsics for bound reporting. + */ + +#include +#include +#include +#include +#include + +#include "bump_alloc.h" + +int count = 0; /* number of bytes allocated so far*/ +int max = 0; /* upper limit for count */ +char *buffer = NULL; /* the allocation buffer */ + +void init_alloc(int size_in_bytes) +{ + /* request memory for our allocation buffer + * NB mmap min bounds for capability is 1 page (4K) + */ + char *res = mmap(NULL, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (res == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + buffer = res; + max = size_in_bytes; + return; +} + +/* + * allocate len bytes with bump pointer allocator + * this is our simplistic `malloc` function + */ +char *bump_alloc(int len) +{ + char *chunk = buffer + count; + size_t rounded_len; /* for CHERI alignment */ + size_t new_count; /* for buffer overflow check */ + + /* ensure we can represent the capability accurately, + * see p30 of CHERI C/C++ Prog Guide (June 2020) + * www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947 + */ + chunk = __builtin_align_up(chunk, ~cheri_representable_alignment_mask(len) + 1); + rounded_len = cheri_representable_length(len); + + new_count = (chunk + rounded_len) - buffer; + if (new_count > max) + { + /* out of bounds - don't allocate anything */ + chunk = 0; + } + else + { + /* restrict capability range before returning ptr */ + chunk = cheri_bounds_set_exact(chunk, rounded_len); + + /* update bytes allocated count */ + count = new_count; + } + + return chunk; +} diff --git a/example_allocators/bump_allocator/bump_alloc.h b/example_allocators/bump_allocator/bump_alloc.h new file mode 100644 index 0000000..e14e0d7 --- /dev/null +++ b/example_allocators/bump_allocator/bump_alloc.h @@ -0,0 +1,18 @@ +/********************************** + * bump_alloc.h + * Jeremy.Singer@glasgow.ac.uk + * + * This is a simple bump-pointer allocator. + * It mmaps a large buffer of SIZE bytes, + * then allocates this space in word-sized + * chunks to client code (in main fn). + * + * Initial simple memory allocator test. + * Explore capability narrowing operations + * and intrinsics for bound reporting. + */ + +void init_alloc(int size_in_bytes); + +char *bump_alloc(int bytes); /* the simplest malloc */ + /* oh, and there's no free() ! */ diff --git a/example_allocators/bump_allocator/good_client.c b/example_allocators/bump_allocator/good_client.c new file mode 100644 index 0000000..7ca070a --- /dev/null +++ b/example_allocators/bump_allocator/good_client.c @@ -0,0 +1,47 @@ +/* + * good_client.c + * Jeremy Singer + * + * Example use of bump allocator, showing how + * 'good' client code should interact with the + * allocator. + */ + +#include + +#include "bump_alloc.h" +#include "include/common.h" + +/* should program generate debugging output? 1 for Yes, 0 for No */ +#define DEBUG_PRINTF 1 + +/* size of buffer to allocate (in words) */ +#define NUM_WORDS 100 + +int main() +{ + int i; + + init_alloc(NUM_WORDS * sizeof(int)); + + /* now try to do some bump pointer allocations */ + for (i = 0; i < NUM_WORDS; i++) + { + int *x = (int *)bump_alloc(1 * sizeof(int)); + if (DEBUG_PRINTF) + inspect_pointer(x); + if (x) + { + *x = 42; + if (DEBUG_PRINTF) + printf("stored value %d\n", *x); + } + else + { + if (DEBUG_PRINTF) + printf("bump_alloc returned null\n"); + } + } + + return 0; +} From 9080deaef96852e4c3ec923e69ca4aad8516c89e Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 19 Nov 2020 16:22:40 +0000 Subject: [PATCH 11/98] doubly linked list with XOR pointers --- xor_pointers.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 xor_pointers.c diff --git a/xor_pointers.c b/xor_pointers.c new file mode 100644 index 0000000..c6850f7 --- /dev/null +++ b/xor_pointers.c @@ -0,0 +1,72 @@ +/* xor_lists.c + * Jeremy Singer + * Example of doubly-linked list data structure + * featuring xor'd pointers for shared next/prev field + */ + +#include +#include + +#define NUM_CELLS 100 +#define WORD long int + +typedef struct cell { + WORD data; + WORD ptr; +} cell_t; + +cell_t *alloc_cell(WORD payload, cell_t *tail) { + cell_t *cell = (cell_t *)malloc(sizeof(cell_t)); + cell->data = payload; + /* xor this cell's address into the tail's ptr field */ + if (tail != NULL) + tail->ptr ^= (WORD)cell; + /* store tail address in this cell's ptr field + * NOTE most recently allocated cell (i.e. head) + * only stores a single ptr, not an XOR'd pair + */ + cell->ptr = (WORD)tail; + return cell; +} + +int main() { + int i = 0; + cell_t *head = NULL; + cell_t *prev; + cell_t *curr; + cell_t *next; + for (i=0; idata); + prev = head; + curr = (cell_t *)prev->ptr; + while (curr != NULL) { + printf("%ld\n", curr->data); + // chase tail + next = (cell_t *)((curr->ptr)^(WORD)prev); + // move along one + prev = curr; + curr = next; + } + /* now we are at the NULL ptr at the end of the list */ + /* let's traverse back to the start of the list */ + next = curr; + curr = prev; + prev = (cell_t *)((curr->ptr)^(WORD)next); + while (curr != head) { + printf("%ld\n", curr->data); + // chase reverse tail FIXME + next = curr; + curr = prev; + prev = (cell_t *)((curr->ptr)^(WORD)next); + // move along one - move BACK one - FIXME - before or after??? + } + printf("%ld\n", head->data); + + return 0; +} + From 7c51ff17e707a8d36538e2a76b7071c67aacfd72 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 19 Nov 2020 16:23:26 +0000 Subject: [PATCH 12/98] reformatted src code with clang-tidy --- xor_pointers.c | 113 ++++++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/xor_pointers.c b/xor_pointers.c index c6850f7..adb76cd 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -10,63 +10,68 @@ #define NUM_CELLS 100 #define WORD long int -typedef struct cell { - WORD data; - WORD ptr; +typedef struct cell +{ + WORD data; + WORD ptr; } cell_t; -cell_t *alloc_cell(WORD payload, cell_t *tail) { - cell_t *cell = (cell_t *)malloc(sizeof(cell_t)); - cell->data = payload; - /* xor this cell's address into the tail's ptr field */ - if (tail != NULL) - tail->ptr ^= (WORD)cell; - /* store tail address in this cell's ptr field - * NOTE most recently allocated cell (i.e. head) - * only stores a single ptr, not an XOR'd pair - */ - cell->ptr = (WORD)tail; - return cell; +cell_t *alloc_cell(WORD payload, cell_t *tail) +{ + cell_t *cell = (cell_t *)malloc(sizeof(cell_t)); + cell->data = payload; + /* xor this cell's address into the tail's ptr field */ + if (tail != NULL) + tail->ptr ^= (WORD)cell; + /* store tail address in this cell's ptr field + * NOTE most recently allocated cell (i.e. head) + * only stores a single ptr, not an XOR'd pair + */ + cell->ptr = (WORD)tail; + return cell; } -int main() { - int i = 0; - cell_t *head = NULL; - cell_t *prev; - cell_t *curr; - cell_t *next; - for (i=0; idata); - prev = head; - curr = (cell_t *)prev->ptr; - while (curr != NULL) { - printf("%ld\n", curr->data); - // chase tail - next = (cell_t *)((curr->ptr)^(WORD)prev); - // move along one - prev = curr; - curr = next; - } - /* now we are at the NULL ptr at the end of the list */ - /* let's traverse back to the start of the list */ - next = curr; - curr = prev; - prev = (cell_t *)((curr->ptr)^(WORD)next); - while (curr != head) { - printf("%ld\n", curr->data); - // chase reverse tail FIXME - next = curr; - curr = prev; - prev = (cell_t *)((curr->ptr)^(WORD)next); - // move along one - move BACK one - FIXME - before or after??? - } - printf("%ld\n", head->data); +int main() +{ + int i = 0; + cell_t *head = NULL; + cell_t *prev; + cell_t *curr; + cell_t *next; + for (i = 0; i < NUM_CELLS; i++) + { + /* allocate a cell */ + head = alloc_cell(i, head); + } + /* now we have 100 cells in a doubly-linked list */ + /* traverse list from head to tail */ + printf("%ld\n", head->data); + prev = head; + curr = (cell_t *)prev->ptr; + while (curr != NULL) + { + printf("%ld\n", curr->data); + // chase tail + next = (cell_t *)((curr->ptr) ^ (WORD)prev); + // move along one + prev = curr; + curr = next; + } + /* now we are at the NULL ptr at the end of the list */ + /* let's traverse back to the start of the list */ + next = curr; + curr = prev; + prev = (cell_t *)((curr->ptr) ^ (WORD)next); + while (curr != head) + { + printf("%ld\n", curr->data); + // chase reverse tail FIXME + next = curr; + curr = prev; + prev = (cell_t *)((curr->ptr) ^ (WORD)next); + // move along one - move BACK one - FIXME - before or after??? + } + printf("%ld\n", head->data); - return 0; + return 0; } - From a6f2ef404bb0cb05d76706409e756f320c5edaaa Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 19 Nov 2020 16:24:38 +0000 Subject: [PATCH 13/98] remove superfluous comment --- xor_pointers.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xor_pointers.c b/xor_pointers.c index adb76cd..9a49793 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -65,11 +65,10 @@ int main() while (curr != head) { printf("%ld\n", curr->data); - // chase reverse tail FIXME + // chase reverse tail next = curr; curr = prev; prev = (cell_t *)((curr->ptr) ^ (WORD)next); - // move along one - move BACK one - FIXME - before or after??? } printf("%ld\n", head->data); From dba97c92540e700eb65de67782ffbb4cfe869d5c Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 19 Nov 2020 16:27:05 +0000 Subject: [PATCH 14/98] added DOI link for relevant paper from OOPSLA 20 --- xor_pointers.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xor_pointers.c b/xor_pointers.c index 9a49793..d941a7f 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -2,6 +2,10 @@ * Jeremy Singer * Example of doubly-linked list data structure * featuring xor'd pointers for shared next/prev field + * + * This code inspired by OOPSLA'20 talk: + * "Sound Garbage Collection for C using Pointer Provenance" + * https://dl.acm.org/doi/10.1145/3428244 */ #include From f36189bc69a39a7704f514d01cce6e7f56e22497 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Tue, 26 Jan 2021 16:07:43 +0000 Subject: [PATCH 15/98] tidied up documentation for this example --- xor_pointers.c | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/xor_pointers.c b/xor_pointers.c index d941a7f..80befb0 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -1,11 +1,24 @@ /* xor_lists.c * Jeremy Singer * Example of doubly-linked list data structure - * featuring xor'd pointers for shared next/prev field - * - * This code inspired by OOPSLA'20 talk: - * "Sound Garbage Collection for C using Pointer Provenance" - * https://dl.acm.org/doi/10.1145/3428244 + * featuring xor'd pointers for shared next/prev field. + * + * This is an example of pointer fiddling code that + * currently _won't_ work on CHERI platforms, since + * the XOR operation on a capability probably puts + * the pointer out of bounds (invalidating the + * capability so it cannot be dereferenced). + * + * Related Reading Material: + * 1) Wikipedia article on XOR linked list + * https://en.wikipedia.org/wiki/XOR_linked_list + * 2) OOPSLA 2020 paper on + * "Sound Garbage Collection for C using Pointer Provenance" + * https://dl.acm.org/doi/10.1145/3428244 + * 3) Complete spatial safety for C and C++ using + * CHERI capabilities. + * https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-949.pdf + See Section 3.9.1 XOR-linked lists (p56) */ #include @@ -14,6 +27,11 @@ #define NUM_CELLS 100 #define WORD long int +/* linked list cell has two fields + * data is cell payload + * ptr is superposition of next/previous pointers, + * which are XOR'd together + */ typedef struct cell { WORD data; @@ -47,7 +65,7 @@ int main() /* allocate a cell */ head = alloc_cell(i, head); } - /* now we have 100 cells in a doubly-linked list */ + /* now we have NUM_CELLS cells in a doubly-linked list */ /* traverse list from head to tail */ printf("%ld\n", head->data); prev = head; From a8b848dd1ceb782e84389cb9bb9289f1c8cedd88 Mon Sep 17 00:00:00 2001 From: Cristian Urlea Date: Mon, 26 Oct 2020 17:42:01 +0000 Subject: [PATCH 16/98] Timsort example Fixed out-of-date use error in timsort.c typo fix 2x: /s/straglers/stragglers/ removed extraneous empty line extend testing for classical timsort with array sorting tests Made 'timsort_purecap' safe by dispatching on 'bounds' field precision check. Falls back to 'timsort_classic' (lib/timsort_lib.c) when needed. Tests now cover array lengths [0..2048]. removed extraneous empty line Removed bounds representability assumption based optimisations; Added tests against qsort with stable (for ints) comparator; Abstracted length encoding/checking; Small typo in comment 2x Added ephemeral store with documentation role. refactored; fixed offset issue --- Makefile | 4 +- include/timsort_lib.h | 256 ++++++++++++++++++++++++++++++++++++++++++ timsort.c | 161 ++++++++++++++++++++++++++ 3 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 include/timsort_lib.h create mode 100644 timsort.c diff --git a/Makefile b/Makefile index c2f8cef..b344b7c 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) .PHONY: all run clean all: $(examples) - + bin/%: %.c $(CC) $(CFLAGS) $< -o $@ @@ -24,4 +24,4 @@ run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/$( +#include +#include +#include +#include +#include + +#ifdef __CHERI_PURE_CAPABILITY__ +#include "common.h" +#include +#include +#endif + +const int MAX_ARRAY_SZ = 2048; + +int *random_chunk(size_t arr_length) +{ + + srand(time(NULL)); + + int *arr = malloc(arr_length * sizeof(int)); + for (size_t ix = 0; ix < arr_length; ix++) + { + arr[ix] = rand(); + } + + return arr; +} + +int cmpfunc(const void *a, const void *b) +{ + return (*(int *)a - *(int *)b); +} + +bool arrEq(int arr_a[], int arr_b[], size_t lowerBound, size_t upperBound) +{ + if (lowerBound == upperBound || 0 == upperBound) + { + return true; + } + + for (size_t ix = lowerBound; ix <= upperBound; ix++) + { + if (arr_a[ix] != arr_b[ix]) + { + return false; + } + } + return true; +} + +/** + * Min of two size_t arguments. + * @param a first value to choose from + * @param b second value to choose from + * @return The minimum value between `a` and `b` + */ +size_t min(size_t a, size_t b) +{ + if (a <= b) + { + return a; + } + else + { + return b; + } +} + +/** + * Checks if an array of integers `arr` having length `length` is sorted in ascending order. + * @param arr Array to sort + * @param length The legth of `arr` + */ +bool isSorted(int arr[], size_t length) +{ + if (length <= 1) + { + return true; + } + + for (size_t ix = 1; ix < length; ix++) + { + if (arr[ix - 1] > arr[ix]) + { + return false; + } + } + + return true; +} + +struct bp_array_s +{ + void *pointer; + size_t base; + size_t length; +}; + +struct bp_array_s packBP_mangled(void *pointer, size_t baseIndex, size_t sizeInBytes) +{ + struct bp_array_s ret; + + ret.pointer = pointer; + ret.base = baseIndex; + ret.length = sizeInBytes; + + return ret; +} + +__attribute__((always_inline)) void *get_pointer_mangled(struct bp_array_s bp) +{ + return bp.pointer; +} + +__attribute__((always_inline)) size_t get_base_mangled(struct bp_array_s bp) +{ + return bp.base; +} + +__attribute__((always_inline)) size_t get_length_mangled(struct bp_array_s bp) +{ + return bp.length; +} + +#ifndef __CHERI_PURE_CAPABILITY__ + +typedef struct bp_array_s bp_array; +#define packBP(x, y, z) packBP_mangled((x), (y), (z)) +#define get_pointer(x) get_pointer_mangled(x) +#define get_base(x) get_base_mangled(x) +#define get_length(x) get_length_mangled(x) + +#else + +typedef void *bp_array; + +__attribute__((always_inline)) bp_array packBP(int *pointer, const size_t baseIndex, + const size_t sizeInBytes) +{ + if (0 == baseIndex) + { + + struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); + if (NULL == largeDescriptor) + { + exit(EXIT_FAILURE); + } + *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); + + return (bp_array)largeDescriptor; + } + + int *descriptor = cheri_setoffset(pointer, 0); + descriptor = cheri_setbounds(descriptor, sizeInBytes); + descriptor = cheri_setoffset(descriptor, baseIndex); + + if (baseIndex != cheri_getoffset(descriptor) || sizeInBytes != cheri_getlen(descriptor)) + { + struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); + if (NULL == largeDescriptor) + { + exit(EXIT_FAILURE); + } + *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); + + return (bp_array)largeDescriptor; + } + + return (bp_array)descriptor; +} + +__attribute__((always_inline)) bool isMangled(bp_array bp) +{ + return 0 == cheri_getoffset(bp); +} + +__attribute__((always_inline)) void *get_pointer(bp_array bp) +{ + if (isMangled(bp)) + { + return cheri_setoffset(get_pointer_mangled(*(struct bp_array_s *)bp), 0); + } + void *ret = bp; + return cheri_setoffset(ret, 0); +} + +__attribute__((always_inline)) size_t get_base(bp_array bp) +{ + if (isMangled(bp)) + { + return get_base_mangled(*(struct bp_array_s *)bp); + } + return cheri_getoffset(bp); +} + +__attribute__((always_inline)) size_t get_length(bp_array bp) +{ + if (isMangled(bp)) + { + return get_length_mangled(*(struct bp_array_s *)bp); + } + return cheri_getlength(bp); +} + +#endif + +#ifndef __CHERI_PURE_CAPABILITY__ + +__attribute__((always_inline)) void callBP_dispatch(void (*function)(bp_array), bp_array args) +{ + function(args); +} + +#define callBP(function, pointer, base, length) \ + callBP_dispatch((function), packBP((pointer), (base), (length))) + +#else +__attribute__((always_inline)) void call_and_free(void (*function)(bp_array), void *descriptor) +{ + function(descriptor); + free(descriptor); + return; +} + +__attribute__((always_inline)) void callBP_dispatch(void (*function)(bp_array), void *pointer, + size_t base, size_t length) +{ + bp_array descriptor = packBP(pointer, base, length); + + if (0 == base) + { + call_and_free(function, descriptor); + return; + } + + function(descriptor); + return; +} + +#define callBP(function, pointer, base, length) (callBP_dispatch(function, pointer, base, length)) + +#endif + +#define RUN_LENGTH 64 + +size_t min(size_t a, size_t b); +bool isSorted(int arr[], size_t length); +void insertionSort(bp_array bp); +void merge(bp_array bp); +void timSort(bp_array bp); + +#endif \ No newline at end of file diff --git a/timsort.c b/timsort.c new file mode 100644 index 0000000..0cd0964 --- /dev/null +++ b/timsort.c @@ -0,0 +1,161 @@ +#include "include/timsort_lib.h" + +/** + * Sorts the input array, in place, using `insertion sort`. + * @param arr array to sort + */ +void insertionSort(bp_array bp) +{ + size_t base = get_base(bp); + size_t length = (get_length(bp) / sizeof(int)) - base; + + int *arr = get_pointer(bp); + inspect_pointer(arr); + + int ix, ixValue, ixP; + for (ix = base + 1; ix < (base + length); ix++) + { + ixValue = arr[ix]; + ixP = ix - 1; + + while (ixP >= 0 && arr[ixP] > ixValue) + { + arr[ixP + 1] = arr[ixP]; + ixP = ixP - 1; + } + arr[ixP + 1] = ixValue; + } +} + +/** + * Merges two runs of an array. + * @param arr super-array to merge + */ +void merge(bp_array bp) +{ + printf("merge \n"); + + // allocations + size_t lengthFirstHalf = get_base(bp); + size_t lengthSecondHalf = (get_length(bp) / sizeof(int)) - lengthFirstHalf; + + int *arr = get_pointer(bp); + + int firstHalf[lengthFirstHalf]; + int secondHalf[lengthSecondHalf]; + + // copy to intermediate storage + memcpy(&firstHalf[0], &arr[0], lengthFirstHalf * sizeof(int)); + memcpy(secondHalf, &arr[lengthFirstHalf], lengthSecondHalf * sizeof(int)); + + // merge intermediate back to output + size_t ix_fst = 0; + size_t ix_snd = 0; + size_t ix_out = 0; + while ((ix_fst < lengthFirstHalf) && (ix_snd < lengthSecondHalf)) + { + if (firstHalf[ix_fst] <= secondHalf[ix_snd]) + { + arr[ix_out++] = firstHalf[ix_fst++]; + } + else + { + arr[ix_out++] = secondHalf[ix_snd++]; + } + } + + // copy stragglers + if (ix_fst < lengthFirstHalf) + { + size_t delta = lengthFirstHalf - ix_fst; + memcpy(&arr[ix_out], &firstHalf[ix_fst], delta * sizeof(int)); + ix_out += delta; + ix_fst += delta; + } + + if (ix_snd < lengthSecondHalf) + { + size_t delta = lengthSecondHalf - ix_snd; + memcpy(&arr[ix_out], &secondHalf[ix_snd], delta * sizeof(int)); + ix_out += delta; + ix_snd += delta; + } + + return; +} + +/** + * Timsort routine for an array of `int`. + * @param arr Array to sort + */ +void timSort(bp_array arr) +{ + size_t length = get_length(arr) / sizeof(int); + + if (length <= 1) + { + return; + } + + // insertion sort on `RUN_LENGTH` segments + for (size_t ix = 0; ix < length; ix += RUN_LENGTH) + { + size_t max_ix = min((ix + RUN_LENGTH) + 1, length); + callBP(insertionSort, get_pointer(arr), ix, max_ix * sizeof(int)); + } + // Merge window doubles every iteration + for (size_t size = RUN_LENGTH; size < length; size *= 2) + { + // Merge + for (size_t left = 0; left + size < length - 1; left += 2 * size) + { + size_t mid = left + size; + size_t right = min((left + 2 * size), length); + + callBP(merge, &(get_pointer(arr)[left]), mid, right * sizeof(int)); + } + } +} + +/** + * Allocates a large chunk of memory, copies data to it and then sorts using + * `timsort`. The example uses two implementations, one that uses explicit parameters to encode run + * lengths and another that uses capabilities. + * @param arr array to print + * @return EXIT_SUCCESS on success. EXIT_FAILURE otherwise + */ +int main(int argc, char *argv[]) +{ + for (size_t sz = 1; sz <= MAX_ARRAY_SZ; sz++) + { + // place data to be sorted on the heap and make a copy + int *arr = random_chunk(sz); + int *arr_cpy = malloc(sz * sizeof(int)); + + assert(NULL != arr); + assert(NULL != arr_cpy); + + memcpy(arr_cpy, arr, sz * sizeof(int)); + + // check copy + assert(arrEq(arr, arr_cpy, 0, sz - 1)); + + // sort initial data set with TimSort + callBP(timSort, arr, 0, sz * sizeof(int)); + + // sort the copy with a stable quicksort + qsort(arr_cpy, sz, sizeof(int), cmpfunc); + + // check we have done real work + assert(isSorted(arr, sz)); + + // check the sorting is correct + assert(arrEq(arr, arr_cpy, 0, sz - 1)); + + // clean up + free(arr); + free(arr_cpy); + } + + return EXIT_SUCCESS; +} \ No newline at end of file From 4508318fcafe1875e9432ffb578a712959a6fcf1 Mon Sep 17 00:00:00 2001 From: Cristian Urlea Date: Thu, 28 Jan 2021 18:41:51 +0000 Subject: [PATCH 17/98] moved timsort example to own subfolder --- timsort/Makefile | 27 ++++++++++++++++++++++ {include => timsort/include}/timsort_lib.h | 2 +- timsort.c => timsort/timsort.c | 2 -- 3 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 timsort/Makefile rename {include => timsort/include}/timsort_lib.h (99%) rename timsort.c => timsort/timsort.c (99%) diff --git a/timsort/Makefile b/timsort/Makefile new file mode 100644 index 0000000..041e269 --- /dev/null +++ b/timsort/Makefile @@ -0,0 +1,27 @@ +CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang +CXX=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang++ +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O0 +SSHPORT=10003 +export + +cfiles := $(wildcard *.c) +cppfiles := $(wildcard *.cpp) +examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) + +.PHONY: all run clean + +all: $(examples) + +bin/%: %.c + $(CC) $(CFLAGS) $< -o $@ + +bin/%: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +.SECONDEXPANSION: +run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) + scp -P $(SSHPORT) $(word 2,$^) bin/$( #ifdef __CHERI_PURE_CAPABILITY__ -#include "common.h" +#include "../../include/common.h" #include #include #endif diff --git a/timsort.c b/timsort/timsort.c similarity index 99% rename from timsort.c rename to timsort/timsort.c index 0cd0964..2fa2600 100644 --- a/timsort.c +++ b/timsort/timsort.c @@ -33,8 +33,6 @@ void insertionSort(bp_array bp) */ void merge(bp_array bp) { - printf("merge \n"); - // allocations size_t lengthFirstHalf = get_base(bp); size_t lengthSecondHalf = (get_length(bp) / sizeof(int)) - lengthFirstHalf; From 7b7a4e92ce050f8baa1716f6075c1cb455a97ef0 Mon Sep 17 00:00:00 2001 From: Cristian Urlea Date: Thu, 28 Jan 2021 18:57:35 +0000 Subject: [PATCH 18/98] Removed explicit always_inline; Replaced -O0 with -02 --- timsort/Makefile | 2 +- timsort/include/timsort_lib.h | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/timsort/Makefile b/timsort/Makefile index 041e269..150e54b 100644 --- a/timsort/Makefile +++ b/timsort/Makefile @@ -1,6 +1,6 @@ CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang CXX=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang++ -CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O0 +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O2 SSHPORT=10003 export diff --git a/timsort/include/timsort_lib.h b/timsort/include/timsort_lib.h index 8171400..c30fcd2 100644 --- a/timsort/include/timsort_lib.h +++ b/timsort/include/timsort_lib.h @@ -111,17 +111,17 @@ struct bp_array_s packBP_mangled(void *pointer, size_t baseIndex, size_t sizeInB return ret; } -__attribute__((always_inline)) void *get_pointer_mangled(struct bp_array_s bp) +void *get_pointer_mangled(struct bp_array_s bp) { return bp.pointer; } -__attribute__((always_inline)) size_t get_base_mangled(struct bp_array_s bp) +size_t get_base_mangled(struct bp_array_s bp) { return bp.base; } -__attribute__((always_inline)) size_t get_length_mangled(struct bp_array_s bp) +size_t get_length_mangled(struct bp_array_s bp) { return bp.length; } @@ -138,7 +138,7 @@ typedef struct bp_array_s bp_array; typedef void *bp_array; -__attribute__((always_inline)) bp_array packBP(int *pointer, const size_t baseIndex, +bp_array packBP(int *pointer, const size_t baseIndex, const size_t sizeInBytes) { if (0 == baseIndex) @@ -173,12 +173,12 @@ __attribute__((always_inline)) bp_array packBP(int *pointer, const size_t baseIn return (bp_array)descriptor; } -__attribute__((always_inline)) bool isMangled(bp_array bp) +bool isMangled(bp_array bp) { return 0 == cheri_getoffset(bp); } -__attribute__((always_inline)) void *get_pointer(bp_array bp) +void *get_pointer(bp_array bp) { if (isMangled(bp)) { @@ -188,7 +188,7 @@ __attribute__((always_inline)) void *get_pointer(bp_array bp) return cheri_setoffset(ret, 0); } -__attribute__((always_inline)) size_t get_base(bp_array bp) +size_t get_base(bp_array bp) { if (isMangled(bp)) { @@ -197,7 +197,7 @@ __attribute__((always_inline)) size_t get_base(bp_array bp) return cheri_getoffset(bp); } -__attribute__((always_inline)) size_t get_length(bp_array bp) +size_t get_length(bp_array bp) { if (isMangled(bp)) { @@ -210,7 +210,7 @@ __attribute__((always_inline)) size_t get_length(bp_array bp) #ifndef __CHERI_PURE_CAPABILITY__ -__attribute__((always_inline)) void callBP_dispatch(void (*function)(bp_array), bp_array args) +void callBP_dispatch(void (*function)(bp_array), bp_array args) { function(args); } @@ -219,14 +219,14 @@ __attribute__((always_inline)) void callBP_dispatch(void (*function)(bp_array), callBP_dispatch((function), packBP((pointer), (base), (length))) #else -__attribute__((always_inline)) void call_and_free(void (*function)(bp_array), void *descriptor) +void call_and_free(void (*function)(bp_array), void *descriptor) { function(descriptor); free(descriptor); return; } -__attribute__((always_inline)) void callBP_dispatch(void (*function)(bp_array), void *pointer, +void callBP_dispatch(void (*function)(bp_array), void *pointer, size_t base, size_t length) { bp_array descriptor = packBP(pointer, base, length); From c1b2785669c55db253d59a3583ada3f03cb410ad Mon Sep 17 00:00:00 2001 From: Aleksandar Bonin Date: Thu, 28 Jan 2021 13:11:34 +0000 Subject: [PATCH 19/98] Organized the code. Rename some of the files as to make their purpose clear. --- shared_objects/Makefile | 19 ++++--- .../{lib2.c => compartment_per_object.c} | 7 +-- .../{lib2.h => compartment_per_object.h} | 0 .../include/{lib1.h => static_variable.h} | 0 shared_objects/main.c | 50 +++++++++++++++---- shared_objects/{lib1.c => static_variable.c} | 2 +- 6 files changed, 53 insertions(+), 25 deletions(-) rename shared_objects/{lib2.c => compartment_per_object.c} (95%) rename shared_objects/include/{lib2.h => compartment_per_object.h} (100%) rename shared_objects/include/{lib1.h => static_variable.h} (100%) rename shared_objects/{lib1.c => static_variable.c} (90%) diff --git a/shared_objects/Makefile b/shared_objects/Makefile index fda44fd..0264a9d 100644 --- a/shared_objects/Makefile +++ b/shared_objects/Makefile @@ -1,16 +1,19 @@ CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g -SSHPORT=10021 +SSHPORT=10007 main: main.c - $(CC) $(CFLAGS) -L. -l1 -l2 -g $< -o main + $(CC) $(CFLAGS) -O2 -fpie -L. -Wl,-rpath,. -ldl -lstatic_variable -lunexported_function -lcompartment_per_object -g $< -o main -lib1.so: lib1.c - $(CC) $(CFLAGS) -g -fPIC -shared $< -o lib1.so +libstatic_variable.so: static_variable.c include/static_variable.h + $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libstatic_variable.so -lib2.so: lib2.c - $(CC) $(CFLAGS) -g -fPIC -shared $< -o lib2.so +libunexported_function.so: unexported_function.c include/unexported_function.h + $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libunexported_function.so + +libcompartment_per_object.so: compartment_per_object.c include/compartment_per_object.h + $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libcompartment_per_object.so run: - scp -P $(SSHPORT) * root@127.0.0.1:/root/demo/ - ssh -p $(SSHPORT) root@127.0.0.1 -t 'setenv LD_LIBRARY_PATH /root/demo; /root/demo/main' + scp -r -P $(SSHPORT) * root@127.0.0.1:/root/demo/ + ssh -p $(SSHPORT) root@127.0.0.1 -t 'cd /root/demo; /root/demo/main' diff --git a/shared_objects/lib2.c b/shared_objects/compartment_per_object.c similarity index 95% rename from shared_objects/lib2.c rename to shared_objects/compartment_per_object.c index f0e86fd..1fcb51b 100644 --- a/shared_objects/lib2.c +++ b/shared_objects/compartment_per_object.c @@ -14,12 +14,7 @@ #include "../include/instructions.h" #include "../include/regs.h" -#include "include/lib2.h" - -static struct Car *arena; -static uint32_t size; - -void init() {} +#include "include/compartment_per_object.h" void crash() { diff --git a/shared_objects/include/lib2.h b/shared_objects/include/compartment_per_object.h similarity index 100% rename from shared_objects/include/lib2.h rename to shared_objects/include/compartment_per_object.h diff --git a/shared_objects/include/lib1.h b/shared_objects/include/static_variable.h similarity index 100% rename from shared_objects/include/lib1.h rename to shared_objects/include/static_variable.h diff --git a/shared_objects/main.c b/shared_objects/main.c index 8d58549..065ce21 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -2,22 +2,19 @@ * Displays how lib1 and lib2 will look like from the point of view of the users of the libraries. */ +#include #include #include #include +#include -#include "include/lib1.h" -#include "include/lib2.h" +#include "include/static_variable.h" +#include "include/compartment_per_object.h" +#include "include/unexported_function.h" +#include "include/find_sentries.h" -int main() -{ - struct Car *car = new_car(); - car->speed = 1; - car->honk(); - - car->speed = 999; - car->honk(); +void static_variables() { increment(); increment(); increment(); @@ -30,3 +27,36 @@ int main() printf("Count: %d\n", get_count()); } + +void compartments_per_object() { + struct Car *car = new_car(); + car->speed = 1; + car->honk(); + + car->speed = 999; + car->honk(); +} + +void unexported_functions() { + printf("Finding do_work using dlsym (main): "); inspect_pointer(dlsym(NULL, "do_work")); + printf("test: %d\n", test()); + + printf(" ------- scanning pcc for sealed pointers ------- \n"); + scan_range(cheri_getpcc()); +} + +int main() +{ + printf("Choose an exapmle: \n1. Static Variables\n2. OOP Compartments\n3. Unexported Functions\n> "); + uint32_t example = 0; + while(scanf("%u", &example) == 1) { + if(example == 1) { + static_variables(); + }else if(example == 2) { + compartments_per_object(); + }else if(example == 3) { + unexported_functions(); + } + printf("> "); + } +} diff --git a/shared_objects/lib1.c b/shared_objects/static_variable.c similarity index 90% rename from shared_objects/lib1.c rename to shared_objects/static_variable.c index f0c3336..1a434b1 100644 --- a/shared_objects/lib1.c +++ b/shared_objects/static_variable.c @@ -3,7 +3,7 @@ * cont variable will be inaccessible from the things that link with this shared library. */ -#include "include/lib1.h" +#include "include/static_variable.h" static int32_t count = -5; From e71313b8ab95ea1e615605c1281f73113caa05a4 Mon Sep 17 00:00:00 2001 From: Aleksandar Bonin Date: Thu, 28 Jan 2021 18:10:25 +0000 Subject: [PATCH 20/98] Add a new third example for the shared_libraries demo. This example prints out a table that displays the contents of the PLT at the current function, with the purpose of showing how that changes when calling a function in a different library. --- shared_objects/include/find_sentries.h | 27 ++++++++++++++++++ shared_objects/include/unexported_function.h | 6 ++++ shared_objects/unexported_function.c | 30 ++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 shared_objects/include/find_sentries.h create mode 100644 shared_objects/include/unexported_function.h create mode 100644 shared_objects/unexported_function.c diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h new file mode 100644 index 0000000..4ba2ae9 --- /dev/null +++ b/shared_objects/include/find_sentries.h @@ -0,0 +1,27 @@ +#include +#include + +#include "../../include/common.h" + +bool is_pointer(void *ptr) +{ + if (cheri_gettag(ptr)) + { + return true; + } + return false; +} + +void scan_range(void * ptr) +{ + for(void* iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); iter = cheri_incoffset(iter, 16)) + { + if (is_pointer(*(void**)iter)) + { + if(cheri_getsealed(*(void**)iter)) { + inspect_pointer(*(void**)iter); + } + } + } +} + diff --git a/shared_objects/include/unexported_function.h b/shared_objects/include/unexported_function.h new file mode 100644 index 0000000..96caa91 --- /dev/null +++ b/shared_objects/include/unexported_function.h @@ -0,0 +1,6 @@ + +__attribute__ ((visibility ("hidden"))) +void do_work(); + +int test(); + diff --git a/shared_objects/unexported_function.c b/shared_objects/unexported_function.c new file mode 100644 index 0000000..0cf7906 --- /dev/null +++ b/shared_objects/unexported_function.c @@ -0,0 +1,30 @@ +/** + * An example showing how functions put into different shared objects are in different compartments + * based on the function pointers that can be observed in the PCC of the current function. The + * range represented by the PCC is scanned twice, once inside of the function and once outside to + * show that the two differ. + */ + +#include +#include +#include "include/unexported_function.h" +#include "include/find_sentries.h" + +__attribute__ ((visibility ("hidden"))) +void do_work() { + printf("Doing work\n"); +} + +int test() { + + + + printf("Finding do_work using dlsym (lib4):\nptr: "); inspect_pointer(dlsym(NULL, "do_work")); + printf("Obtaining a direct pointer to it: \nptr: "); inspect_pointer(&do_work); + printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); + printf(" ------- scanning pcc for sealed pointers --------\n"); + scan_range(cheri_getpcc()); + + do_work(); + return 99; +} From 8e498091f9a6d20ece4b8b577193777c67d4eca0 Mon Sep 17 00:00:00 2001 From: Aleksandar Bonin Date: Mon, 1 Feb 2021 11:41:44 +0000 Subject: [PATCH 21/98] Turn the example into an automatic test, and an example. To make it an automatic test, a pointer to the do_work function with a cleared tag is returned and used to scan the pcc, and determine if there is a capability with the same address. --- shared_objects/include/find_sentries.h | 18 ++++++++++++++---- shared_objects/include/unexported_function.h | 2 +- shared_objects/main.c | 7 +++++-- shared_objects/unexported_function.c | 17 ++++++++++------- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index 4ba2ae9..e94ec3c 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -12,16 +12,26 @@ bool is_pointer(void *ptr) return false; } -void scan_range(void * ptr) +bool scan_range(void * ptr, void * exact) { + bool found = false; for(void* iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); iter = cheri_incoffset(iter, 16)) { - if (is_pointer(*(void**)iter)) + void* current = *(void**)iter; + if (is_pointer(current)) { - if(cheri_getsealed(*(void**)iter)) { - inspect_pointer(*(void**)iter); + if(cheri_getsealed(current)) { + if(cheri_getaddress(current) == cheri_getaddress(exact)) { + found = true; + printf("[Exact match] "); + }else if(cheri_gettop(current) == cheri_gettop(exact) && cheri_getbase(current) == cheri_getbase(exact)) { + printf("[Range match] "); + } + + inspect_pointer(current); } } } + return found; } diff --git a/shared_objects/include/unexported_function.h b/shared_objects/include/unexported_function.h index 96caa91..81cf22d 100644 --- a/shared_objects/include/unexported_function.h +++ b/shared_objects/include/unexported_function.h @@ -2,5 +2,5 @@ __attribute__ ((visibility ("hidden"))) void do_work(); -int test(); +void* test(); diff --git a/shared_objects/main.c b/shared_objects/main.c index 065ce21..c14180d 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "include/static_variable.h" #include "include/compartment_per_object.h" @@ -39,10 +40,12 @@ void compartments_per_object() { void unexported_functions() { printf("Finding do_work using dlsym (main): "); inspect_pointer(dlsym(NULL, "do_work")); - printf("test: %d\n", test()); + void* function_to_search_for = test(); + printf("test: %p\n", function_to_search_for); printf(" ------- scanning pcc for sealed pointers ------- \n"); - scan_range(cheri_getpcc()); + bool found = scan_range(cheri_getpcc(), function_to_search_for); + assert(found == false); } int main() diff --git a/shared_objects/unexported_function.c b/shared_objects/unexported_function.c index 0cf7906..bae60a1 100644 --- a/shared_objects/unexported_function.c +++ b/shared_objects/unexported_function.c @@ -5,8 +5,11 @@ * show that the two differ. */ +#include #include +#include #include +#include #include "include/unexported_function.h" #include "include/find_sentries.h" @@ -15,16 +18,16 @@ void do_work() { printf("Doing work\n"); } -int test() { - - - +void* test() { printf("Finding do_work using dlsym (lib4):\nptr: "); inspect_pointer(dlsym(NULL, "do_work")); printf("Obtaining a direct pointer to it: \nptr: "); inspect_pointer(&do_work); printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); printf(" ------- scanning pcc for sealed pointers --------\n"); - scan_range(cheri_getpcc()); - + bool found = scan_range(cheri_getpcc(), &do_work); + assert(found == true); do_work(); - return 99; + + // We return a non-tagged capability to show + // and automatically test if there is outside access. + return cheri_cleartag(&do_work); } From 1ddf4604595b31c97b72faadb71a8e637d75d925 Mon Sep 17 00:00:00 2001 From: Dejice Jacob Date: Tue, 9 Feb 2021 22:57:21 +0000 Subject: [PATCH 22/98] Adding CMake to build all examples. 1. cmake build file with riscv toolchain file (currently only one supported) 2. added default sdk-path to cmake config - allows easy retargetting of examples for various target architectures/libraries/sdk/toolchains - allows use of Make or Ninja as the underlying build system. --- CMakeLists.txt | 23 +++++++++++++++++++++++ README.md | 27 +++++++++++++++++++++++++++ riscv64-purecap.cmake | 23 +++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 riscv64-purecap.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..747f41d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required (VERSION 3.15.0) + +project(CHERI_EXAMPLES + VERSION 0.1 + DESCRIPTION "Stub examples showing how to exploit capabilities" + LANGUAGES C) + +get_filename_component(COMP_NAME ${CMAKE_C_COMPILER} NAME_WE) + +add_executable(allocate allocate.c) +add_executable(check_length check_length.c) +add_executable(check_mask check_mask.c) +add_executable(function function.c) +add_executable(hello hello.c) +add_executable(mmap mmap.c) +if( ${COMP_NAME} MATCHES "^mips*" ) + add_executable(seal seal.c) +endif() +add_executable(sentry sentry.c) +add_executable(set_bounds set_bounds.c) +add_executable(setjmp setjmp.c) +add_executable(stackscan stackscan.c) +add_executable(xor_pointers xor_pointers.c) diff --git a/README.md b/README.md index df8ea5d..eb67f4c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,29 @@ # cheri-examples cheri-riscv sample c programs + +------ +## Build instructions with CMake +Build all the example code with CMake using default settings : +1. Create a separate build directory (\) outside the source directory (\). +2. Generate the build files using either **Unix Makefiles** (*default*) or **Ninja** (specfied + with **-G**). Currently only the *RISC-V* cheri-purecap toolchain file has been created. + 1. If executing from inside the build directory : + + `cmake -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake ` + + 2. If executing from outside the build directory : + + `cmake -B -S -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake` + +3. Build all the examples + 1. If executing from inside the build directory, `make all` *OR* `ninja all` + 2. If executing from outside the build directory, `cmake --build ` + +#### CMake options: +* **-DCMAKE_TOOLCHAIN_FILE**: Use `-DCMAKE_TOOLCHAIN_FILE=` to select + architecture to compile binary for. Only *riscv64-purecap.cmake* is currently supported. +* **-DSDK**: Use `-DSDK=` to point to the *CHERI* SDK directory. + (Default path: ${HOME}/cheri/output/sdk) +* **-G**: Choose preferred build system + - Use `-G "Unix Makefiles"` to build using *makefiles* (*default*). + - Use `-G Ninja` to build using *ninja*. diff --git a/riscv64-purecap.cmake b/riscv64-purecap.cmake new file mode 100644 index 0000000..bb6cc4f --- /dev/null +++ b/riscv64-purecap.cmake @@ -0,0 +1,23 @@ +set(CMAKE_SYSTEM_NAME Linux) + +# Default SDK path +set(SDK "$ENV{HOME}/cheri/output/sdk" CACHE PATH "path to cheri SDK") + +# Set toolchain compilers +set(CMAKE_C_COMPILER ${SDK}/bin/riscv64-unknown-freebsd13-cc) +set(CMAKE_CXX_COMPILER ${SDK}/bin/riscv64-unknown-freebsd13-c++) + +# Don't run the linker on compiler check +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Define sysroot path for CHERI-build` +set(CMAKE_SYSROOT ${SDK}/sysroot-riscv64-purecap) + +# Use only cross compiler tools for compilation and linking +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Set correct machine and abi flags +add_compile_options(-march=rv64imafdcxcheri -mabi=l64pc128d -mno-relax) +add_link_options(-march=rv64imafdcxcheri -mabi=l64pc128d -mno-relax) From d854975a80fdef70cd310da3662a60c5d365afc8 Mon Sep 17 00:00:00 2001 From: Aleksandar Bonin Date: Mon, 22 Feb 2021 14:25:42 +0000 Subject: [PATCH 23/98] Integrate the new compilation process, and add the cmake build to the buildbot script. Switched from using `riscv64-unknown-freebsd13-clang` to `clang --config cheribsd-riscv64-purcap.cfg". Now the only linker that can be used is llvm's lld by doing `-fuse-ld=lld`. --- .buildbot.sh | 9 +++++++++ Makefile | 8 ++++---- riscv64-purecap.cmake | 10 +++++----- shared_objects/Makefile | 4 ++-- 4 files changed, 20 insertions(+), 11 deletions(-) mode change 100644 => 100755 .buildbot.sh diff --git a/.buildbot.sh b/.buildbot.sh old mode 100644 new mode 100755 index 8fd3391..7503c35 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -9,3 +9,12 @@ for example in *.c; do fi make bin/"${example%%.*}" done + +mkdir build +cd build + +cmake -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake .. +make + +cd .. +rm -rfv build diff --git a/Makefile b/Makefile index b344b7c..0c4adc1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang -CXX=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang++ -CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-purecap -mno-relax -g -O0 +CC=$(HOME)/cheri/output/sdk/bin/clang +CXX=$(HOME)/cheri/output/sdk/bin/clang++ +CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 export @@ -24,4 +24,4 @@ run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/$( Date: Fri, 26 Feb 2021 13:41:22 +0000 Subject: [PATCH 24/98] Rename the builder used for status for bors. --- bors.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bors.toml b/bors.toml index 805b481..dcc1523 100644 --- a/bors.toml +++ b/bors.toml @@ -1,4 +1,4 @@ -status = ["buildbot/cheri-build-script"] +status = ["buildbot/capablevms-test-script"] timeout_sec = 600 # 10 minutes From d538231c3a07d7639b803a96c209a7c0f9cf9a9b Mon Sep 17 00:00:00 2001 From: Dejice Jacob Date: Wed, 24 Feb 2021 14:48:52 +0000 Subject: [PATCH 25/98] Added Morello purecap cmake toolchain file --- .buildbot.sh | 21 ++++++++++++++------- README.md | 12 ++++++++---- morello-purecap.cmake | 20 ++++++++++++++++++++ riscv64-purecap.cmake | 3 --- 4 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 morello-purecap.cmake diff --git a/.buildbot.sh b/.buildbot.sh index 7503c35..e786aad 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -10,11 +10,18 @@ for example in *.c; do make bin/"${example%%.*}" done -mkdir build -cd build - -cmake -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake .. -make +# arg-1 : Source directory +# arg-2 : cmake toolchain file +function build_cheri_examples() +{ + ARCH=$(echo ${2} | cut -d '.' -f 1) + BUILD_DIR=${1}/build/${ARCH} -cd .. -rm -rfv build + mkdir -p ${BUILD_DIR} + cmake -B ${BUILD_DIR} -S ${1} -DCMAKE_TOOLCHAIN_FILE=${2} + cmake --build ${BUILD_DIR} +} + +build_cheri_examples $(pwd) riscv64-purecap.cmake +build_cheri_examples $(pwd) morello-purecap.cmake +rm -rfv $(pwd)/build diff --git a/README.md b/README.md index eb67f4c..c169aaf 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # cheri-examples -cheri-riscv sample c programs +cheri sample C programs for RISC-V and Morello targets in *pure capability* (purecap) mode. ------ ## Build instructions with CMake Build all the example code with CMake using default settings : 1. Create a separate build directory (\) outside the source directory (\). 2. Generate the build files using either **Unix Makefiles** (*default*) or **Ninja** (specfied - with **-G**). Currently only the *RISC-V* cheri-purecap toolchain file has been created. + with **-G**). Currently only the *RISC-V* and *Morello* cheri-purecap toolchain files have + been created. 1. If executing from inside the build directory : `cmake -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake ` @@ -21,9 +22,12 @@ Build all the example code with CMake using default settings : #### CMake options: * **-DCMAKE_TOOLCHAIN_FILE**: Use `-DCMAKE_TOOLCHAIN_FILE=` to select - architecture to compile binary for. Only *riscv64-purecap.cmake* is currently supported. + architecture to compile binary for. Only *riscv64-purecap.cmake* and *morello-purecap.cmake* + are currently supported. * **-DSDK**: Use `-DSDK=` to point to the *CHERI* SDK directory. - (Default path: ${HOME}/cheri/output/sdk) + Default paths: + 1. *RISC-V*: ${HOME}/cheri/output/sdk + 2. *Morello*: ${HOME}/cheri/output/morello-sdk * **-G**: Choose preferred build system - Use `-G "Unix Makefiles"` to build using *makefiles* (*default*). - Use `-G Ninja` to build using *ninja*. diff --git a/morello-purecap.cmake b/morello-purecap.cmake new file mode 100644 index 0000000..b7a2ab8 --- /dev/null +++ b/morello-purecap.cmake @@ -0,0 +1,20 @@ +set(CMAKE_SYSTEM_NAME Linux) + +# Default SDK path +set(SDK "$ENV{HOME}/cheri/output/morello-sdk" CACHE PATH "path to cheri SDK") + +# Set toolchain compilers +set(CMAKE_C_COMPILER ${SDK}/bin/clang) +set(CMAKE_CXX_COMPILER ${SDK}/bin/clang++) + +# Don't run the linker on compiler check +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# Use only cross compiler tools for compilation and linking +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Set correct machine and abi flags +add_compile_options(--config cheribsd-morello-purecap.cfg) +add_link_options(-fuse-ld=lld --config cheribsd-morello-purecap.cfg) diff --git a/riscv64-purecap.cmake b/riscv64-purecap.cmake index 6494ebc..995b9a1 100644 --- a/riscv64-purecap.cmake +++ b/riscv64-purecap.cmake @@ -10,9 +10,6 @@ set(CMAKE_CXX_COMPILER ${SDK}/bin/clang++) # Don't run the linker on compiler check set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) -# Define sysroot path for CHERI-build` -# set(CMAKE_SYSROOT ${SDK}/sysroot-riscv64-purecap) - # Use only cross compiler tools for compilation and linking set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) From ab1f9c85cebfd2a7ea74c5ac260a8df45607231d Mon Sep 17 00:00:00 2001 From: Aleksandar Bonin Date: Wed, 10 Mar 2021 18:02:25 +0000 Subject: [PATCH 26/98] Add formatting and add a clang format check. Buildbot should now check if things are formated. --- .buildbot.sh | 2 + check_length.c | 6 +- check_mask.c | 2 +- hello-cxx.cpp | 13 +- include/common.h | 25 +- include/instructions.h | 1977 +++++++++-------- include/regs.h | 129 +- mmap.c | 2 +- sentry.c | 2 +- setjmp.c | 2 +- shared_objects/compartment_per_object.c | 8 +- .../include/compartment_per_object.h | 14 +- shared_objects/include/find_sentries.h | 21 +- shared_objects/include/static_variable.h | 6 +- shared_objects/include/unexported_function.h | 6 +- shared_objects/main.c | 46 +- shared_objects/unexported_function.c | 31 +- timsort/include/timsort_lib.h | 6 +- 18 files changed, 1251 insertions(+), 1047 deletions(-) diff --git a/.buildbot.sh b/.buildbot.sh index e786aad..5b6fba6 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -2,6 +2,8 @@ # vim: expandtab sts=0 sw=4 smarttab set -e +find . -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" -o -iname "*.hpp" | xargs ~/cheri/output/sdk/bin/clang-format --dry-run -Werror + mkdir -p ./bin for example in *.c; do if [ "${example}" == "seal.c" ]; then diff --git a/check_length.c b/check_length.c index 4b4202a..066bd55 100644 --- a/check_length.c +++ b/check_length.c @@ -1,12 +1,12 @@ /*** - * Testing to see what the representable - * lengths function returns for different + * Testing to see what the representable + * lengths function returns for different * values. */ +#include #include #include -#include int main() { diff --git a/check_mask.c b/check_mask.c index 9e9f46c..a0897e5 100644 --- a/check_mask.c +++ b/check_mask.c @@ -1,5 +1,5 @@ /*** - * A example to see what the representable alignment mask is and + * A example to see what the representable alignment mask is and * how it changes the alignment based on how high in memory it is. */ diff --git a/hello-cxx.cpp b/hello-cxx.cpp index 6af1019..d27976a 100644 --- a/hello-cxx.cpp +++ b/hello-cxx.cpp @@ -1,13 +1,14 @@ +#include "include/common.h" +#include #include #include -#include -#include "include/common.h" -int main() { - std::vector list = { 1, 2, 3, 4 }; - for(unsigned int i = 0; i <= 16; i++) { +int main() +{ + std::vector list = {1, 2, 3, 4}; + for (unsigned int i = 0; i <= 16; i++) + { inspect_pointer(&list[i]); std::cout << "Value: " << list[i] << std::endl; } - } diff --git a/include/common.h b/include/common.h index 6fd4a81..47f85da 100644 --- a/include/common.h +++ b/include/common.h @@ -1,9 +1,9 @@ -#include -#include -#include -#include +#include +#include +#include +#include -#include +#include void inspect_pointer(void *ptr) { @@ -12,28 +12,27 @@ void inspect_pointer(void *ptr) uint64_t base = cheri_getbase(ptr); uint64_t flags = cheri_getflags(ptr); uint64_t perms = cheri_getperm(ptr); - uint64_t type = cheri_gettype(ptr); + uint64_t type = cheri_gettype(ptr); bool tag = cheri_gettag(ptr); uint64_t offset = cheri_getoffset(ptr); - printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, Perms: %04lx, Type: %04lx, Tag: %d\n", - address, base, base + length, flags, length, offset, perms, type, tag); + printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, " + "Perms: %04lx, Type: %04lx, Tag: %d\n", + address, base, base + length, flags, length, offset, perms, type, tag); } - -void error(char* string) { +void error(char *string) +{ fputs(string, stderr); fputc('\n', stderr); } - // This function returns the current stack pointer. // // We don't want to inline it because __builtin_frame_address will change it's meaning. // __builtin_frame_address gives you the stack bottom of the current function. // This will be the stack top of the calling function. -__attribute__((noinline)) void* cheri_getcsp() +__attribute__((noinline)) void *cheri_getcsp() { return __builtin_frame_address(0); } - diff --git a/include/instructions.h b/include/instructions.h index 24fdb87..9f2ba1c 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -1,1463 +1,1650 @@ -#include +#include - - uint32_t add(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00000033; // 33 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t add(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00000033; // 33 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t addi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000013; // 13 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t addi(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00000013; // 13 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t addiw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000001B; // 1B 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t addiw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0000001B; // 1B 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t addw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000003B; // 3B 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t addw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0000003B; // 3B 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t and(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00007033; // 33 70 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t and (uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00007033; // 33 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t andi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00007013; // 13 70 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t andi(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00007013; // 13 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t auipc(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000017; // 17 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( imm >> 0 ) & 0b11111111111111111111) << 12); +uint32_t auipc(uint32_t rd, uint32_t imm) +{ + uint32_t i = 0x00000017; // 17 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); return i; } -uint32_t auipcc(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000017; // 17 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( imm >> 0 ) & 0b11111111111111111111) << 12); +uint32_t auipcc(uint32_t rd, uint32_t imm) +{ + uint32_t i = 0x00000017; // 17 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); return i; } -uint32_t beq(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000063; // 63 00 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t beq(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00000063; // 63 00 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t bge(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00005063; // 63 50 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t bge(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00005063; // 63 50 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t bgeu(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00007063; // 63 70 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t bgeu(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00007063; // 63 70 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t blt(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004063; // 63 40 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t blt(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00004063; // 63 40 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t bltu(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00006063; // 63 60 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t bltu(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00006063; // 63 60 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t bne(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001063; // 63 10 00 00 - i |= ((( imm >> 10 ) & 0b1) << 7); - i |= ((( imm >> 0 ) & 0b1111) << 8); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 4 ) & 0b111111) << 25); - i |= ((( imm >> 11 ) & 0b1) << 31); +uint32_t bne(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00001063; // 63 10 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); return i; } -uint32_t candperm(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1A00005B; // 5B 00 00 1A - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t candperm(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1A00005B; // 5B 00 00 1A + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cbuildcap(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3A00005B; // 5B 00 00 3A - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cbuildcap(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x3A00005B; // 5B 00 00 3A + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ccseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3E00005B; // 5B 00 00 3E - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ccseal(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x3E00005B; // 5B 00 00 3E + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ccall(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xFC0000DB; // DB 00 00 FC - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ccall(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xFC0000DB; // DB 00 00 FC + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ccleartag(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEB0005B; // 5B 00 B0 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t ccleartag(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFEB0005B; // 5B 00 B0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t ccopytype(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3C00005B; // 5B 00 00 3C - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ccopytype(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x3C00005B; // 5B 00 00 3C + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cfld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003007; // 07 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t cfld(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003007; // 07 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t cflw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002007; // 07 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t cflw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00002007; // 07 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t cfsd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003027; // 27 30 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t cfsd(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00003027; // 27 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t cfsw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002027; // 27 20 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t cfsw(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00002027; // 27 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t cfromptr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2600005B; // 5B 00 00 26 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cfromptr(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x2600005B; // 5B 00 00 26 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cgetaddr(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEF0005B; // 5B 00 F0 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetaddr(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFEF0005B; // 5B 00 F0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetbase(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE20005B; // 5B 00 20 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetbase(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE20005B; // 5B 00 20 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetflags(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE70005B; // 5B 00 70 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetflags(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE70005B; // 5B 00 70 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetlen(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE30005B; // 5B 00 30 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetlen(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE30005B; // 5B 00 30 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetoffset(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE60005B; // 5B 00 60 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetoffset(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE60005B; // 5B 00 60 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetperm(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE00005B; // 5B 00 00 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetperm(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE00005B; // 5B 00 00 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgetsealed(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE50005B; // 5B 00 50 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgetsealed(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE50005B; // 5B 00 50 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgettag(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE40005B; // 5B 00 40 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgettag(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE40005B; // 5B 00 40 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cgettype(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE10005B; // 5B 00 10 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cgettype(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE10005B; // 5B 00 10 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cincoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2200005B; // 5B 00 00 22 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cincoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x2200005B; // 5B 00 00 22 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cincoffsetimm(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000105B; // 5B 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t cincoffsetimm(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0000105B; // 5B 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t cjalr(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEC0005B; // 5B 00 C0 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cjalr(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFEC0005B; // 5B 00 C0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t clb(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000003; // 03 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clb(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00000003; // 03 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clbu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00004003; // 03 40 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clbu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00004003; // 03 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clc_128(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000200F; // 0F 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clc_128(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0000200F; // 0F 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clc_64(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clc_64(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t cld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t cld(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clh(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001003; // 03 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clh(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00001003; // 03 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clhu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005003; // 03 50 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clhu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00005003; // 03 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002003; // 03 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00002003; // 03 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t clwu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006003; // 03 60 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t clwu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00006003; // 03 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t cmove(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEA0005B; // 5B 00 A0 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cmove(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFEA0005B; // 5B 00 A0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t cram(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE90005B; // 5B 00 90 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t cram(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE90005B; // 5B 00 90 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t crrl(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE80005B; // 5B 00 80 FE - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t crrl(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFE80005B; // 5B 00 80 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t csb(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000023; // 23 00 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csb(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00000023; // 23 00 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t csc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004023; // 23 40 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00004023; // 23 40 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t csc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t csd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csd(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t cseqx(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4200005B; // 5B 00 00 42 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cseqx(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x4200005B; // 5B 00 00 42 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csh(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001023; // 23 10 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csh(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00001023; // 23 10 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t csrrc(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003073; // 73 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrc(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003073; // 73 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csrrci(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00007073; // 73 70 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrci(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00007073; // 73 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csrrs(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002073; // 73 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrs(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00002073; // 73 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csrrsi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006073; // 73 60 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrsi(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00006073; // 73 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csrrw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001073; // 73 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00001073; // 73 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csrrwi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005073; // 73 50 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csrrwi(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00005073; // 73 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002023; // 23 20 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t csw(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00002023; // 23 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t cseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1600005B; // 5B 00 00 16 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cseal(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1600005B; // 5B 00 00 16 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csealentry(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFF10005B; // 5B 00 10 FF - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t csealentry(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFF10005B; // 5B 00 10 FF + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t csetaddr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2000005B; // 5B 00 00 20 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csetaddr(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x2000005B; // 5B 00 00 20 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csetbounds(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1000005B; // 5B 00 00 10 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csetbounds(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1000005B; // 5B 00 00 10 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csetboundsexact(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1200005B; // 5B 00 00 12 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csetboundsexact(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1200005B; // 5B 00 00 12 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csetboundsimm(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000205B; // 5B 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t csetboundsimm(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0000205B; // 5B 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t csetflags(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1C00005B; // 5B 00 00 1C - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csetflags(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1C00005B; // 5B 00 00 1C + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t csetoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1E00005B; // 5B 00 00 1E - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csetoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1E00005B; // 5B 00 00 1E + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cspecialrw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0200005B; // 5B 00 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b11111) << 20); +uint32_t cspecialrw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0200005B; // 5B 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b11111) << 20); return i; } -uint32_t csub(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2800005B; // 5B 00 00 28 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t csub(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x2800005B; // 5B 00 00 28 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ctestsubset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000005B; // 5B 00 00 40 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ctestsubset(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x4000005B; // 5B 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ctoptr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2400005B; // 5B 00 00 24 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ctoptr(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x2400005B; // 5B 00 00 24 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t cunseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1800005B; // 5B 00 00 18 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t cunseal(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x1800005B; // 5B 00 00 18 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t clear(uint32_t quarter, uint32_t mask) { - uint32_t i = 0xFED0005B; // 5B 00 D0 FE - i |= ((( mask >> 0 ) & 0b11111) << 7); - i |= ((( mask >> 5 ) & 0b111) << 15); - i |= ((( quarter >> 0 ) & 0b11) << 18); +uint32_t clear(uint32_t quarter, uint32_t mask) +{ + uint32_t i = 0xFED0005B; // 5B 00 D0 FE + i |= (((mask >> 0) & 0b11111) << 7); + i |= (((mask >> 5) & 0b111) << 15); + i |= (((quarter >> 0) & 0b11) << 18); return i; } -uint32_t asm_div(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02004033; // 33 40 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t asm_div(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02004033; // 33 40 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t divu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02005033; // 33 50 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t divu(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02005033; // 33 50 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t divuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200503B; // 3B 50 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t divuw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0200503B; // 3B 50 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t divw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200403B; // 3B 40 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t divw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0200403B; // 3B 40 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t dret() { - uint32_t i = 0x7B200073; // 73 00 20 7B - - return i; -} +uint32_t dret() +{ + uint32_t i = 0x7B200073; // 73 00 20 7B -uint32_t ebreak() { - uint32_t i = 0x00100073; // 73 00 10 00 - return i; } -uint32_t ecall() { - uint32_t i = 0x00000073; // 73 00 00 00 - - return i; -} +uint32_t ebreak() +{ + uint32_t i = 0x00100073; // 73 00 10 00 -uint32_t fence(uint32_t succ, uint32_t pred) { - uint32_t i = 0x0000000F; // 0F 00 00 00 - i |= ((( succ >> 0 ) & 0b1111) << 20); - i |= ((( pred >> 0 ) & 0b1111) << 24); return i; } -uint32_t fence_i() { - uint32_t i = 0x0000100F; // 0F 10 00 00 - +uint32_t ecall() +{ + uint32_t i = 0x00000073; // 73 00 00 00 + return i; } -uint32_t fence_tso() { - uint32_t i = 0x8330000F; // 0F 00 30 83 - +uint32_t fence(uint32_t succ, uint32_t pred) +{ + uint32_t i = 0x0000000F; // 0F 00 00 00 + i |= (((succ >> 0) & 0b1111) << 20); + i |= (((pred >> 0) & 0b1111) << 24); return i; } -uint32_t fpclear(uint32_t quarter, uint32_t mask) { - uint32_t i = 0xFF00005B; // 5B 00 00 FF - i |= ((( mask >> 0 ) & 0b11111) << 7); - i |= ((( mask >> 5 ) & 0b111) << 15); - i |= ((( quarter >> 0 ) & 0b11) << 18); +uint32_t fence_i() +{ + uint32_t i = 0x0000100F; // 0F 10 00 00 + return i; } -uint32_t jal(uint32_t rd, uint32_t imm) { - uint32_t i = 0x0000006F; // 6F 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( imm >> 11 ) & 0b11111111) << 12); - i |= ((( imm >> 10 ) & 0b1) << 20); - i |= ((( imm >> 0 ) & 0b1111111111) << 21); - i |= ((( imm >> 19 ) & 0b1) << 31); +uint32_t fence_tso() +{ + uint32_t i = 0x8330000F; // 0F 00 30 83 + return i; } -uint32_t jalr(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000067; // 67 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t fpclear(uint32_t quarter, uint32_t mask) +{ + uint32_t i = 0xFF00005B; // 5B 00 00 FF + i |= (((mask >> 0) & 0b11111) << 7); + i |= (((mask >> 5) & 0b111) << 15); + i |= (((quarter >> 0) & 0b11) << 18); return i; } -uint32_t lb(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000003; // 03 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t jal(uint32_t rd, uint32_t imm) +{ + uint32_t i = 0x0000006F; // 6F 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 11) & 0b11111111) << 12); + i |= (((imm >> 10) & 0b1) << 20); + i |= (((imm >> 0) & 0b1111111111) << 21); + i |= (((imm >> 19) & 0b1) << 31); return i; } -uint32_t lbu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00004003; // 03 40 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t jalr(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00000067; // 67 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lbu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAC0005B; // 5B 00 C0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lb(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00000003; // 03 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lbu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA40005B; // 5B 00 40 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lbu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00004003; // 03 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lb_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA80005B; // 5B 00 80 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lbu_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAC0005B; // 5B 00 C0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lb_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA00005B; // 5B 00 00 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lbu_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA40005B; // 5B 00 40 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lc_128(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000200F; // 0F 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t lb_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA80005B; // 5B 00 80 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lc_64(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t lb_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA00005B; // 5B 00 00 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lc_cap_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBF0005B; // 5B 00 F0 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lc_128(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x0000200F; // 0F 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lc_cap_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAB0005B; // 5B 00 B0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lc_64(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lc_ddc_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB70005B; // 5B 00 70 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lc_cap_128(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFBF0005B; // 5B 00 F0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lc_ddc_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA30005B; // 5B 00 30 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lc_cap_64(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAB0005B; // 5B 00 B0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t ld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t lc_ddc_128(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB70005B; // 5B 00 70 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t ld_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAB0005B; // 5B 00 B0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lc_ddc_64(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA30005B; // 5B 00 30 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t ld_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA30005B; // 5B 00 30 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t ld(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lh(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001003; // 03 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t ld_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAB0005B; // 5B 00 B0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lhu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005003; // 03 50 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t ld_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA30005B; // 5B 00 30 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lhu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAD0005B; // 5B 00 D0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lh(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00001003; // 03 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lhu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA50005B; // 5B 00 50 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lhu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00005003; // 03 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lh_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA90005B; // 5B 00 90 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lhu_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAD0005B; // 5B 00 D0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lh_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA10005B; // 5B 00 10 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lhu_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA50005B; // 5B 00 50 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_b_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB80005B; // 5B 00 80 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lh_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA90005B; // 5B 00 90 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_b_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB00005B; // 5B 00 00 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lh_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA10005B; // 5B 00 10 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_c_cap_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBC0005B; // 5B 00 C0 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_b_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB80005B; // 5B 00 80 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_c_cap_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBB0005B; // 5B 00 B0 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_b_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB00005B; // 5B 00 00 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_c_ddc_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB40005B; // 5B 00 40 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_c_cap_128(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFBC0005B; // 5B 00 C0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_c_ddc_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB30005B; // 5B 00 30 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_c_cap_64(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFBB0005B; // 5B 00 B0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_d_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBB0005B; // 5B 00 B0 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_c_ddc_128(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB40005B; // 5B 00 40 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_d_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB30005B; // 5B 00 30 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_c_ddc_64(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB30005B; // 5B 00 30 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_h_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB90005B; // 5B 00 90 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_d_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFBB0005B; // 5B 00 B0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_h_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB10005B; // 5B 00 10 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_d_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB30005B; // 5B 00 30 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_w_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBA0005B; // 5B 00 A0 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_h_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB90005B; // 5B 00 90 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lr_w_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB20005B; // 5B 00 20 FB - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lr_h_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB10005B; // 5B 00 10 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lui(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000037; // 37 00 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( imm >> 0 ) & 0b11111111111111111111) << 12); +uint32_t lr_w_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFBA0005B; // 5B 00 A0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002003; // 03 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t lr_w_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFB20005B; // 5B 00 20 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lwu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006003; // 03 60 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t lui(uint32_t rd, uint32_t imm) +{ + uint32_t i = 0x00000037; // 37 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); return i; } -uint32_t lwu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAE0005B; // 5B 00 E0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lw(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00002003; // 03 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lwu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA60005B; // 5B 00 60 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lwu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00006003; // 03 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t lw_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAA0005B; // 5B 00 A0 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lwu_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAE0005B; // 5B 00 E0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t lw_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA20005B; // 5B 00 20 FA - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); +uint32_t lwu_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA60005B; // 5B 00 60 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t mret() { - uint32_t i = 0x30200073; // 73 00 20 30 - +uint32_t lw_cap(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFAA0005B; // 5B 00 A0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t mul(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02000033; // 33 00 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t lw_ddc(uint32_t rd, uint32_t rs1) +{ + uint32_t i = 0xFA20005B; // 5B 00 20 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); return i; } -uint32_t mulh(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02001033; // 33 10 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t mret() +{ + uint32_t i = 0x30200073; // 73 00 20 30 + return i; } -uint32_t mulhsu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02002033; // 33 20 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t mul(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02000033; // 33 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t mulhu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02003033; // 33 30 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t mulh(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02001033; // 33 10 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t mulw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200003B; // 3B 00 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t mulhsu(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02002033; // 33 20 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t or(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00006033; // 33 60 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t mulhu(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02003033; // 33 30 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t ori(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006013; // 13 60 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t mulw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0200003B; // 3B 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t rem(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02006033; // 33 60 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t or (uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00006033; // 33 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t remu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02007033; // 33 70 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t ori(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00006013; // 13 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t remuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200703B; // 3B 70 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t rem(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02006033; // 33 60 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t remw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200603B; // 3B 60 00 02 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t remu(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x02007033; // 33 70 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sb(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000023; // 23 00 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t remuw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0200703B; // 3B 70 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sb_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800045B; // 5B 04 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t remw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0200603B; // 3B 60 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sb_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800005B; // 5B 00 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sb(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00000023; // 23 00 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t sc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004023; // 23 40 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t sb_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800045B; // 5B 04 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t sb_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800005B; // 5B 00 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_b_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000C5B; // 5B 0C 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00004023; // 23 40 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t sc_b_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800085B; // 5B 08 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t sc_cap_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800065B; // 5B 06 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_b_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000C5B; // 5B 0C 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_cap_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80005DB; // DB 05 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_b_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800085B; // 5B 08 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_c_cap_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000E5B; // 5B 0E 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_cap_128(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800065B; // 5B 06 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_c_cap_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000DDB; // DB 0D 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_cap_64(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80005DB; // DB 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_c_ddc_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000A5B; // 5B 0A 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_c_cap_128(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000E5B; // 5B 0E 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_c_ddc_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80009DB; // DB 09 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_c_cap_64(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000DDB; // DB 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_ddc_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800025B; // 5B 02 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_c_ddc_128(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000A5B; // 5B 0A 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_ddc_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80001DB; // DB 01 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_c_ddc_64(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80009DB; // DB 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_d_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000DDB; // DB 0D 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_ddc_128(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800025B; // 5B 02 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_d_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80009DB; // DB 09 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_ddc_64(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80001DB; // DB 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_h_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000CDB; // DB 0C 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_d_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000DDB; // DB 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_h_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80008DB; // DB 08 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_d_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80009DB; // DB 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_w_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000D5B; // 5B 0D 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_h_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000CDB; // DB 0C 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sc_w_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800095B; // 5B 09 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_h_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80008DB; // DB 08 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t sc_w_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF8000D5B; // 5B 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sd_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80005DB; // DB 05 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sc_w_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800095B; // 5B 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sd_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80001DB; // DB 01 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sd(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t sfence_vma(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x12000073; // 73 00 00 12 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sd_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80005DB; // DB 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sh(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001023; // 23 10 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t sd_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80001DB; // DB 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sh_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80004DB; // DB 04 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sfence_vma(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x12000073; // 73 00 00 12 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sh_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80000DB; // DB 00 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sh(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00001023; // 23 10 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t sll(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00001033; // 33 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sh_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80004DB; // DB 04 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sllw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000103B; // 3B 10 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sh_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF80000DB; // DB 00 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t slt(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00002033; // 33 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sll(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00001033; // 33 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t slti(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002013; // 13 20 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t sllw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0000103B; // 3B 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sltiu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003013; // 13 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t slt(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00002033; // 33 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sltu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00003033; // 33 30 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t slti(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00002013; // 13 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t sra(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x40005033; // 33 50 00 40 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sltiu(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00003013; // 13 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); return i; } -uint32_t sraw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000503B; // 3B 50 00 40 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sltu(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00003033; // 33 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sret() { - uint32_t i = 0x10200073; // 73 00 20 10 - +uint32_t sra(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x40005033; // 33 50 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t srl(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00005033; // 33 50 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sraw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x4000503B; // 3B 50 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t srlw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000503B; // 3B 50 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sret() +{ + uint32_t i = 0x10200073; // 73 00 20 10 + return i; } -uint32_t sub(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x40000033; // 33 00 00 40 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t srl(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00005033; // 33 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t subw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000003B; // 3B 00 00 40 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t srlw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x0000503B; // 3B 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002023; // 23 20 00 00 - i |= ((( imm >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); - i |= ((( imm >> 5 ) & 0b1111111) << 25); +uint32_t sub(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x40000033; // 33 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sw_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800055B; // 5B 05 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t subw(uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x4000003B; // 3B 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t sw_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800015B; // 5B 01 00 F8 - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t sw(uint32_t rs1, uint32_t rs2, uint32_t imm) +{ + uint32_t i = 0x00002023; // 23 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); return i; } -uint32_t unimp() { - uint32_t i = 0xC0001073; // 73 10 00 C0 - +uint32_t sw_cap(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800055B; // 5B 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t uret() { - uint32_t i = 0x00200073; // 73 00 20 00 - +uint32_t sw_ddc(uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0xF800015B; // 5B 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); return i; } -uint32_t wfi() { - uint32_t i = 0x10500073; // 73 00 50 10 - +uint32_t unimp() +{ + uint32_t i = 0xC0001073; // 73 10 00 C0 + return i; } -uint32_t xor(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00004033; // 33 40 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( rs2 >> 0 ) & 0b11111) << 20); +uint32_t uret() +{ + uint32_t i = 0x00200073; // 73 00 20 00 + return i; } -uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00004013; // 13 40 00 00 - i |= ((( rd >> 0 ) & 0b11111) << 7); - i |= ((( rs1 >> 0 ) & 0b11111) << 15); - i |= ((( imm >> 0 ) & 0b111111111111) << 20); +uint32_t wfi() +{ + uint32_t i = 0x10500073; // 73 00 50 10 + return i; } +uint32_t xor + (uint32_t rd, uint32_t rs1, uint32_t rs2) { + uint32_t i = 0x00004033; // 33 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; + } + uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00004013; // 13 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; +} diff --git a/include/regs.h b/include/regs.h index 046652a..bbeda77 100644 --- a/include/regs.h +++ b/include/regs.h @@ -1,65 +1,64 @@ - const uint32_t zero = 0; - const uint32_t ra = 1; - const uint32_t sp = 2; - const uint32_t gp = 3; - const uint32_t tp = 4; - const uint32_t t0 = 5; - const uint32_t t1 = 6; - const uint32_t t2 = 7; - const uint32_t s0 = 8; - const uint32_t s1 = 9; - const uint32_t a0 = 10; - const uint32_t a1 = 11; - const uint32_t a2 = 12; - const uint32_t a3 = 13; - const uint32_t a4 = 14; - const uint32_t a5 = 15; - const uint32_t a6 = 16; - const uint32_t a7 = 17; - const uint32_t s2 = 18; - const uint32_t s3 = 19; - const uint32_t s4 = 20; - const uint32_t s5 = 21; - const uint32_t s6 = 22; - const uint32_t s7 = 23; - const uint32_t s8 = 24; - const uint32_t s9 = 25; - const uint32_t s10 = 26; - const uint32_t s11 = 27; - const uint32_t t3 = 28; - const uint32_t t4 = 29; - const uint32_t t5 = 30; - const uint32_t t6 = 31; - const uint32_t cnull = 0; - const uint32_t cra = 1; - const uint32_t csp = 2; - const uint32_t cgp = 3; - const uint32_t ctp = 4; - const uint32_t ct0 = 5; - const uint32_t ct1 = 6; - const uint32_t ct2 = 7; - const uint32_t cs0 = 8; - const uint32_t cs1 = 9; - const uint32_t ca0 = 10; - const uint32_t ca1 = 11; - const uint32_t ca2 = 12; - const uint32_t ca3 = 13; - const uint32_t ca4 = 14; - const uint32_t ca5 = 15; - const uint32_t ca6 = 16; - const uint32_t ca7 = 17; - const uint32_t cs2 = 18; - const uint32_t cs3 = 19; - const uint32_t cs4 = 20; - const uint32_t cs5 = 21; - const uint32_t cs6 = 22; - const uint32_t cs7 = 23; - const uint32_t cs8 = 24; - const uint32_t cs9 = 25; - const uint32_t cs10 = 26; - const uint32_t cs11 = 27; - const uint32_t ct3 = 28; - const uint32_t ct4 = 29; - const uint32_t ct5 = 30; - const uint32_t ct6 = 31; - +const uint32_t zero = 0; +const uint32_t ra = 1; +const uint32_t sp = 2; +const uint32_t gp = 3; +const uint32_t tp = 4; +const uint32_t t0 = 5; +const uint32_t t1 = 6; +const uint32_t t2 = 7; +const uint32_t s0 = 8; +const uint32_t s1 = 9; +const uint32_t a0 = 10; +const uint32_t a1 = 11; +const uint32_t a2 = 12; +const uint32_t a3 = 13; +const uint32_t a4 = 14; +const uint32_t a5 = 15; +const uint32_t a6 = 16; +const uint32_t a7 = 17; +const uint32_t s2 = 18; +const uint32_t s3 = 19; +const uint32_t s4 = 20; +const uint32_t s5 = 21; +const uint32_t s6 = 22; +const uint32_t s7 = 23; +const uint32_t s8 = 24; +const uint32_t s9 = 25; +const uint32_t s10 = 26; +const uint32_t s11 = 27; +const uint32_t t3 = 28; +const uint32_t t4 = 29; +const uint32_t t5 = 30; +const uint32_t t6 = 31; +const uint32_t cnull = 0; +const uint32_t cra = 1; +const uint32_t csp = 2; +const uint32_t cgp = 3; +const uint32_t ctp = 4; +const uint32_t ct0 = 5; +const uint32_t ct1 = 6; +const uint32_t ct2 = 7; +const uint32_t cs0 = 8; +const uint32_t cs1 = 9; +const uint32_t ca0 = 10; +const uint32_t ca1 = 11; +const uint32_t ca2 = 12; +const uint32_t ca3 = 13; +const uint32_t ca4 = 14; +const uint32_t ca5 = 15; +const uint32_t ca6 = 16; +const uint32_t ca7 = 17; +const uint32_t cs2 = 18; +const uint32_t cs3 = 19; +const uint32_t cs4 = 20; +const uint32_t cs5 = 21; +const uint32_t cs6 = 22; +const uint32_t cs7 = 23; +const uint32_t cs8 = 24; +const uint32_t cs9 = 25; +const uint32_t cs10 = 26; +const uint32_t cs11 = 27; +const uint32_t ct3 = 28; +const uint32_t ct4 = 29; +const uint32_t ct5 = 30; +const uint32_t ct6 = 31; diff --git a/mmap.c b/mmap.c index 6db33d7..f58ae32 100644 --- a/mmap.c +++ b/mmap.c @@ -1,6 +1,7 @@ #include "include/common.h" #include "include/instructions.h" #include "include/regs.h" +#include #include #include #include @@ -8,7 +9,6 @@ #include #include #include -#include uint32_t *get_executable_block() { diff --git a/sentry.c b/sentry.c index 14b6907..d72c9fb 100644 --- a/sentry.c +++ b/sentry.c @@ -6,11 +6,11 @@ * used to load additional data, that isn't available to the user. */ +#include #include #include #include #include -#include #include "include/common.h" #include "include/instructions.h" diff --git a/setjmp.c b/setjmp.c index 6e4ede6..2a3f985 100644 --- a/setjmp.c +++ b/setjmp.c @@ -1,9 +1,9 @@ #include "include/common.h" +#include #include #include #include -#include int main() { diff --git a/shared_objects/compartment_per_object.c b/shared_objects/compartment_per_object.c index 1fcb51b..1a1801e 100644 --- a/shared_objects/compartment_per_object.c +++ b/shared_objects/compartment_per_object.c @@ -8,9 +8,9 @@ #include #include +#include #include #include -#include #include "../include/instructions.h" #include "../include/regs.h" @@ -37,11 +37,11 @@ struct Car *new_car() mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); void **ptrs = mem; - + const uint32_t vtable_start_index = 2; ptrs[vtable_start_index + 0] = honk; - const ptrdiff_t vtable_offset = ((char*)(ptrs + vtable_start_index) - (char*)ptrs); + const ptrdiff_t vtable_offset = ((char *)(ptrs + vtable_start_index) - (char *)ptrs); const size_t vtable_size = 1 * 16; uint32_t idx = 0; @@ -55,7 +55,7 @@ struct Car *new_car() struct Car *public_car = (struct Car *)(((char *)mem) + sizeof(struct Car_priv) + functions_size); struct Car_priv *private_car = (struct Car_priv *)(((char *)mem) + functions_size); - + private_car->maxSpeed = 10; private_car->crash = &crash; diff --git a/shared_objects/include/compartment_per_object.h b/shared_objects/include/compartment_per_object.h index d53776c..07c4c8f 100644 --- a/shared_objects/include/compartment_per_object.h +++ b/shared_objects/include/compartment_per_object.h @@ -1,14 +1,16 @@ #pragma once -#include -#include -#include +#include +#include +#include -struct Car_priv { +struct Car_priv +{ int maxSpeed; void (*crash)(); }; -struct Car { +struct Car +{ int speed; void (*honk)(); char name[]; @@ -16,4 +18,4 @@ struct Car { void init(); -struct Car* new_car(); +struct Car *new_car(); diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index e94ec3c..36a0da7 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -1,5 +1,5 @@ -#include #include +#include #include "../../include/common.h" @@ -12,19 +12,25 @@ bool is_pointer(void *ptr) return false; } -bool scan_range(void * ptr, void * exact) +bool scan_range(void *ptr, void *exact) { bool found = false; - for(void* iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); iter = cheri_incoffset(iter, 16)) + for (void *iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); + iter = cheri_incoffset(iter, 16)) { - void* current = *(void**)iter; + void *current = *(void **)iter; if (is_pointer(current)) { - if(cheri_getsealed(current)) { - if(cheri_getaddress(current) == cheri_getaddress(exact)) { + if (cheri_getsealed(current)) + { + if (cheri_getaddress(current) == cheri_getaddress(exact)) + { found = true; printf("[Exact match] "); - }else if(cheri_gettop(current) == cheri_gettop(exact) && cheri_getbase(current) == cheri_getbase(exact)) { + } + else if (cheri_gettop(current) == cheri_gettop(exact) && + cheri_getbase(current) == cheri_getbase(exact)) + { printf("[Range match] "); } @@ -34,4 +40,3 @@ bool scan_range(void * ptr, void * exact) } return found; } - diff --git a/shared_objects/include/static_variable.h b/shared_objects/include/static_variable.h index fefa99c..896fa54 100644 --- a/shared_objects/include/static_variable.h +++ b/shared_objects/include/static_variable.h @@ -1,7 +1,7 @@ #pragma once -#include -#include -#include +#include +#include +#include void increment(); diff --git a/shared_objects/include/unexported_function.h b/shared_objects/include/unexported_function.h index 81cf22d..9fbc2e4 100644 --- a/shared_objects/include/unexported_function.h +++ b/shared_objects/include/unexported_function.h @@ -1,6 +1,4 @@ -__attribute__ ((visibility ("hidden"))) -void do_work(); - -void* test(); +__attribute__((visibility("hidden"))) void do_work(); +void *test(); diff --git a/shared_objects/main.c b/shared_objects/main.c index c14180d..74c7147 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -2,20 +2,20 @@ * Displays how lib1 and lib2 will look like from the point of view of the users of the libraries. */ +#include #include +#include #include #include #include -#include -#include -#include "include/static_variable.h" #include "include/compartment_per_object.h" -#include "include/unexported_function.h" #include "include/find_sentries.h" +#include "include/static_variable.h" +#include "include/unexported_function.h" - -void static_variables() { +void static_variables() +{ increment(); increment(); increment(); @@ -29,7 +29,8 @@ void static_variables() { printf("Count: %d\n", get_count()); } -void compartments_per_object() { +void compartments_per_object() +{ struct Car *car = new_car(); car->speed = 1; car->honk(); @@ -38,9 +39,11 @@ void compartments_per_object() { car->honk(); } -void unexported_functions() { - printf("Finding do_work using dlsym (main): "); inspect_pointer(dlsym(NULL, "do_work")); - void* function_to_search_for = test(); +void unexported_functions() +{ + printf("Finding do_work using dlsym (main): "); + inspect_pointer(dlsym(NULL, "do_work")); + void *function_to_search_for = test(); printf("test: %p\n", function_to_search_for); printf(" ------- scanning pcc for sealed pointers ------- \n"); @@ -50,16 +53,23 @@ void unexported_functions() { int main() { - printf("Choose an exapmle: \n1. Static Variables\n2. OOP Compartments\n3. Unexported Functions\n> "); + printf("Choose an exapmle: \n1. Static Variables\n2. OOP Compartments\n3. Unexported " + "Functions\n> "); uint32_t example = 0; - while(scanf("%u", &example) == 1) { - if(example == 1) { + while (scanf("%u", &example) == 1) + { + if (example == 1) + { static_variables(); - }else if(example == 2) { - compartments_per_object(); - }else if(example == 3) { - unexported_functions(); + } + else if (example == 2) + { + compartments_per_object(); + } + else if (example == 3) + { + unexported_functions(); } printf("> "); - } + } } diff --git a/shared_objects/unexported_function.c b/shared_objects/unexported_function.c index bae60a1..0411f0f 100644 --- a/shared_objects/unexported_function.c +++ b/shared_objects/unexported_function.c @@ -1,33 +1,36 @@ /** * An example showing how functions put into different shared objects are in different compartments - * based on the function pointers that can be observed in the PCC of the current function. The + * based on the function pointers that can be observed in the PCC of the current function. The * range represented by the PCC is scanned twice, once inside of the function and once outside to - * show that the two differ. + * show that the two differ. */ -#include -#include -#include -#include -#include #include "include/unexported_function.h" #include "include/find_sentries.h" +#include +#include +#include +#include +#include -__attribute__ ((visibility ("hidden"))) -void do_work() { +__attribute__((visibility("hidden"))) void do_work() +{ printf("Doing work\n"); } -void* test() { - printf("Finding do_work using dlsym (lib4):\nptr: "); inspect_pointer(dlsym(NULL, "do_work")); - printf("Obtaining a direct pointer to it: \nptr: "); inspect_pointer(&do_work); +void *test() +{ + printf("Finding do_work using dlsym (lib4):\nptr: "); + inspect_pointer(dlsym(NULL, "do_work")); + printf("Obtaining a direct pointer to it: \nptr: "); + inspect_pointer(&do_work); printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); printf(" ------- scanning pcc for sealed pointers --------\n"); bool found = scan_range(cheri_getpcc(), &do_work); - assert(found == true); + assert(found == true); do_work(); - // We return a non-tagged capability to show + // We return a non-tagged capability to show // and automatically test if there is outside access. return cheri_cleartag(&do_work); } diff --git a/timsort/include/timsort_lib.h b/timsort/include/timsort_lib.h index c30fcd2..62d4921 100644 --- a/timsort/include/timsort_lib.h +++ b/timsort/include/timsort_lib.h @@ -138,8 +138,7 @@ typedef struct bp_array_s bp_array; typedef void *bp_array; -bp_array packBP(int *pointer, const size_t baseIndex, - const size_t sizeInBytes) +bp_array packBP(int *pointer, const size_t baseIndex, const size_t sizeInBytes) { if (0 == baseIndex) { @@ -226,8 +225,7 @@ void call_and_free(void (*function)(bp_array), void *descriptor) return; } -void callBP_dispatch(void (*function)(bp_array), void *pointer, - size_t base, size_t length) +void callBP_dispatch(void (*function)(bp_array), void *pointer, size_t base, size_t length) { bp_array descriptor = packBP(pointer, base, length); From 27ef938446f9d9acc9d138fc1c5d94e3865ff5d6 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Tue, 9 Mar 2021 16:12:49 +0000 Subject: [PATCH 27/98] Example segregated freelist allocator for CHERI --- .../freelist_allocator/Makefile | 25 +++ .../freelist_allocator/README.md | 17 ++ .../freelist_allocator/binary_trees.c | 119 +++++++++++++ .../freelist_allocator/freelist_allocator.c | 166 ++++++++++++++++++ .../freelist_allocator/freelist_allocator.h | 53 ++++++ 5 files changed, 380 insertions(+) create mode 100644 example_allocators/freelist_allocator/Makefile create mode 100644 example_allocators/freelist_allocator/README.md create mode 100644 example_allocators/freelist_allocator/binary_trees.c create mode 100644 example_allocators/freelist_allocator/freelist_allocator.c create mode 100644 example_allocators/freelist_allocator/freelist_allocator.h diff --git a/example_allocators/freelist_allocator/Makefile b/example_allocators/freelist_allocator/Makefile new file mode 100644 index 0000000..0a45263 --- /dev/null +++ b/example_allocators/freelist_allocator/Makefile @@ -0,0 +1,25 @@ +CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang + +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g -O0 -I../.. -lm +SSHPORT=10006 +export + +cfiles := $(wildcard *.c) +examples := $(patsubst %.c,bin/%,$(cfiles)) + + +binary_trees: binary_trees.c freelist_allocator.c + +test: binary_trees + scp -P $(SSHPORT) binary_trees root@127.0.0.1:/root + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/binary_trees 10' + +bin/%: %.c + $(CC) $(CFLAGS) $< -o $@ + +run-%: bin/% + scp -P $(SSHPORT) bin/$( +#include +#include + +typedef struct tn +{ + struct tn *left; + struct tn *right; +} treeNode; + +treeNode *NewTreeNode(treeNode *left, treeNode *right) +{ + treeNode *new; + + new = (treeNode *)alloc(sizeof(treeNode)); + + new->left = left; + new->right = right; + + return new; +} /* NewTreeNode() */ + +long ItemCheck(treeNode *tree) +{ + if (tree->left == NULL) + return 1; + else + return 1 + ItemCheck(tree->left) + ItemCheck(tree->right); +} /* ItemCheck() */ + +treeNode *BottomUpTree(unsigned depth) +{ + if (depth > 0) + return NewTreeNode(BottomUpTree(depth - 1), BottomUpTree(depth - 1)); + else + return NewTreeNode(NULL, NULL); +} /* BottomUpTree() */ + +void DeleteTree(treeNode *tree) +{ + if (tree->left != NULL) + { + DeleteTree(tree->left); + DeleteTree(tree->right); + } + + dealloc(tree); +} /* DeleteTree() */ + +int main(int argc, char *argv[]) +{ + unsigned N, depth, minDepth, maxDepth, stretchDepth; + treeNode *stretchTree, *longLivedTree, *tempTree; + unsigned pages; /* mem required */ + + N = (argc > 1) ? atol(argv[1]) : 0; + + minDepth = 4; + + if ((minDepth + 2) > N) + maxDepth = minDepth + 2; + else + maxDepth = N; + + stretchDepth = maxDepth + 1; + + /* calculate mem requirements, with allocator-specific + * size-class assumptions + */ + pages = ((2 << (stretchDepth + 3)) * sizeof(treeNode)) / BYTES_IN_PAGE; + printf("treenode size is %u bytes\n", (unsigned int)sizeof(treeNode)); + printf("we need %u pages\n", pages); + + /* allocate memory pool */ + initialize(pages); + + /* start creating tree data structures */ + stretchTree = BottomUpTree(stretchDepth); + printf("stretch tree of depth %u\t check: %li\n", stretchDepth, ItemCheck(stretchTree)); + + DeleteTree(stretchTree); + + longLivedTree = BottomUpTree(maxDepth); + + for (depth = minDepth; depth <= maxDepth; depth += 2) + { + long i, iterations, check; + + iterations = pow(2, maxDepth - depth + minDepth); + + check = 0; + + for (i = 1; i <= iterations; i++) + { + tempTree = BottomUpTree(depth); + check += ItemCheck(tempTree); + DeleteTree(tempTree); + } /* for(i = 1...) */ + + printf("%li\t trees of depth %u\t check: %li\n", iterations, depth, check); + } /* for(depth = minDepth...) */ + + printf("long lived tree of depth %u\t check: %li\n", maxDepth, ItemCheck(longLivedTree)); + + return 0; +} /* main() */ diff --git a/example_allocators/freelist_allocator/freelist_allocator.c b/example_allocators/freelist_allocator/freelist_allocator.c new file mode 100644 index 0000000..5c242ff --- /dev/null +++ b/example_allocators/freelist_allocator/freelist_allocator.c @@ -0,0 +1,166 @@ +#include "freelist_allocator.h" +#include +#include +#include +#include +#include + +char *small_freelist = NULL; + +char *medium_freelist = NULL; + +char *large_freelist = NULL; + +void initialize(unsigned int size_in_pages) +{ + /* request memory for our allocation buffer + * NB mmap min bounds for capability is 1 page (4K) + */ + size_t bytes_to_allocate = size_in_pages * BYTES_IN_PAGE; + char *res = + mmap(NULL, bytes_to_allocate, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (res == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + // put in linked list pointers and + // stick into the large freelist + // give this space to the large freelist ... + large_freelist = insert_linked_list_pointers(LARGE, bytes_to_allocate, res, large_freelist); + return; +} + +char *insert_linked_list_pointers(size_t cell_size, size_t limit, char *start, char *freelist) +{ + char *curr = start; + char *next = curr + cell_size; + char *max = start + limit; + // ensure next ptr will fit into cell + assert(sizeof(void *) <= cell_size); + + while (next < max) + { + ((char **)curr)[0] = next; + curr = next; + next = curr + cell_size; + } + // at the end, concatenate this newly formed + // list with existing freelist + ((char **)curr)[0] = freelist; + + return start; +} + +char *alloc(size_t bytes) +{ + + size_t size; + char *freelist_to_use = NULL; + char *ret = NULL; // ptr to return + + // work out which freelist to use + if (bytes <= SMALL) + { + size = SMALL; + freelist_to_use = small_freelist; + } + else if (bytes <= MEDIUM) + { + size = MEDIUM; + freelist_to_use = medium_freelist; + } + else + { + size = LARGE; + freelist_to_use = large_freelist; + } + + if (freelist_to_use == NULL) + { + // fixup freelist (if no available mem there) + char *new_space = NULL; + switch (size) + { + case SMALL: + new_space = alloc(MEDIUM); + if (new_space != NULL) + { + small_freelist = + insert_linked_list_pointers(SMALL, MEDIUM, new_space, small_freelist); + freelist_to_use = small_freelist; + // now we have replenished space... + } + break; + case MEDIUM: + new_space = alloc(LARGE); + if (new_space != NULL) + { + medium_freelist = + insert_linked_list_pointers(MEDIUM, LARGE, new_space, medium_freelist); + freelist_to_use = medium_freelist; + } + break; + default: + // stuck! no more mem! + // we will return NULL + break; + } + } + + // pop from head of freelist (if there's anything there) + if (freelist_to_use != NULL) + { + char *head = freelist_to_use; + char *tail = ((char **)head)[0]; + switch (size) + { + case SMALL: + small_freelist = tail; + break; + case MEDIUM: + medium_freelist = tail; + break; + default: + large_freelist = tail; + break; + } + ret = head; + SET_SIZE(ret, size); + } + return ret; +} + +void dealloc(void *buffer) +{ + + // work out the size of the buffer + size_t size; + char *freelist; + + size = GET_SIZE(buffer); + + // then prepend it to the appropriate freelist + switch (size) + { + case SMALL: + small_freelist = cons_onto_freelist(buffer, small_freelist); + break; + case MEDIUM: + medium_freelist = cons_onto_freelist(buffer, medium_freelist); + break; + default: + large_freelist = cons_onto_freelist(buffer, large_freelist); + break; + } + + return; +} + +char *cons_onto_freelist(char *cell, char *freelist) +{ + ((char **)cell)[0] = freelist; + return cell; +} diff --git a/example_allocators/freelist_allocator/freelist_allocator.h b/example_allocators/freelist_allocator/freelist_allocator.h new file mode 100644 index 0000000..b8a99f1 --- /dev/null +++ b/example_allocators/freelist_allocator/freelist_allocator.h @@ -0,0 +1,53 @@ +/********************************** + * freelist_allocator.h + * Jeremy.Singer@glasgow.ac.uk + * + * This is a simple segregated freelist allocator. + * It mmaps a large buffer of num_pages pages, + * then constructs a linked list of LARGE-sized cells. + * + * When an alloc request occurs, we have three size + * classes we can use - SMALL, MEDIUM, and LARGE. + * If there is a empty cell available in the appropriate + * freelist, we return this cell. + * If there are no cells available, we try to + * grab a cell from a larger freelist to replenish + * our freelist, and return one of these cells. + * If there is no memory available, alloc returns NULL. + * + * When a dealloc request occurs, we know the size + * of the cell so we can prepend the cell onto the + * appropriate freelist. + * + * NB Allocated cells have their sizes encoded in the + * corresponding cell capability - this means we + * naively assume that allocator client code + * does _not_ interfere with the capability + * metadata. + */ + +#include +#include + +/* possible sizes for cells */ +#define SMALL 16 +#define MEDIUM 256 +#define LARGE 4096 + +/* we assume 4K pages */ +#define BYTES_IN_PAGE LARGE + +/* cell sizes encoded in CHERI bounds metadata */ +#define SET_SIZE(cell, size) cell = cheri_bounds_set_exact(cell, size) +#define GET_SIZE(cell) cheri_length_get(cell) + +/* allocator init routine */ +void initialize(unsigned int num_pages); + +/* malloc and free */ +char *alloc(size_t bytes); +void dealloc(void *buffer); + +/* freelist management routines */ +char *insert_linked_list_pointers(size_t cell_size, size_t limit, char *start, char *freelist); +char *cons_onto_freelist(char *cell, char *freelist); From a85d9b1cb92e61e4d2468d8ea3a2ecad8f956e82 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Thu, 7 Jan 2021 16:18:47 +0000 Subject: [PATCH 28/98] Simple bitmap memory allocator for systems supporting Capabilities. Memory chunks of a fixed size are allocated in a first-come-first-serve manner. Capabilities are used to constrain pointer dereferencing by callers of the allocators. --- CMakeLists.txt | 4 +- example_allocators/bitmap_allocator/Makefile | 28 +++ example_allocators/bitmap_allocator/README.md | 9 + .../bitmap_allocator/bitmap_alloc.c | 204 ++++++++++++++++++ .../bitmap_allocator/bitmap_alloc.h | 18 ++ .../bitmap_allocator/good_client.c | 72 +++++++ 6 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 example_allocators/bitmap_allocator/Makefile create mode 100644 example_allocators/bitmap_allocator/README.md create mode 100644 example_allocators/bitmap_allocator/bitmap_alloc.c create mode 100644 example_allocators/bitmap_allocator/bitmap_alloc.h create mode 100644 example_allocators/bitmap_allocator/good_client.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 747f41d..b0f60fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,4 +20,6 @@ add_executable(sentry sentry.c) add_executable(set_bounds set_bounds.c) add_executable(setjmp setjmp.c) add_executable(stackscan stackscan.c) -add_executable(xor_pointers xor_pointers.c) +add_executable(xor_pointers xor_pointers.c) +add_executable(bitmap_alloc_good example_allocators/bitmap_allocator/good_client.c + example_allocators/bitmap_allocator/bitmap_alloc.c) diff --git a/example_allocators/bitmap_allocator/Makefile b/example_allocators/bitmap_allocator/Makefile new file mode 100644 index 0000000..f57bed5 --- /dev/null +++ b/example_allocators/bitmap_allocator/Makefile @@ -0,0 +1,28 @@ +CC=$(HOME)/cheri/output/sdk/bin/riscv64-unknown-freebsd13-clang + +CFLAGS=-march=rv64imafdcxcheri -mabi=l64pc128d --sysroot=$(HOME)/cheri/output/rootfs-riscv64-hybrid -mno-relax -g -O0 -I../.. +SSHPORT=10003 +export + +cfiles := $(wildcard *.c) +examples := $(patsubst %.c,bin/%,$(cfiles)) + + +good_client: good_client.c bitmap_alloc.c + +bad_client: bad_client.c bitmap_alloc.c + +test: + scp -P $(SSHPORT) good_client bad_client root@127.0.0.1:/root + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/good_client' + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/bad_client' + +bin/%: %.c + $(CC) $(CFLAGS) $< -o $@ + +run-%: bin/% + scp -P $(SSHPORT) bin/$( +#include + +#include +#include +#include +#include +#include +#include + +#include "bitmap_alloc.h" + +#define BITS_PER_BYTE 8 + +char *buffer = NULL; /* allocation buffer */ +unsigned char *bitmap = NULL; /* bitmap for the buffer */ + +int buffer_size = 0; /* size of buffer (in bytes) */ +int bitmap_size = 0; /* size of bitmap (in bytes) */ +int bytes_per_chunk = 0; /* size of single chunk (in bytes) */ + +void init_alloc(int num_chunks, int chunk_size) +{ + int i = 0; + + /* we need to increase the num_chunks + * so every bit in bitmap will be used + */ + int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0) + ? num_chunks + : (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE))); + + /* we need to increase the chunk_size + * so chunks will be CHERI aligned + * (i.e. 16 bytes for RISC-V 64-bit arch) + */ + int adjusted_chunk_size = + (chunk_size % (sizeof(void *)) == 0) + ? chunk_size + : (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *)))); + + /* check this chunk size is small enough so we can represent + * bounds precisely with CHERI compressed representation + */ + adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size); + + /* request memory for our allocation buffer */ + char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + /* request memory for our bitmap */ + bitmap = (unsigned char *)mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (res == MAP_FAILED || bitmap == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + /* NB mmap min bounds for capability is 1 page (4K) */ + buffer = res; + /* check buffer is aligned */ + assert((uintptr_t)buffer % sizeof(void *) == 0); + /* check bitmap is aligned */ + assert((uintptr_t)bitmap % sizeof(void *) == 0); + + bytes_per_chunk = adjusted_chunk_size; + buffer_size = adjusted_num_chunks * adjusted_chunk_size; + bitmap_size = adjusted_num_chunks / BITS_PER_BYTE; + + /* zero bitmap, since all chunks are free initially */ + for (i = 0; i < bitmap_size; i++) + { + bitmap[i] = 0; + } + + // set exact bounds for buffer and bitmap? + buffer = cheri_setbounds(buffer, buffer_size); + bitmap = cheri_setbounds(bitmap, bitmap_size); + return; +} + +/* + * allocate fixed size chunk with bitmap allocator + * this is our simplistic `malloc` function + */ +char *alloc_chunk() +{ + unsigned char updated_byte = 0; + int chunk_index = 0; + char *chunk = NULL; + // iterate over all bits in bitmap, looking for a 0 + // when we find a 0, set it to 1 and + // return the corresponding chunk + // (setting its capability bounds) + int i = 0; + while (bitmap[i] == (unsigned char)0xff) + { + i++; + if (i >= bitmap_size) + break; + } + // do we have a 0? + if (i < bitmap_size && bitmap[i] != (unsigned char)0xff) + { + // find the lowest 0 ... + int j = 0; + // right shift until bottom bit is 0 + for (j = 0; j < BITS_PER_BYTE; j++) + { + int bit = (bitmap[i] >> j) & 1; + if (bit == 0) + { + break; + } + } + // now i is the word index, j is the bit index + // set this bit to 1 ... + // and work out the chunk to allocate + updated_byte = bitmap[i] + (unsigned char)(1 << j); + bitmap[i] = updated_byte; + + chunk_index = i * BITS_PER_BYTE + j; + chunk = buffer + (chunk_index * bytes_per_chunk); + + /* restrict capability range before returning ptr */ + chunk = cheri_setbounds(chunk, bytes_per_chunk); + } + + return chunk; +} + +void free_chunk(void *chunk) +{ + vaddr_t base = cheri_getbase(chunk); + vaddr_t buff_base = cheri_getbase(buffer); + /* calculate chunk index in buffer */ + int chunk_index = (base - buff_base) / bytes_per_chunk; + assert(chunk_index >= 0); + /* calculate corresponding bitmap index */ + int bitmap_index = chunk_index / BITS_PER_BYTE; + assert(bitmap_index < bitmap_size); + int bitmap_offset = chunk_index % BITS_PER_BYTE; + /* set this bitmap entry to 0 */ + unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char)(~(1 << bitmap_offset)); + bitmap[bitmap_index] = updated_byte; + return; +} + +int num_used_chunks() +{ + int i = 0; + int used_chunks = 0; + + while (i < bitmap_size) + { + unsigned char x = bitmap[i]; + if (x != 0) + { + /* some used chunks here */ + unsigned char j; + for (j = 1; j <= x; j = j << 1) + { + if (x & j) + { + used_chunks++; + } + } + } + i++; + } + return used_chunks; +} diff --git a/example_allocators/bitmap_allocator/bitmap_alloc.h b/example_allocators/bitmap_allocator/bitmap_alloc.h new file mode 100644 index 0000000..642a185 --- /dev/null +++ b/example_allocators/bitmap_allocator/bitmap_alloc.h @@ -0,0 +1,18 @@ +/********************************** + * bitmap_alloc.h + * Jeremy.Singer@glasgow.ac.uk + * + * This is a simple bitmap allocator, + * to allocate fixed-size chunks of data. + * + * Initial simple memory allocator test. + * Explore capability narrowing operations + * and intrinsics for bound reporting. + */ + +void init_alloc(int num_chunks, int chunk_size); + +char *alloc_chunk(); /* simplest malloc */ +void free_chunk(void *chunk); /* simplest free */ + +int num_used_chunks(); /* count of used memory */ diff --git a/example_allocators/bitmap_allocator/good_client.c b/example_allocators/bitmap_allocator/good_client.c new file mode 100644 index 0000000..1c9234d --- /dev/null +++ b/example_allocators/bitmap_allocator/good_client.c @@ -0,0 +1,72 @@ +/* + * good_client.c + * Jeremy Singer + * + * Example use of bitmap allocator, showing how + * 'good' client code should interact with the + * allocator. + */ + +#include +#include +#include +#include + +#include "bitmap_alloc.h" + +/* should program generate debugging output? 1 for Yes, 0 for No */ +#define DEBUG_PRINTF 1 + +/* size of buffer to allocate (in list cells) */ +#define NUM_CELLS 64 + +/* trivial linked list data structure */ +typedef struct _list +{ + int payload; + struct _list *next; + ; +} list; + +int main() +{ + unsigned int i = 0; + unsigned int n = 1; + list *l = NULL; + + init_alloc(NUM_CELLS, sizeof(list)); + while (n <= NUM_CELLS) + { + /* now try to do some allocations */ + for (i = 0; i < n; i++) + { + list *tmp = (list *)alloc_chunk(); + if (tmp) + { + tmp->payload = i; + tmp->next = l; + printf("alloc cell %d\n", i); + l = tmp; + } + else + { + fprintf(stderr, "chunk allocation failed but there should be enough memory!"); + exit(-1); + } + } + + // now free this list + while (l != NULL) + { + list *tmp = l->next; + printf("free cell %d\n", l->payload); + free_chunk(l); + l = tmp; + } + + /* double the limit, n */ + n <<= 1; + } + + return 0; +} From 4d1b0830a061be823a3a632ac77df400ed0aa930 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Sun, 20 Jun 2021 18:55:59 +0100 Subject: [PATCH 29/98] A few operations for keeping the repository in order - Renamed files to give them a meaningful name - Deleted redundant code - Added some comments --- CMakeLists.txt | 2 +- allocate.c | 4 ++++ hello-cxx.cpp => bounds-cxx.cpp | 0 hello.c => bounds.c | 12 ++++++++---- check_mask.c | 2 +- function.c | 5 +++++ set_bounds.c | 17 ++++++----------- 7 files changed, 25 insertions(+), 17 deletions(-) rename hello-cxx.cpp => bounds-cxx.cpp (100%) rename hello.c => bounds.c (65%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0f60fd..506d363 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ add_executable(allocate allocate.c) add_executable(check_length check_length.c) add_executable(check_mask check_mask.c) add_executable(function function.c) -add_executable(hello hello.c) +add_executable(bounds bounds.c) add_executable(mmap mmap.c) if( ${COMP_NAME} MATCHES "^mips*" ) add_executable(seal seal.c) diff --git a/allocate.c b/allocate.c index ed72332..9c3fd4b 100644 --- a/allocate.c +++ b/allocate.c @@ -2,6 +2,10 @@ #include #include +/*** + * Inspect capabilities metadata after a malloc. + ***/ + int main() { size_t size = 31; diff --git a/hello-cxx.cpp b/bounds-cxx.cpp similarity index 100% rename from hello-cxx.cpp rename to bounds-cxx.cpp diff --git a/hello.c b/bounds.c similarity index 65% rename from hello.c rename to bounds.c index 9424fad..e601627 100644 --- a/hello.c +++ b/bounds.c @@ -3,16 +3,20 @@ #include #include +/*** + * This simple program shows how CHERI limits the + * range of addresses that may be dereferenced. + * Reading the value outside the range will give + * an "In-address space security exception". + ***/ + int main() { int32_t array[16] = {0}; - - int64_t type = 4; int32_t *typed_array = &array; - int32_t *aptr = &array; uint64_t length = cheri_getlength(typed_array); - for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 16; counter++) + for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 15; counter++) { inspect_pointer(typed_array + counter); // Read value to crash diff --git a/check_mask.c b/check_mask.c index a0897e5..09884ce 100644 --- a/check_mask.c +++ b/check_mask.c @@ -1,5 +1,5 @@ /*** - * A example to see what the representable alignment mask is and + * An example to see what the representable alignment mask is and * how it changes the alignment based on how high in memory it is. */ diff --git a/function.c b/function.c index c6cd60e..852f9b2 100644 --- a/function.c +++ b/function.c @@ -3,6 +3,11 @@ #include #include +/*** + * Inspect capabilities metadata of a simple function's address. + * The simple function is gcd (Greatest Common Divisor). + ***/ + int gcd(int a, int b) { if (0 == a) diff --git a/set_bounds.c b/set_bounds.c index 9e412f1..7c1214d 100644 --- a/set_bounds.c +++ b/set_bounds.c @@ -6,25 +6,20 @@ /*** * The point of this program is to demonstrate the failing of a program * that tries to extend the bounderies of a capable pointer. + * Compared to the simpler 'bounds.c', here we try to modify the bounds + * explicitly. ***/ + int main() { int32_t array[16] = {0}; - uint32_t bounds = 64; - puts("bounds(64): "); + + puts("Bounds [Choose a value greater than 64]:"); if (0 == scanf("%u", &bounds)) { error("Extraneous input"); } - + // Trying to extend the bounds generates an exception. int32_t *custom_boundes_array = cheri_setbounds(array, bounds); - - uint64_t length = cheri_getlength(custom_boundes_array); - for (uint32_t counter = 0; counter < length / sizeof(int32_t); counter++) - { - inspect_pointer(custom_boundes_array + counter); - // Read value to crash - printf("Count: %d, Value: %d\n", counter, *(custom_boundes_array + counter)); - } } From 8dfa66b98f7b8ca180462b3e001aa739e5ce2bea Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 21 Jun 2021 08:55:50 +0100 Subject: [PATCH 30/98] Added a clang-format target to the Makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 0c4adc1..ba8a61c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ CC=$(HOME)/cheri/output/sdk/bin/clang +CFORMAT=$(HOME)/cheri/output/sdk/bin/clang-format CXX=$(HOME)/cheri/output/sdk/bin/clang++ CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 @@ -23,5 +24,8 @@ run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) scp -P $(SSHPORT) $(word 2,$^) bin/$( Date: Wed, 23 Jun 2021 18:00:35 +0100 Subject: [PATCH 31/98] Add two separate Makefile for riscv64 and Morello --- .buildbot.sh | 2 +- Makefile.morello | 31 +++++++++++++++++++++++++++++++ Makefile => Makefile.riscv64 | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 Makefile.morello rename Makefile => Makefile.riscv64 (98%) diff --git a/.buildbot.sh b/.buildbot.sh index 5b6fba6..13b3e4b 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -9,7 +9,7 @@ for example in *.c; do if [ "${example}" == "seal.c" ]; then continue fi - make bin/"${example%%.*}" + make -f Makefile.riscv64 bin/"${example%%.*}" done # arg-1 : Source directory diff --git a/Makefile.morello b/Makefile.morello new file mode 100644 index 0000000..9374df5 --- /dev/null +++ b/Makefile.morello @@ -0,0 +1,31 @@ +CC=$(HOME)/cheri/output/morello-sdk/bin/clang +CFORMAT=$(HOME)/cheri/output/morello-sdk/bin/clang-format +CXX=$(HOME)/cheri/output/morello-sdk/bin/clang++ +CFLAGS=-fuse-ld=lld --config cheribsd-morello-purecap.cfg +SSHPORT=10085 +export + +cfiles := $(wildcard *.c) +cppfiles := $(wildcard *.cpp) +examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) + +.PHONY: all run clean + +all: $(examples) + +bin/%: %.c + $(CC) $(CFLAGS) $< -o $@ + +bin/%: %.cpp + $(CXX) $(CFLAGS) $< -o $@ + +.SECONDEXPANSION: +run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) + scp -P $(SSHPORT) $(word 2,$^) bin/$( Date: Fri, 2 Jul 2021 22:01:26 +0100 Subject: [PATCH 32/98] Add general example regarding bounds This example wants to clarify the difference between setting the bounds of a capable pointer explicitly and dereferencing a pointer outside the bounds on two architectures: "riscv64" and "Morello" (ARMv8). CHERI limits the range of addresses that may be dereferenced and, in this example, reading the value outside the range will give an "In-address space security exception" on "Morello". However, on "riscv64", we can make the program crash by explicitly setting the bounds outside the allowed range. --- CMakeLists.txt | 1 + Makefile.morello | 4 ++-- Makefile.riscv64 | 2 +- bounds.c | 10 +++++----- function.c | 10 +++++----- general_bounds.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ include/common.h | 3 +-- seal.c | 15 +++++++------- sentry.c | 10 ++++------ set_bounds.c | 12 ++++++------ setjmp.c | 1 - stackscan.c | 1 - 12 files changed, 83 insertions(+), 37 deletions(-) create mode 100644 general_bounds.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 506d363..96834cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ add_executable(check_length check_length.c) add_executable(check_mask check_mask.c) add_executable(function function.c) add_executable(bounds bounds.c) +add_executable(general_bounds general_bounds.c) add_executable(mmap mmap.c) if( ${COMP_NAME} MATCHES "^mips*" ) add_executable(seal seal.c) diff --git a/Makefile.morello b/Makefile.morello index 9374df5..404d895 100644 --- a/Makefile.morello +++ b/Makefile.morello @@ -2,7 +2,7 @@ CC=$(HOME)/cheri/output/morello-sdk/bin/clang CFORMAT=$(HOME)/cheri/output/morello-sdk/bin/clang-format CXX=$(HOME)/cheri/output/morello-sdk/bin/clang++ CFLAGS=-fuse-ld=lld --config cheribsd-morello-purecap.cfg -SSHPORT=10085 +SSHPORT?=10085 export cfiles := $(wildcard *.c) @@ -25,7 +25,7 @@ run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/$( -#include -#include - /*** * This simple program shows how CHERI limits the * range of addresses that may be dereferenced. @@ -10,6 +5,11 @@ * an "In-address space security exception". ***/ +#include "include/common.h" +#include +#include +#include + int main() { int32_t array[16] = {0}; diff --git a/function.c b/function.c index 852f9b2..b468388 100644 --- a/function.c +++ b/function.c @@ -1,13 +1,13 @@ -#include "include/common.h" -#include -#include -#include - /*** * Inspect capabilities metadata of a simple function's address. * The simple function is gcd (Greatest Common Divisor). ***/ +#include "include/common.h" +#include +#include +#include + int gcd(int a, int b) { if (0 == a) diff --git a/general_bounds.c b/general_bounds.c new file mode 100644 index 0000000..5ea8f3d --- /dev/null +++ b/general_bounds.c @@ -0,0 +1,51 @@ +/*** + * This program is a fusion of ``bounds.c`` + * and ``set_bounds.c``. + * CHERI limits the range of addresses that may be dereferenced + * and reading the value outside the range will always give + * an "In-address space security exception". However, if we try to + * extend the bounderies of a capable pointer by setting them explicitly, + * the program fails on "riscv64". + ***/ + +#include "include/common.h" +#include +#include +#include +#include + +int main() +{ + int32_t array[12] = {0}; + int32_t *typed_array = &array; + u_int32_t bounds = 48; + +#if defined(__aarch64__) && __ARM_ARCH == 8 + // Do a dereference + uint64_t length = cheri_getlength(typed_array); + for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 11; counter++) + { + inspect_pointer(typed_array + counter); + // Read value to crash + if (counter == 12) + { + printf("--> On Morello (ARMv8) dereferencing outside the range" + " causes the following exception:\n"); + fflush(stdout); + } + printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); + } +#elif defined(__riscv) + // Simply increase the bounds + printf("Bounds [Choose a value greater than 48]:\n"); + if (0 == scanf("%u", &bounds)) + { + error("Extraneous input"); + } + printf("Explicitly setting the bounds outside the range causes the following exception: "); + fflush(stdout); + int32_t custom_bounds_array = cheri_setbounds(array, bounds); +#else +#error Platform not currently supported. +#endif +} \ No newline at end of file diff --git a/include/common.h b/include/common.h index 47f85da..2819032 100644 --- a/include/common.h +++ b/include/common.h @@ -1,10 +1,9 @@ #include #include +#include #include #include -#include - void inspect_pointer(void *ptr) { uint64_t length = cheri_getlength(ptr); diff --git a/seal.c b/seal.c index 1c07f0c..6cfce1c 100644 --- a/seal.c +++ b/seal.c @@ -1,18 +1,17 @@ +/*** + * This is mostly an experiment and it doens't work + * The CHERI_GET_SEALCAP syscall is mips only + ***/ + #include "include/common.h" +#include +#include #include #include #include #include #include -#include -#include - -/*** - * This is mostly an experiment and it doens't work - * The CHERI_GET_SEALCAP syscall is mips only - ***/ - typedef struct cap { uint32_t perms : 16; diff --git a/sentry.c b/sentry.c index d72c9fb..b0bb75b 100644 --- a/sentry.c +++ b/sentry.c @@ -6,18 +6,16 @@ * used to load additional data, that isn't available to the user. */ +#include "include/common.h" +#include "include/instructions.h" +#include "include/regs.h" +#include #include #include #include #include #include -#include "include/common.h" -#include "include/instructions.h" -#include "include/regs.h" - -#include - struct data { int a; diff --git a/set_bounds.c b/set_bounds.c index 7c1214d..45ac489 100644 --- a/set_bounds.c +++ b/set_bounds.c @@ -1,8 +1,3 @@ -#include "include/common.h" -#include -#include -#include - /*** * The point of this program is to demonstrate the failing of a program * that tries to extend the bounderies of a capable pointer. @@ -10,6 +5,11 @@ * explicitly. ***/ +#include "include/common.h" +#include +#include +#include + int main() { int32_t array[16] = {0}; @@ -21,5 +21,5 @@ int main() error("Extraneous input"); } // Trying to extend the bounds generates an exception. - int32_t *custom_boundes_array = cheri_setbounds(array, bounds); + int32_t *custom_bounds_array = cheri_setbounds(array, bounds); } diff --git a/setjmp.c b/setjmp.c index 2a3f985..5a81713 100644 --- a/setjmp.c +++ b/setjmp.c @@ -1,5 +1,4 @@ #include "include/common.h" - #include #include #include diff --git a/stackscan.c b/stackscan.c index e6666ff..f0ab4d4 100644 --- a/stackscan.c +++ b/stackscan.c @@ -1,5 +1,4 @@ #include "include/common.h" - #include #include #include From 032da36af991045e8659f921642a4ff3cb9fc678 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Tue, 6 Jul 2021 16:23:46 +0100 Subject: [PATCH 33/98] Simple Shell Script test for "bounds.c" After some trial/error, simple SH scripts seem the only feasible way to write the tests. This is the first test and the one that led to the final decision of writing tests as shell scripts. --- tests/test_bounds.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100755 tests/test_bounds.sh diff --git a/tests/test_bounds.sh b/tests/test_bounds.sh new file mode 100755 index 0000000..386b722 --- /dev/null +++ b/tests/test_bounds.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# This is a simple way to test "bounds.c" by checking ``stderr``. +set -e + +: "${SSHPORT:=10021}" +if [ -f "../bin/bounds" ]; then + scp -P $SSHPORT ../bin/bounds root@127.0.0.1:/root + exit_status=0 + RESULT={{$(ssh -p $SSHPORT -t root@127.0.0.1 'sh -c "/root/bounds 2>&1 > /dev/null"')} && exit_status=1} || true + echo -n "bounds... " + if [ $exit_status != 0 ]; then + echo "FAILED! See below for more details." + echo $RESULT + else + echo "ok" + fi +else + echo "Please, build first bounds.c before running the tests." + exit 1 +fi From 03b625e100c50c51dc639066d54820db46e36d85 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Fri, 9 Jul 2021 11:21:45 +0100 Subject: [PATCH 34/98] Renamed "test_bounds.sh" to a more general "run_tests.sh" All the examples rising an "In-address space security exception" can be tested together avoiding code duplication. --- tests/test_bounds.sh | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100755 tests/test_bounds.sh diff --git a/tests/test_bounds.sh b/tests/test_bounds.sh deleted file mode 100755 index 386b722..0000000 --- a/tests/test_bounds.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -# This is a simple way to test "bounds.c" by checking ``stderr``. -set -e - -: "${SSHPORT:=10021}" -if [ -f "../bin/bounds" ]; then - scp -P $SSHPORT ../bin/bounds root@127.0.0.1:/root - exit_status=0 - RESULT={{$(ssh -p $SSHPORT -t root@127.0.0.1 'sh -c "/root/bounds 2>&1 > /dev/null"')} && exit_status=1} || true - echo -n "bounds... " - if [ $exit_status != 0 ]; then - echo "FAILED! See below for more details." - echo $RESULT - else - echo "ok" - fi -else - echo "Please, build first bounds.c before running the tests." - exit 1 -fi From 3f81553f17fb38a6044d81eca9c7b791ac39a31d Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Fri, 9 Jul 2021 11:17:42 +0100 Subject: [PATCH 35/98] Add tests for the examples that rise an "In-address space security exception" Tests need to be done on a running `qemu-system`. "run_cheri_examples.py" starts a `qemu` VM, runs the tests (by calling "run_tests.sh") and safely shutdown the VM. Note: at the moment, only `riscv64` is supported. `Morello` will be added soon. --- .buildbot.sh | 30 +++++++++++++++++++++++++++- bounds.c | 2 +- general_bounds.c | 25 +++++++++++++++++++----- include/common.h | 21 ++++++++++---------- include/instructions.h | 15 +++++++------- set_bounds.c | 23 ++++++++++++++++++---- shared_objects/main.c | 3 +++ tests/run_cheri_examples.py | 23 ++++++++++++++++++++++ tests/run_tests.sh | 39 +++++++++++++++++++++++++++++++++++++ 9 files changed, 152 insertions(+), 29 deletions(-) create mode 100755 tests/run_cheri_examples.py create mode 100755 tests/run_tests.sh diff --git a/.buildbot.sh b/.buildbot.sh index 13b3e4b..e7afaf0 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -12,6 +12,32 @@ for example in *.c; do make -f Makefile.riscv64 bin/"${example%%.*}" done +# Copy the test-script to `cheribuild` +cp ./tests/run_cheri_examples.py $HOME/build/test-scripts/. + +# Run the tests on the `qemu-system riscv64` +args=( + # Architecture + --architecture riscv64 + # Qemu System to use + --qemu-cmd $HOME/cheri/output/sdk/bin/qemu-system-riscv64cheri + # Kernel (to avoid the default one) + --kernel $HOME/cheri/output/rootfs-riscv64-purecap/boot/kernel/kernel + # Bios (to avoid the default one) + --bios bbl-riscv64cheri-virt-fw_jump.bin + # Disk Image + --disk-image $HOME/cheri/output/cheribsd-riscv64-purecap.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port 10021 + --ssh-key $HOME/.ssh/id_ed25519.pub + ) + +python3 $HOME/build/test-scripts/run_cheri_examples.py "${args[@]}" +rm -rfv bin/* + +BUILD_DIR="bin" + # arg-1 : Source directory # arg-2 : cmake toolchain file function build_cheri_examples() @@ -25,5 +51,7 @@ function build_cheri_examples() } build_cheri_examples $(pwd) riscv64-purecap.cmake -build_cheri_examples $(pwd) morello-purecap.cmake + +# FIXME: EXCLUDING MORELLO to close PR#26 +# build_cheri_examples $(pwd) morello-purecap.cmake rm -rfv $(pwd)/build diff --git a/bounds.c b/bounds.c index 982d043..c84a4f2 100644 --- a/bounds.c +++ b/bounds.c @@ -15,7 +15,7 @@ int main() int32_t array[16] = {0}; int32_t *typed_array = &array; - uint64_t length = cheri_getlength(typed_array); + uint64_t length = cheri_length_get(typed_array); for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 15; counter++) { inspect_pointer(typed_array + counter); diff --git a/general_bounds.c b/general_bounds.c index 5ea8f3d..fc49141 100644 --- a/general_bounds.c +++ b/general_bounds.c @@ -14,7 +14,7 @@ #include #include -int main() +int main(int argc, char *argv[]) { int32_t array[12] = {0}; int32_t *typed_array = &array; @@ -36,12 +36,27 @@ int main() printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); } #elif defined(__riscv) - // Simply increase the bounds - printf("Bounds [Choose a value greater than 48]:\n"); - if (0 == scanf("%u", &bounds)) + if (argc < 2) { - error("Extraneous input"); + // Simply increase the bounds + printf("Bounds [Choose a value greater than 48]:\n"); + if (0 == scanf("%u", &bounds)) + { + error("Extraneous input"); + } + } + // Command line argument to simplify testing + else if (atoi(argv[1]) > 48) + { + bounds = atoi(argv[1]); } + else + { + printf("Please choose a value greater than 48."); + // This will cause the test to fail if a value lower than 64 has been chosen + exit(0); + } + printf("Explicitly setting the bounds outside the range causes the following exception: "); fflush(stdout); int32_t custom_bounds_array = cheri_setbounds(array, bounds); diff --git a/include/common.h b/include/common.h index 2819032..a2637c4 100644 --- a/include/common.h +++ b/include/common.h @@ -1,20 +1,19 @@ -#include -#include +#include #include #include #include void inspect_pointer(void *ptr) { - uint64_t length = cheri_getlength(ptr); - uint64_t address = cheri_getaddress(ptr); - uint64_t base = cheri_getbase(ptr); - uint64_t flags = cheri_getflags(ptr); - uint64_t perms = cheri_getperm(ptr); - uint64_t type = cheri_gettype(ptr); - bool tag = cheri_gettag(ptr); + uint64_t length = cheri_length_get(ptr); + uint64_t address = cheri_address_get(ptr); + uint64_t base = cheri_base_get(ptr); + uint64_t flags = cheri_flags_get(ptr); + uint64_t perms = cheri_perms_get(ptr); + uint64_t type = cheri_type_get(ptr); + bool tag = cheri_tag_get(ptr); - uint64_t offset = cheri_getoffset(ptr); + uint64_t offset = cheri_offset_get(ptr); printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, " "Perms: %04lx, Type: %04lx, Tag: %d\n", address, base, base + length, flags, length, offset, perms, type, tag); @@ -29,7 +28,7 @@ void error(char *string) // This function returns the current stack pointer. // // We don't want to inline it because __builtin_frame_address will change it's meaning. -// __builtin_frame_address gives you the stack bottom of the current function. +// __builtin_frame_address gives you the stack bottom of the current function.` // This will be the stack top of the calling function. __attribute__((noinline)) void *cheri_getcsp() { diff --git a/include/instructions.h b/include/instructions.h index 9f2ba1c..7b19947 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -1632,13 +1632,14 @@ uint32_t wfi() } uint32_t xor - (uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00004033; // 33 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; - } + (uint32_t rd, uint32_t rs1, uint32_t rs2) +{ + uint32_t i = 0x00004033; // 33 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; +} uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) { diff --git a/set_bounds.c b/set_bounds.c index 45ac489..e326bfc 100644 --- a/set_bounds.c +++ b/set_bounds.c @@ -9,16 +9,31 @@ #include #include #include +#include -int main() +int main(int argc, char *argv[]) { int32_t array[16] = {0}; uint32_t bounds = 64; - puts("Bounds [Choose a value greater than 64]:"); - if (0 == scanf("%u", &bounds)) + if (argc < 2) { - error("Extraneous input"); + puts("Bounds [Choose a value greater than 64]:"); + if (0 == scanf("%u", &bounds)) + { + error("Extraneous input"); + } + } + // Command line argument to simplify testing + else if (atoi(argv[1]) > 64) + { + bounds = atoi(argv[1]); + } + else + { + printf("Please choose a value greater than 64."); + // This will cause the test to fail for a value lower than 64 + exit(0); } // Trying to extend the bounds generates an exception. int32_t *custom_bounds_array = cheri_setbounds(array, bounds); diff --git a/shared_objects/main.c b/shared_objects/main.c index 74c7147..ff2b756 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -17,6 +17,9 @@ void static_variables() { increment(); +#include +#include +#include increment(); increment(); diff --git a/tests/run_cheri_examples.py b/tests/run_cheri_examples.py new file mode 100755 index 0000000..72dcd1a --- /dev/null +++ b/tests/run_cheri_examples.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# This script starts a `qemu-system` to run the tests and then safely shutdown +# the VM. +# +# Inspired by https://github.com/CTSRD-CHERI/cheribuild/tree/master/test-scripts + +import argparse +import os + +from run_tests_common import boot_cheribsd, run_tests_main + + +def run_cheri_examples_tests(qemu: boot_cheribsd.QemuCheriBSDInstance, args: argparse.Namespace) -> bool: + if args.sysroot_dir is not None: + boot_cheribsd.set_ld_library_path_with_sysroot(qemu) + boot_cheribsd.info("Running tests for cheri-examples") + + # This is the BUILD_DIR for the tests, not for this script + os.system("BUILD_DIR=bin ./tests/run_tests.sh") + return True + +if __name__ == '__main__': + run_tests_main(test_function=run_cheri_examples_tests, need_ssh=True, should_mount_builddir=False) \ No newline at end of file diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000..95813ab --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# Test runner to test the following examples: +# bounds.c, set_bounds.c, general_bounds.c, xor_pointers.c. +# +# *** NOTE *** +# The following examples do not need to be +# tested because they are simply exploring features or +# they are tests themselves/experiments: +# allocate.c, check_length.c, check_mask.c, function.c, +# seal.c, sentry.c, setjmp.c, stackscan.c +set -e + +: "${BUILD_DIR:="../bin"}" +: "${SSHPORT:=10021}" +# Examples that must trigger an "In-address space security exception" +EXAMPLES="bounds set_bounds general_bounds xor_pointers" +ARCH=$(ssh -o "StrictHostKeyChecking no" -p $SSHPORT -t root@127.0.0.1 uname -m) + +for example in ${EXAMPLES}; do + if [ ! $(find $BUILD_DIR -prune -empty 2>/dev/null) ]; then + if [ ! -z "${ARCH##*riscv*}" ] && [ "$example" = "set_bounds" ]; then + echo "Skipping '$example' because you are not on riscv64..." + continue + fi + scp -o "StrictHostKeyChecking no" -P $SSHPORT "$BUILD_DIR/$example" root@127.0.0.1:/root + exit_status=0 + RESULT={{$(ssh -o "StrictHostKeyChecking no" -p $SSHPORT -t root@127.0.0.1 "/root/$example 68")} && exit_status=1} || true + echo -n ""$example"... " + if [ $exit_status != 0 ]; then + echo "FAILED! See below for more details." + echo $RESULT | tr -d "{}" + else + echo "ok" + fi + else + echo "Please, build first the examples before running the tests." + exit 1 + fi +done From a6c1699a588702271ee76361b54b9a517b96f9b8 Mon Sep 17 00:00:00 2001 From: Laurence Tratt Date: Tue, 10 Aug 2021 19:08:52 +0100 Subject: [PATCH 36/98] We don't need CMake 3.15.0. Since Debian 10 only has 3.13.0 this requirement is a bit annoying! --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96834cc..9fef3b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.15.0) +cmake_minimum_required (VERSION 3.13.0) project(CHERI_EXAMPLES VERSION 0.1 From 86d2aa40be58d1f38a83867890fa410617bc6ca6 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Fri, 6 Aug 2021 16:00:01 +0100 Subject: [PATCH 37/98] Simple example to show how permissions work. A struct is made read-only by setting only CHERI_PERM_LOAD - Test checking the rise of "In-address space security exception" added to "run_tests.sh". --- employee/Makefile | 27 ++++++++++++++++++++++++ employee/employee.c | 23 ++++++++++++++++++++ employee/full_privileges.c | 20 ++++++++++++++++++ employee/include/employee.h | 16 ++++++++++++++ employee/read_only.c | 42 +++++++++++++++++++++++++++++++++++++ tests/run_tests.sh | 18 +++++++++++++--- 6 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 employee/Makefile create mode 100644 employee/employee.c create mode 100644 employee/full_privileges.c create mode 100644 employee/include/employee.h create mode 100644 employee/read_only.c diff --git a/employee/Makefile b/employee/Makefile new file mode 100644 index 0000000..e76fb23 --- /dev/null +++ b/employee/Makefile @@ -0,0 +1,27 @@ +CC=$(HOME)/cheri/output/sdk/bin/clang +CFORMAT=$(HOME)/cheri/output/sdk/bin/clang-format +CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg +SSHPORT=10021 +export + +read_only := read_only.c employee.c +full_privileges := full_privileges.c employee.c + +all: bin/full_privileges bin/read_only + +bin/read_only: $(read_only) + $(CC) $(CFLAGS) $^ -o $@ + +bin/full_privileges: $(full_privileges) + $(CC) $(CFLAGS) $^ -o $@ + +run: + scp -P $(SSHPORT) bin/* root@127.0.0.1:/root + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/full_privileges' + ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/read_only' + +clang-format: + $(CFORMAT) -i $(cfiles) + +clean: + rm -R bin/* \ No newline at end of file diff --git a/employee/employee.c b/employee/employee.c new file mode 100644 index 0000000..e60582e --- /dev/null +++ b/employee/employee.c @@ -0,0 +1,23 @@ +/* + * Auxiliary functions to display employee's details and + * change the salary. + */ + +#include "include/employee.h" +#include +#include +#include + +void change_salary(struct employee *e, double salary) +{ + e->salary = salary; +} + +void print_details(struct employee *e) +{ + printf("Employee Id: %d \n", e->id); + printf("Employee Name: %s \n", e->name); + printf("Employee Surname: %s\n", e->surname); + printf("Employee Salary: %lf\n", e->salary); + fflush(stdout); +} \ No newline at end of file diff --git a/employee/full_privileges.c b/employee/full_privileges.c new file mode 100644 index 0000000..821750f --- /dev/null +++ b/employee/full_privileges.c @@ -0,0 +1,20 @@ +/* A normal program without any capabilities set. + * Print the details of a generic employee and + * and then allows the user to change the salary. + */ + +#include "include/employee.h" +#include +#include +#include + +int main() +{ + struct employee employee = {0, "Good", "Employee", 55000}; + double salary = 0.0; + print_details(&employee); + printf("\nInsert the new salary for this employee: \n"); + scanf("%lf", &salary); + change_salary(&employee, salary); + print_details(&employee); +} diff --git a/employee/include/employee.h b/employee/include/employee.h new file mode 100644 index 0000000..10c782f --- /dev/null +++ b/employee/include/employee.h @@ -0,0 +1,16 @@ +#include +#include +#include + +struct employee +{ + int id; + char *name; + char *surname; + double salary; +}; + +void change_salary(struct employee *e, double salary); +void print_details(struct employee *e); + +struct employee *set_read_only(struct employee *e); \ No newline at end of file diff --git a/employee/read_only.c b/employee/read_only.c new file mode 100644 index 0000000..11efb2b --- /dev/null +++ b/employee/read_only.c @@ -0,0 +1,42 @@ +/* This program is very similar to "full_privileges.c" + * but here we set the permissions of the capability + * to LOAD, i.e. read-only. So the user is not allowed to + * change the salary. + */ + +#include "../include/common.h" +#include "include/employee.h" +#include +#include +#include + +int main() +{ + struct employee employee = {0, "Good", "Employee", 55000}; + double salary = 0.0; + // set the permissions to read-only + struct employee *ro_employee = set_read_only(&employee); + inspect_pointer(ro_employee); + print_details(ro_employee); + printf("The struct is read-only so trying to change the salary will make the program crash..."); + printf("\nInsert the new salary for this employee: \n"); + fflush(stdout); + scanf("%lf", &salary); + + change_salary(ro_employee, salary); + print_details(ro_employee); +} + +/* + * Traverse the struct to set the permissions. + */ +struct employee *set_read_only(struct employee *e) +{ + char *ro_name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); + char *ro_surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); + struct employee *ro_employee = + (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD || CHERI_PERM_LOAD_CAP); + ro_employee->name = ro_name; + ro_employee->surname = ro_surname; + return ro_employee; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 95813ab..d713fba 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1,6 +1,7 @@ #!/bin/sh # Test runner to test the following examples: -# bounds.c, set_bounds.c, general_bounds.c, xor_pointers.c. +# bounds.c, set_bounds.c, general_bounds.c, xor_pointers.c, +# read_only.c (from the employee example) # # *** NOTE *** # The following examples do not need to be @@ -13,9 +14,17 @@ set -e : "${BUILD_DIR:="../bin"}" : "${SSHPORT:=10021}" # Examples that must trigger an "In-address space security exception" -EXAMPLES="bounds set_bounds general_bounds xor_pointers" +EXAMPLES="bounds set_bounds general_bounds xor_pointers read_only" +EMPLOYEE_DIR="./employee/bin" ARCH=$(ssh -o "StrictHostKeyChecking no" -p $SSHPORT -t root@127.0.0.1 uname -m) +if [ ! $(find $EMPLOYEE_DIR -prune -empty 2>/dev/null) ]; then + cp $EMPLOYEE_DIR/read_only $BUILD_DIR/read_only +else + echo "WARNING: you have not built the employee example yet!" + echo "SKIPPING employee/read_only..." +fi + for example in ${EXAMPLES}; do if [ ! $(find $BUILD_DIR -prune -empty 2>/dev/null) ]; then if [ ! -z "${ARCH##*riscv*}" ] && [ "$example" = "set_bounds" ]; then @@ -25,6 +34,9 @@ for example in ${EXAMPLES}; do scp -o "StrictHostKeyChecking no" -P $SSHPORT "$BUILD_DIR/$example" root@127.0.0.1:/root exit_status=0 RESULT={{$(ssh -o "StrictHostKeyChecking no" -p $SSHPORT -t root@127.0.0.1 "/root/$example 68")} && exit_status=1} || true + if [ "$example" = "read_only" ]; then + example=employee/$example + fi echo -n ""$example"... " if [ $exit_status != 0 ]; then echo "FAILED! See below for more details." @@ -36,4 +48,4 @@ for example in ${EXAMPLES}; do echo "Please, build first the examples before running the tests." exit 1 fi -done +done \ No newline at end of file From 1aefb6f1df07fd378014cfaef8a2f5b9cdcb6ac3 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Thu, 19 Aug 2021 11:42:03 +0000 Subject: [PATCH 38/98] Create `bin` dir in Makefile - The user should not be confused by not having a `bin` dir; - The build should not fail because a `bin` dir does not exist. --- Makefile.morello | 2 ++ Makefile.riscv64 | 2 ++ employee/Makefile | 2 ++ 3 files changed, 6 insertions(+) diff --git a/Makefile.morello b/Makefile.morello index 404d895..9f3c1cd 100644 --- a/Makefile.morello +++ b/Makefile.morello @@ -14,9 +14,11 @@ examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) all: $(examples) bin/%: %.c + @mkdir -p $(@D) $(CC) $(CFLAGS) $< -o $@ bin/%: %.cpp + @mkdir -p $(@D) $(CXX) $(CFLAGS) $< -o $@ .SECONDEXPANSION: diff --git a/Makefile.riscv64 b/Makefile.riscv64 index 95c9360..f504cdd 100644 --- a/Makefile.riscv64 +++ b/Makefile.riscv64 @@ -14,9 +14,11 @@ examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) all: $(examples) bin/%: %.c + @mkdir -p $(@D) $(CC) $(CFLAGS) $< -o $@ bin/%: %.cpp + @mkdir -p $(@D) $(CXX) $(CFLAGS) $< -o $@ .SECONDEXPANSION: diff --git a/employee/Makefile b/employee/Makefile index e76fb23..e0e41ac 100644 --- a/employee/Makefile +++ b/employee/Makefile @@ -10,9 +10,11 @@ full_privileges := full_privileges.c employee.c all: bin/full_privileges bin/read_only bin/read_only: $(read_only) + @mkdir -p $(@D) $(CC) $(CFLAGS) $^ -o $@ bin/full_privileges: $(full_privileges) + @mkdir -p $(@D) $(CC) $(CFLAGS) $^ -o $@ run: From 278ded6cc220abf7982c83a0ff7c23c304e07f6a Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Thu, 19 Aug 2021 09:59:35 +0000 Subject: [PATCH 39/98] Fix bug where stack pointer is returned to caller The tests did not detect a subtle bug due to `read_only` originally returning a stack pointer and causing undefined behaviour. --- employee/Makefile | 3 ++- employee/employee.c | 4 ++-- employee/read_only.c | 32 +++++++++++++++----------------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/employee/Makefile b/employee/Makefile index e0e41ac..33a55b8 100644 --- a/employee/Makefile +++ b/employee/Makefile @@ -4,6 +4,7 @@ CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 export +employee := full_privileges.c read_only.c employee.c include/employee.h read_only := read_only.c employee.c full_privileges := full_privileges.c employee.c @@ -23,7 +24,7 @@ run: ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/read_only' clang-format: - $(CFORMAT) -i $(cfiles) + $(CFORMAT) -i $(employee) clean: rm -R bin/* \ No newline at end of file diff --git a/employee/employee.c b/employee/employee.c index e60582e..4bd426c 100644 --- a/employee/employee.c +++ b/employee/employee.c @@ -15,9 +15,9 @@ void change_salary(struct employee *e, double salary) void print_details(struct employee *e) { - printf("Employee Id: %d \n", e->id); + printf("Employee Id: %d\n", e->id); printf("Employee Name: %s \n", e->name); printf("Employee Surname: %s\n", e->surname); - printf("Employee Salary: %lf\n", e->salary); + printf("Employee Salary: %.2lf\n", e->salary); fflush(stdout); } \ No newline at end of file diff --git a/employee/read_only.c b/employee/read_only.c index 11efb2b..fd197c9 100644 --- a/employee/read_only.c +++ b/employee/read_only.c @@ -1,29 +1,31 @@ /* This program is very similar to "full_privileges.c" * but here we set the permissions of the capability * to LOAD, i.e. read-only. So the user is not allowed to - * change the salary. + * change, e.g. the salary. */ #include "../include/common.h" #include "include/employee.h" +#include #include #include #include int main() { - struct employee employee = {0, "Good", "Employee", 55000}; - double salary = 0.0; + struct employee employee = {1000, "Good", "Employee", 55000.0}; + printf("--- EMPLOYEE ---\n"); // set the permissions to read-only struct employee *ro_employee = set_read_only(&employee); - inspect_pointer(ro_employee); + assert((cheri_perms_get(ro_employee) & CHERI_PERM_STORE) == 0); print_details(ro_employee); - printf("The struct is read-only so trying to change the salary will make the program crash..."); - printf("\nInsert the new salary for this employee: \n"); + double new_salary = 65000.0; + printf( + "\nThe struct is read-only so trying to change the salary to %.2lf will make the program " + "crash...\n", + new_salary); fflush(stdout); - scanf("%lf", &salary); - - change_salary(ro_employee, salary); + change_salary(ro_employee, new_salary); print_details(ro_employee); } @@ -32,11 +34,7 @@ int main() */ struct employee *set_read_only(struct employee *e) { - char *ro_name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); - char *ro_surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); - struct employee *ro_employee = - (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD || CHERI_PERM_LOAD_CAP); - ro_employee->name = ro_name; - ro_employee->surname = ro_surname; - return ro_employee; -} + e->name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); + e->surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); + return (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); +} \ No newline at end of file From 3eadfdbbc6fcff7cce04e9d972d2c7aa08776cad Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Fri, 20 Aug 2021 13:43:59 +0000 Subject: [PATCH 40/98] Some refactoring to allow code reuse for the next parts of this example 1. Removed `employee.c` and moved its content plus `set_read_only()` to `employee.h; 2. updated `Makefile` to reflect the changes. --- employee/Makefile | 25 +++++++++++-------------- employee/employee.c | 23 ----------------------- employee/include/employee.h | 32 +++++++++++++++++++++++++++++--- employee/read_only.c | 12 ------------ 4 files changed, 40 insertions(+), 52 deletions(-) delete mode 100644 employee/employee.c diff --git a/employee/Makefile b/employee/Makefile index 33a55b8..785fb8e 100644 --- a/employee/Makefile +++ b/employee/Makefile @@ -4,27 +4,24 @@ CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 export -employee := full_privileges.c read_only.c employee.c include/employee.h -read_only := read_only.c employee.c -full_privileges := full_privileges.c employee.c +cfiles := full_privileges.c read_only.c +employee := $(patsubst %.c,bin/%,$(cfiles)) -all: bin/full_privileges bin/read_only +.PHONY: all run clean -bin/read_only: $(read_only) - @mkdir -p $(@D) - $(CC) $(CFLAGS) $^ -o $@ +all: $(employee) -bin/full_privileges: $(full_privileges) +bin/%: %.c @mkdir -p $(@D) - $(CC) $(CFLAGS) $^ -o $@ + $(CC) $(CFLAGS) $< -o $@ -run: - scp -P $(SSHPORT) bin/* root@127.0.0.1:/root - ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/full_privileges' - ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/read_only' +.SECONDEXPANSION: +run-%: bin/% $$(wildcard %.c) + scp -P $(SSHPORT) $(word 2,$^) bin/$( -#include -#include - -void change_salary(struct employee *e, double salary) -{ - e->salary = salary; -} - -void print_details(struct employee *e) -{ - printf("Employee Id: %d\n", e->id); - printf("Employee Name: %s \n", e->name); - printf("Employee Surname: %s\n", e->surname); - printf("Employee Salary: %.2lf\n", e->salary); - fflush(stdout); -} \ No newline at end of file diff --git a/employee/include/employee.h b/employee/include/employee.h index 10c782f..79a6e3e 100644 --- a/employee/include/employee.h +++ b/employee/include/employee.h @@ -1,3 +1,9 @@ +/* + * Employee struct and auxiliary functions to display employee's details, + * change the salary, and operate on the struct's permissions. + */ + +#include "../../include/common.h" #include #include #include @@ -10,7 +16,27 @@ struct employee double salary; }; -void change_salary(struct employee *e, double salary); -void print_details(struct employee *e); +void print_details(struct employee *e) +{ + printf("--- EMPLOYEE ---\n"); + printf("Employee Id: %d\n", e->id); + printf("Employee Name: %s \n", e->name); + printf("Employee Surname: %s\n", e->surname); + printf("Employee Salary: %.2lf\n", e->salary); + fflush(stdout); +} -struct employee *set_read_only(struct employee *e); \ No newline at end of file +void change_salary(struct employee *e, double salary) +{ + e->salary = salary; +} + +/* + * Traverse the struct to set the permissions. + */ +struct employee *set_read_only(struct employee *e) +{ + e->name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); + e->surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); + return (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); +} \ No newline at end of file diff --git a/employee/read_only.c b/employee/read_only.c index fd197c9..141e07f 100644 --- a/employee/read_only.c +++ b/employee/read_only.c @@ -4,7 +4,6 @@ * change, e.g. the salary. */ -#include "../include/common.h" #include "include/employee.h" #include #include @@ -14,7 +13,6 @@ int main() { struct employee employee = {1000, "Good", "Employee", 55000.0}; - printf("--- EMPLOYEE ---\n"); // set the permissions to read-only struct employee *ro_employee = set_read_only(&employee); assert((cheri_perms_get(ro_employee) & CHERI_PERM_STORE) == 0); @@ -27,14 +25,4 @@ int main() fflush(stdout); change_salary(ro_employee, new_salary); print_details(ro_employee); -} - -/* - * Traverse the struct to set the permissions. - */ -struct employee *set_read_only(struct employee *e) -{ - e->name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); - e->surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); - return (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); } \ No newline at end of file From 7bbca1331a74ebe9f74290f0f91c6ae91eaa41b3 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 23 Aug 2021 07:50:52 +0000 Subject: [PATCH 41/98] Modified `inspect_pointer` to print more information and in the correct order --- include/common.h | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/include/common.h b/include/common.h index a2637c4..957841f 100644 --- a/include/common.h +++ b/include/common.h @@ -3,6 +3,10 @@ #include #include +/* + * Print information about a capability + */ + void inspect_pointer(void *ptr) { uint64_t length = cheri_length_get(ptr); @@ -14,9 +18,13 @@ void inspect_pointer(void *ptr) bool tag = cheri_tag_get(ptr); uint64_t offset = cheri_offset_get(ptr); - printf("Address: %04lx, Base: %04lx, End: %04lx Flags: %04lx, Length: %04lx, Offset: %04lx, " - "Perms: %04lx, Type: %04lx, Tag: %d\n", - address, base, base + length, flags, length, offset, perms, type, tag); + + printf("*** CAPABILITY INFO for ***\n"); + printf("%#lp\n", ptr); + printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, End: %04lx, Flags: %lx, Length: %04lx, Offset: %04lx\n" + , + tag, perms, type, address, base, base + length, flags, length, offset); + printf("****************************\n"); } void error(char *string) From 6ffe77ec306278bc8869692e2509582443d31adc Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 23 Aug 2021 08:05:21 +0000 Subject: [PATCH 42/98] Renamed `inspect_pointer` to `pp_cap` and refactored existing code --- allocate.c | 2 +- bounds-cxx.cpp | 2 +- bounds.c | 2 +- example_allocators/bump_allocator/bad_client.c | 2 +- example_allocators/bump_allocator/good_client.c | 2 +- function.c | 2 +- general_bounds.c | 2 +- include/common.h | 10 ++++------ mmap.c | 4 ++-- seal.c | 6 +++--- setjmp.c | 2 +- shared_objects/include/find_sentries.h | 2 +- shared_objects/main.c | 2 +- shared_objects/unexported_function.c | 4 ++-- stackscan.c | 6 +++--- timsort/timsort.c | 2 +- 16 files changed, 25 insertions(+), 27 deletions(-) diff --git a/allocate.c b/allocate.c index 9c3fd4b..b556836 100644 --- a/allocate.c +++ b/allocate.c @@ -18,5 +18,5 @@ int main() } void *memory = malloc(size); - inspect_pointer(memory); + pp_cap(memory); } diff --git a/bounds-cxx.cpp b/bounds-cxx.cpp index d27976a..fd21aaf 100644 --- a/bounds-cxx.cpp +++ b/bounds-cxx.cpp @@ -8,7 +8,7 @@ int main() std::vector list = {1, 2, 3, 4}; for (unsigned int i = 0; i <= 16; i++) { - inspect_pointer(&list[i]); + pp_cap(&list[i]); std::cout << "Value: " << list[i] << std::endl; } } diff --git a/bounds.c b/bounds.c index c84a4f2..a019605 100644 --- a/bounds.c +++ b/bounds.c @@ -18,7 +18,7 @@ int main() uint64_t length = cheri_length_get(typed_array); for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 15; counter++) { - inspect_pointer(typed_array + counter); + pp_cap(typed_array + counter); // Read value to crash printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); } diff --git a/example_allocators/bump_allocator/bad_client.c b/example_allocators/bump_allocator/bad_client.c index 271a89b..af13f84 100644 --- a/example_allocators/bump_allocator/bad_client.c +++ b/example_allocators/bump_allocator/bad_client.c @@ -31,7 +31,7 @@ int main() p = (int *)bump_alloc(1 * sizeof(int)); if (DEBUG_PRINTF) - inspect_pointer(p); + pp_cap(p); if (p) { *p = 42; diff --git a/example_allocators/bump_allocator/good_client.c b/example_allocators/bump_allocator/good_client.c index 7ca070a..d22b6b4 100644 --- a/example_allocators/bump_allocator/good_client.c +++ b/example_allocators/bump_allocator/good_client.c @@ -29,7 +29,7 @@ int main() { int *x = (int *)bump_alloc(1 * sizeof(int)); if (DEBUG_PRINTF) - inspect_pointer(x); + pp_cap(x); if (x) { *x = 42; diff --git a/function.c b/function.c index b468388..7e58b0b 100644 --- a/function.c +++ b/function.c @@ -50,7 +50,7 @@ int main() int c_gcd = gcd(a, b); printf("The gcd of these numbers is: %d\n", c_gcd); - inspect_pointer(*gcd); + pp_cap(*gcd); return 0; } diff --git a/general_bounds.c b/general_bounds.c index fc49141..8c823e5 100644 --- a/general_bounds.c +++ b/general_bounds.c @@ -25,7 +25,7 @@ int main(int argc, char *argv[]) uint64_t length = cheri_getlength(typed_array); for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 11; counter++) { - inspect_pointer(typed_array + counter); + pp_cap(typed_array + counter); // Read value to crash if (counter == 12) { diff --git a/include/common.h b/include/common.h index 957841f..4a2b2af 100644 --- a/include/common.h +++ b/include/common.h @@ -7,7 +7,7 @@ * Print information about a capability */ -void inspect_pointer(void *ptr) +void pp_cap(void *ptr) { uint64_t length = cheri_length_get(ptr); uint64_t address = cheri_address_get(ptr); @@ -19,12 +19,10 @@ void inspect_pointer(void *ptr) uint64_t offset = cheri_offset_get(ptr); - printf("*** CAPABILITY INFO for ***\n"); - printf("%#lp\n", ptr); - printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, End: %04lx, Flags: %lx, Length: %04lx, Offset: %04lx\n" - , + printf("Capability: %#lp\n", ptr); + printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, End: %04lx, Flags: %lx, " + "Length: %04lx, Offset: %04lx\n", tag, perms, type, address, base, base + length, flags, length, offset); - printf("****************************\n"); } void error(char *string) diff --git a/mmap.c b/mmap.c index f58ae32..d2fd960 100644 --- a/mmap.c +++ b/mmap.c @@ -108,8 +108,8 @@ int main() code = get_executable_block(); code = generate_purecap(code); - inspect_pointer(cheri_getpcc()); - inspect_pointer(code); + pp_cap(cheri_getpcc()); + pp_cap(code); int (*code_function)() = (int (*)())code; int result = code_function(); diff --git a/seal.c b/seal.c index 6cfce1c..47b7eab 100644 --- a/seal.c +++ b/seal.c @@ -33,7 +33,7 @@ typedef union int main() { void *pcc = cheri_pcc_get(); - inspect_pointer(pcc); + pp_cap(pcc); void *searling_root; if (sysarch(CHERI_GET_SEALCAP, &searling_root) < 0) @@ -48,14 +48,14 @@ int main() data.arr[1] = 0; first = cheri_seal(first, other); - inspect_pointer(first); + pp_cap(first); return 0; for (uint32_t i = 0; i < 64; i++) { data.arr[0] |= (1 << i); printf("i: %d ", i); - inspect_pointer(data.ptr); + pp_cap(data.ptr); data.arr[0] = 0; } } diff --git a/setjmp.c b/setjmp.c index 5a81713..ec3f95a 100644 --- a/setjmp.c +++ b/setjmp.c @@ -27,7 +27,7 @@ int main() { printf("[STACK POINTER] "); } - inspect_pointer(((void **)buffer)[idx]); + pp_cap(((void **)buffer)[idx]); } } } diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index 36a0da7..ce3f9f5 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -34,7 +34,7 @@ bool scan_range(void *ptr, void *exact) printf("[Range match] "); } - inspect_pointer(current); + pp_cap(current); } } } diff --git a/shared_objects/main.c b/shared_objects/main.c index ff2b756..e17f03a 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -45,7 +45,7 @@ void compartments_per_object() void unexported_functions() { printf("Finding do_work using dlsym (main): "); - inspect_pointer(dlsym(NULL, "do_work")); + pp_cap(dlsym(NULL, "do_work")); void *function_to_search_for = test(); printf("test: %p\n", function_to_search_for); diff --git a/shared_objects/unexported_function.c b/shared_objects/unexported_function.c index 0411f0f..16be0cb 100644 --- a/shared_objects/unexported_function.c +++ b/shared_objects/unexported_function.c @@ -21,9 +21,9 @@ __attribute__((visibility("hidden"))) void do_work() void *test() { printf("Finding do_work using dlsym (lib4):\nptr: "); - inspect_pointer(dlsym(NULL, "do_work")); + pp_cap(dlsym(NULL, "do_work")); printf("Obtaining a direct pointer to it: \nptr: "); - inspect_pointer(&do_work); + pp_cap(&do_work); printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); printf(" ------- scanning pcc for sealed pointers --------\n"); bool found = scan_range(cheri_getpcc(), &do_work); diff --git a/stackscan.c b/stackscan.c index f0ab4d4..d4e4600 100644 --- a/stackscan.c +++ b/stackscan.c @@ -50,8 +50,8 @@ void scan_range(void *start, void *end) void **location = (void **)start; puts("Scanning range: "); - inspect_pointer(start); - inspect_pointer(end); + pp_cap(start); + pp_cap(end); puts("\n"); while (location > end) @@ -66,7 +66,7 @@ void scan_range(void *start, void *end) { printf("[Executable] "); } - inspect_pointer(*location); + pp_cap(*location); } location -= 1; } diff --git a/timsort/timsort.c b/timsort/timsort.c index 2fa2600..f6b8c69 100644 --- a/timsort/timsort.c +++ b/timsort/timsort.c @@ -10,7 +10,7 @@ void insertionSort(bp_array bp) size_t length = (get_length(bp) / sizeof(int)) - base; int *arr = get_pointer(bp); - inspect_pointer(arr); + pp_cap(arr); int ix, ixValue, ixP; for (ix = base + 1; ix < (base + length); ix++) From 11a68b949bbd258b9b51a6cad5ade77e31581029 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 23 Aug 2021 10:25:26 +0000 Subject: [PATCH 43/98] Restore full privileges by explicitly sealing and unsealing --- employee/Makefile | 2 +- employee/sealed.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 employee/sealed.c diff --git a/employee/Makefile b/employee/Makefile index 785fb8e..4431570 100644 --- a/employee/Makefile +++ b/employee/Makefile @@ -4,7 +4,7 @@ CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 export -cfiles := full_privileges.c read_only.c +cfiles := full_privileges.c read_only.c sealed.c employee := $(patsubst %.c,bin/%,$(cfiles)) .PHONY: all run clean diff --git a/employee/sealed.c b/employee/sealed.c new file mode 100644 index 0000000..c7e7829 --- /dev/null +++ b/employee/sealed.c @@ -0,0 +1,43 @@ +/* + * Starting from a `read_only` struct, this program + * restores full privileges using a previous `sealed` + * capabilty. + */ + +#include "../include/common.h" +#include +#include +#include +#include +#include + +int main() +{ + // Request special capability `sealcap` from the operating system + // in order to use it as key to seal `new_small_salary` + void *__capability sealcap; + size_t sealcap_size = sizeof(sealcap); + if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) + { + error("Fatal error. Cannot get `securiy.cheri.sealcap`."); + exit(1); + } + assert(cheri_perms_get(sealcap) & CHERI_PERM_SEAL); + uint8_t *new_small_salary; + // Seal `new_small_salary` using previously requested `sealcap` + new_small_salary = (uint8_t *)malloc(sizeof(uint8_t)); + new_small_salary = (uint8_t *)cheri_seal(new_small_salary, sealcap); + assert(cheri_is_sealed(new_small_salary)); + uint8_t *small_salary; + small_salary = (uint8_t *)malloc(sizeof(uint8_t)); + assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); + pp_cap(small_salary); + // Make `small_salary` read-only + small_salary = (uint8_t *)cheri_perms_and(&small_salary, CHERI_PERM_LOAD); + assert(cheri_perms_get(small_salary) & CHERI_PERM_LOAD); + pp_cap(small_salary); + // Restore it to read-write using previously sealed capability `new_small_salary` + small_salary = (uint8_t *)cheri_unseal(new_small_salary, sealcap); + assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); + pp_cap(small_salary); +} From 247cedecbd65867cac42317c647711d66dfcda3b Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 23 Aug 2021 16:43:40 +0000 Subject: [PATCH 44/98] Fix some formatting issues + Some files were not `clang-formatted`; + `clang-format` did not allow space after cast. --- .clang-format | 1 + Makefile.riscv64 | 5 +++-- employee/include/employee.h | 6 +++--- employee/sealed.c | 10 +++++----- .../bitmap_allocator/bitmap_alloc.c | 16 ++++++++-------- .../bitmap_allocator/good_client.c | 2 +- example_allocators/bump_allocator/bad_client.c | 2 +- example_allocators/bump_allocator/good_client.c | 2 +- .../freelist_allocator/binary_trees.c | 4 ++-- .../freelist_allocator/freelist_allocator.c | 8 ++++---- mmap.c | 2 +- sentry.c | 14 +++++++------- setjmp.c | 6 +++--- shared_objects/compartment_per_object.c | 6 +++--- shared_objects/include/find_sentries.h | 2 +- stackscan.c | 8 ++++---- timsort/include/timsort_lib.h | 14 +++++++------- xor_pointers.c | 14 +++++++------- 18 files changed, 62 insertions(+), 60 deletions(-) diff --git a/.clang-format b/.clang-format index f7eaa90..4f2b52b 100644 --- a/.clang-format +++ b/.clang-format @@ -5,6 +5,7 @@ UseTab: Always AllowShortIfStatementsOnASingleLine: Never AllowShortFunctionsOnASingleLine: Empty AllowAllArgumentsOnNextLine: true +SpaceAfterCStyleCast: true BreakBeforeBraces: Allman BraceWrapping: AfterControlStatement: Always diff --git a/Makefile.riscv64 b/Makefile.riscv64 index f504cdd..f924f42 100644 --- a/Makefile.riscv64 +++ b/Makefile.riscv64 @@ -5,7 +5,8 @@ CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg SSHPORT=10021 export -cfiles := $(wildcard *.c) +cfiles := $(wildcard *.c **/*.c **/**/*.c) +hfiles := $(wildcard include/*.h **/include/*.h) cppfiles := $(wildcard *.cpp) examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) @@ -27,7 +28,7 @@ run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) ssh -p $(SSHPORT) root@127.0.0.1 -t '/root/$(name = (char *)cheri_perms_and(e->name, CHERI_PERM_LOAD); - e->surname = (char *)cheri_perms_and(e->surname, CHERI_PERM_LOAD); - return (struct employee *)cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); + e->name = (char *) cheri_perms_and(e->name, CHERI_PERM_LOAD); + e->surname = (char *) cheri_perms_and(e->surname, CHERI_PERM_LOAD); + return (struct employee *) cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); } \ No newline at end of file diff --git a/employee/sealed.c b/employee/sealed.c index c7e7829..8c5f650 100644 --- a/employee/sealed.c +++ b/employee/sealed.c @@ -25,19 +25,19 @@ int main() assert(cheri_perms_get(sealcap) & CHERI_PERM_SEAL); uint8_t *new_small_salary; // Seal `new_small_salary` using previously requested `sealcap` - new_small_salary = (uint8_t *)malloc(sizeof(uint8_t)); - new_small_salary = (uint8_t *)cheri_seal(new_small_salary, sealcap); + new_small_salary = (uint8_t *) malloc(sizeof(uint8_t)); + new_small_salary = (uint8_t *) cheri_seal(new_small_salary, sealcap); assert(cheri_is_sealed(new_small_salary)); uint8_t *small_salary; - small_salary = (uint8_t *)malloc(sizeof(uint8_t)); + small_salary = (uint8_t *) malloc(sizeof(uint8_t)); assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); pp_cap(small_salary); // Make `small_salary` read-only - small_salary = (uint8_t *)cheri_perms_and(&small_salary, CHERI_PERM_LOAD); + small_salary = (uint8_t *) cheri_perms_and(&small_salary, CHERI_PERM_LOAD); assert(cheri_perms_get(small_salary) & CHERI_PERM_LOAD); pp_cap(small_salary); // Restore it to read-write using previously sealed capability `new_small_salary` - small_salary = (uint8_t *)cheri_unseal(new_small_salary, sealcap); + small_salary = (uint8_t *) cheri_unseal(new_small_salary, sealcap); assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); pp_cap(small_salary); } diff --git a/example_allocators/bitmap_allocator/bitmap_alloc.c b/example_allocators/bitmap_allocator/bitmap_alloc.c index 37b7706..0be9772 100644 --- a/example_allocators/bitmap_allocator/bitmap_alloc.c +++ b/example_allocators/bitmap_allocator/bitmap_alloc.c @@ -79,8 +79,8 @@ void init_alloc(int num_chunks, int chunk_size) char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); /* request memory for our bitmap */ - bitmap = (unsigned char *)mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, - PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + bitmap = (unsigned char *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (res == MAP_FAILED || bitmap == MAP_FAILED) { @@ -91,9 +91,9 @@ void init_alloc(int num_chunks, int chunk_size) /* NB mmap min bounds for capability is 1 page (4K) */ buffer = res; /* check buffer is aligned */ - assert((uintptr_t)buffer % sizeof(void *) == 0); + assert((uintptr_t) buffer % sizeof(void *) == 0); /* check bitmap is aligned */ - assert((uintptr_t)bitmap % sizeof(void *) == 0); + assert((uintptr_t) bitmap % sizeof(void *) == 0); bytes_per_chunk = adjusted_chunk_size; buffer_size = adjusted_num_chunks * adjusted_chunk_size; @@ -125,14 +125,14 @@ char *alloc_chunk() // return the corresponding chunk // (setting its capability bounds) int i = 0; - while (bitmap[i] == (unsigned char)0xff) + while (bitmap[i] == (unsigned char) 0xff) { i++; if (i >= bitmap_size) break; } // do we have a 0? - if (i < bitmap_size && bitmap[i] != (unsigned char)0xff) + if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff) { // find the lowest 0 ... int j = 0; @@ -148,7 +148,7 @@ char *alloc_chunk() // now i is the word index, j is the bit index // set this bit to 1 ... // and work out the chunk to allocate - updated_byte = bitmap[i] + (unsigned char)(1 << j); + updated_byte = bitmap[i] + (unsigned char) (1 << j); bitmap[i] = updated_byte; chunk_index = i * BITS_PER_BYTE + j; @@ -173,7 +173,7 @@ void free_chunk(void *chunk) assert(bitmap_index < bitmap_size); int bitmap_offset = chunk_index % BITS_PER_BYTE; /* set this bitmap entry to 0 */ - unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char)(~(1 << bitmap_offset)); + unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset)); bitmap[bitmap_index] = updated_byte; return; } diff --git a/example_allocators/bitmap_allocator/good_client.c b/example_allocators/bitmap_allocator/good_client.c index 1c9234d..8afd266 100644 --- a/example_allocators/bitmap_allocator/good_client.c +++ b/example_allocators/bitmap_allocator/good_client.c @@ -40,7 +40,7 @@ int main() /* now try to do some allocations */ for (i = 0; i < n; i++) { - list *tmp = (list *)alloc_chunk(); + list *tmp = (list *) alloc_chunk(); if (tmp) { tmp->payload = i; diff --git a/example_allocators/bump_allocator/bad_client.c b/example_allocators/bump_allocator/bad_client.c index af13f84..e3c3166 100644 --- a/example_allocators/bump_allocator/bad_client.c +++ b/example_allocators/bump_allocator/bad_client.c @@ -29,7 +29,7 @@ int main() init_alloc(NUM_WORDS * sizeof(int)); - p = (int *)bump_alloc(1 * sizeof(int)); + p = (int *) bump_alloc(1 * sizeof(int)); if (DEBUG_PRINTF) pp_cap(p); if (p) diff --git a/example_allocators/bump_allocator/good_client.c b/example_allocators/bump_allocator/good_client.c index d22b6b4..1a3d733 100644 --- a/example_allocators/bump_allocator/good_client.c +++ b/example_allocators/bump_allocator/good_client.c @@ -27,7 +27,7 @@ int main() /* now try to do some bump pointer allocations */ for (i = 0; i < NUM_WORDS; i++) { - int *x = (int *)bump_alloc(1 * sizeof(int)); + int *x = (int *) bump_alloc(1 * sizeof(int)); if (DEBUG_PRINTF) pp_cap(x); if (x) diff --git a/example_allocators/freelist_allocator/binary_trees.c b/example_allocators/freelist_allocator/binary_trees.c index b892800..3d39d8f 100644 --- a/example_allocators/freelist_allocator/binary_trees.c +++ b/example_allocators/freelist_allocator/binary_trees.c @@ -25,7 +25,7 @@ treeNode *NewTreeNode(treeNode *left, treeNode *right) { treeNode *new; - new = (treeNode *)alloc(sizeof(treeNode)); + new = (treeNode *) alloc(sizeof(treeNode)); new->left = left; new->right = right; @@ -81,7 +81,7 @@ int main(int argc, char *argv[]) * size-class assumptions */ pages = ((2 << (stretchDepth + 3)) * sizeof(treeNode)) / BYTES_IN_PAGE; - printf("treenode size is %u bytes\n", (unsigned int)sizeof(treeNode)); + printf("treenode size is %u bytes\n", (unsigned int) sizeof(treeNode)); printf("we need %u pages\n", pages); /* allocate memory pool */ diff --git a/example_allocators/freelist_allocator/freelist_allocator.c b/example_allocators/freelist_allocator/freelist_allocator.c index 5c242ff..b40a59b 100644 --- a/example_allocators/freelist_allocator/freelist_allocator.c +++ b/example_allocators/freelist_allocator/freelist_allocator.c @@ -43,13 +43,13 @@ char *insert_linked_list_pointers(size_t cell_size, size_t limit, char *start, c while (next < max) { - ((char **)curr)[0] = next; + ((char **) curr)[0] = next; curr = next; next = curr + cell_size; } // at the end, concatenate this newly formed // list with existing freelist - ((char **)curr)[0] = freelist; + ((char **) curr)[0] = freelist; return start; } @@ -114,7 +114,7 @@ char *alloc(size_t bytes) if (freelist_to_use != NULL) { char *head = freelist_to_use; - char *tail = ((char **)head)[0]; + char *tail = ((char **) head)[0]; switch (size) { case SMALL: @@ -161,6 +161,6 @@ void dealloc(void *buffer) char *cons_onto_freelist(char *cell, char *freelist) { - ((char **)cell)[0] = freelist; + ((char **) cell)[0] = freelist; return cell; } diff --git a/mmap.c b/mmap.c index d2fd960..98f187c 100644 --- a/mmap.c +++ b/mmap.c @@ -111,7 +111,7 @@ int main() pp_cap(cheri_getpcc()); pp_cap(code); - int (*code_function)() = (int (*)())code; + int (*code_function)() = (int (*)()) code; int result = code_function(); printf("Result: %d\n\n", result); return result; diff --git a/sentry.c b/sentry.c index b0bb75b..71727dd 100644 --- a/sentry.c +++ b/sentry.c @@ -46,15 +46,15 @@ void simple_sentry(int arg) // arguments to the next registers. void gen_oop(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *)code; + intptr_t *code_data = (intptr_t *) code; // The additional data needs to be stored somewhere in memory // where it can not be accessed by the user of the function. // This is why it is stored in the same place where the code will be. // As the code is not modifiable and not readable this data is also // protected. - code_data[255] = (intptr_t)function; - code_data[254] = (intptr_t)malloc(sizeof(struct data)); + code_data[255] = (intptr_t) function; + code_data[254] = (intptr_t) malloc(sizeof(struct data)); uint32_t idx = 0; code[idx++] = auipcc(cs2, 1); @@ -74,8 +74,8 @@ void gen_oop(uint32_t *code, void *function) void gen_simple(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *)code; - code_data[255] = (intptr_t)function; + intptr_t *code_data = (intptr_t *) code; + code_data[255] = (intptr_t) function; uint32_t idx = 0; code[idx++] = auipcc(cs2, 1); @@ -85,8 +85,8 @@ void gen_simple(uint32_t *code, void *function) void gen_override(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *)code; - code_data[255] = (intptr_t)function; + intptr_t *code_data = (intptr_t *) code; + code_data[255] = (intptr_t) function; // Here we again generate code for calling the passed function, // but the code modifies the first argument to be different then diff --git a/setjmp.c b/setjmp.c index ec3f95a..2abc3f7 100644 --- a/setjmp.c +++ b/setjmp.c @@ -18,16 +18,16 @@ int main() // buffer[14..31] = ??? for (uint32_t idx; idx < (length / 16); idx++) { - if (cheri_gettag(((void **)buffer)[idx])) + if (cheri_gettag(((void **) buffer)[idx])) { void *csp = cheri_getcsp(); - uint64_t address = cheri_getaddress(((void **)buffer)[idx]); + uint64_t address = cheri_getaddress(((void **) buffer)[idx]); if (cheri_is_address_inbounds(csp, address)) { printf("[STACK POINTER] "); } - pp_cap(((void **)buffer)[idx]); + pp_cap(((void **) buffer)[idx]); } } } diff --git a/shared_objects/compartment_per_object.c b/shared_objects/compartment_per_object.c index 1a1801e..578d965 100644 --- a/shared_objects/compartment_per_object.c +++ b/shared_objects/compartment_per_object.c @@ -41,7 +41,7 @@ struct Car *new_car() const uint32_t vtable_start_index = 2; ptrs[vtable_start_index + 0] = honk; - const ptrdiff_t vtable_offset = ((char *)(ptrs + vtable_start_index) - (char *)ptrs); + const ptrdiff_t vtable_offset = ((char *) (ptrs + vtable_start_index) - (char *) ptrs); const size_t vtable_size = 1 * 16; uint32_t idx = 0; @@ -53,8 +53,8 @@ struct Car *new_car() uint32_t functions_size = vtable_offset + vtable_size; struct Car *public_car = - (struct Car *)(((char *)mem) + sizeof(struct Car_priv) + functions_size); - struct Car_priv *private_car = (struct Car_priv *)(((char *)mem) + functions_size); + (struct Car *) (((char *) mem) + sizeof(struct Car_priv) + functions_size); + struct Car_priv *private_car = (struct Car_priv *) (((char *) mem) + functions_size); private_car->maxSpeed = 10; private_car->crash = &crash; diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index ce3f9f5..70f79b3 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -18,7 +18,7 @@ bool scan_range(void *ptr, void *exact) for (void *iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); iter = cheri_incoffset(iter, 16)) { - void *current = *(void **)iter; + void *current = *(void **) iter; if (is_pointer(current)) { if (cheri_getsealed(current)) diff --git a/stackscan.c b/stackscan.c index d4e4600..fd0f486 100644 --- a/stackscan.c +++ b/stackscan.c @@ -48,7 +48,7 @@ bool is_exec(void *ptr) void scan_range(void *start, void *end) { - void **location = (void **)start; + void **location = (void **) start; puts("Scanning range: "); pp_cap(start); pp_cap(end); @@ -77,13 +77,13 @@ void scan_frames() // We dont's scan the part of the stack taken by the current function // I promise the collect function doesn't allocate void *stack_bot = __builtin_frame_address(0); - void *previous_frame = *(void **)stack_bot; + void *previous_frame = *(void **) stack_bot; inspect_stack(stack_bot); inspect_stack(previous_frame); while (previous_frame < stack_top) { - void **next_frame = (void **)previous_frame; + void **next_frame = (void **) previous_frame; if (!is_stack_pointer(*next_frame)) { printf("Couldn't find a stack pointer at: %p, looking up the stack", next_frame); @@ -121,7 +121,7 @@ int test_3() uint32_t *test() { - uint32_t *values = (uint32_t *)malloc((size_t)test_3()); + uint32_t *values = (uint32_t *) malloc((size_t) test_3()); values[0] = 42; values[1] = 42; return values; diff --git a/timsort/include/timsort_lib.h b/timsort/include/timsort_lib.h index 62d4921..2c9c990 100644 --- a/timsort/include/timsort_lib.h +++ b/timsort/include/timsort_lib.h @@ -32,7 +32,7 @@ int *random_chunk(size_t arr_length) int cmpfunc(const void *a, const void *b) { - return (*(int *)a - *(int *)b); + return (*(int *) a - *(int *) b); } bool arrEq(int arr_a[], int arr_b[], size_t lowerBound, size_t upperBound) @@ -150,7 +150,7 @@ bp_array packBP(int *pointer, const size_t baseIndex, const size_t sizeInBytes) } *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); - return (bp_array)largeDescriptor; + return (bp_array) largeDescriptor; } int *descriptor = cheri_setoffset(pointer, 0); @@ -166,10 +166,10 @@ bp_array packBP(int *pointer, const size_t baseIndex, const size_t sizeInBytes) } *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); - return (bp_array)largeDescriptor; + return (bp_array) largeDescriptor; } - return (bp_array)descriptor; + return (bp_array) descriptor; } bool isMangled(bp_array bp) @@ -181,7 +181,7 @@ void *get_pointer(bp_array bp) { if (isMangled(bp)) { - return cheri_setoffset(get_pointer_mangled(*(struct bp_array_s *)bp), 0); + return cheri_setoffset(get_pointer_mangled(*(struct bp_array_s *) bp), 0); } void *ret = bp; return cheri_setoffset(ret, 0); @@ -191,7 +191,7 @@ size_t get_base(bp_array bp) { if (isMangled(bp)) { - return get_base_mangled(*(struct bp_array_s *)bp); + return get_base_mangled(*(struct bp_array_s *) bp); } return cheri_getoffset(bp); } @@ -200,7 +200,7 @@ size_t get_length(bp_array bp) { if (isMangled(bp)) { - return get_length_mangled(*(struct bp_array_s *)bp); + return get_length_mangled(*(struct bp_array_s *) bp); } return cheri_getlength(bp); } diff --git a/xor_pointers.c b/xor_pointers.c index 80befb0..b293a82 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -40,16 +40,16 @@ typedef struct cell cell_t *alloc_cell(WORD payload, cell_t *tail) { - cell_t *cell = (cell_t *)malloc(sizeof(cell_t)); + cell_t *cell = (cell_t *) malloc(sizeof(cell_t)); cell->data = payload; /* xor this cell's address into the tail's ptr field */ if (tail != NULL) - tail->ptr ^= (WORD)cell; + tail->ptr ^= (WORD) cell; /* store tail address in this cell's ptr field * NOTE most recently allocated cell (i.e. head) * only stores a single ptr, not an XOR'd pair */ - cell->ptr = (WORD)tail; + cell->ptr = (WORD) tail; return cell; } @@ -69,12 +69,12 @@ int main() /* traverse list from head to tail */ printf("%ld\n", head->data); prev = head; - curr = (cell_t *)prev->ptr; + curr = (cell_t *) prev->ptr; while (curr != NULL) { printf("%ld\n", curr->data); // chase tail - next = (cell_t *)((curr->ptr) ^ (WORD)prev); + next = (cell_t *) ((curr->ptr) ^ (WORD) prev); // move along one prev = curr; curr = next; @@ -83,14 +83,14 @@ int main() /* let's traverse back to the start of the list */ next = curr; curr = prev; - prev = (cell_t *)((curr->ptr) ^ (WORD)next); + prev = (cell_t *) ((curr->ptr) ^ (WORD) next); while (curr != head) { printf("%ld\n", curr->data); // chase reverse tail next = curr; curr = prev; - prev = (cell_t *)((curr->ptr) ^ (WORD)next); + prev = (cell_t *) ((curr->ptr) ^ (WORD) next); } printf("%ld\n", head->data); From c3be9f23dc13a272365505a20156f11238cd0db0 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Thu, 26 Aug 2021 14:28:10 +0100 Subject: [PATCH 45/98] Add Jacob Bramley (Arm Ltd) to COPYRIGHT. --- COPYRIGHT | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index d502b60..27f2445 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -13,5 +13,7 @@ of their contributions is retained by an organisation: Aleks Bonin : copyright retained by King's College London - Laurence Tratt : copyright retained by - King's College London + Jacob Bramley : + copyright retained by Arm Limited + Laurence Tratt : + copyright retained by King's College London From a154fc218fcd6efdb602e41afd75f2d29f231fd5 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Thu, 26 Aug 2021 11:12:07 +0100 Subject: [PATCH 46/98] Add Morello makefiles for 'employee'. Support Morello builds for examples not handled by the main makefiles. Notable other changes: - Make it easier to support other platforms in the future. - Make 'clean' a bit safer. It now won't empty a generically-named `bin/` in the user's current working directory, e.g. if run from an unexpected location. - Require SSHPORT to be explicit. The default often wasn't correct, and this way we can print a useful error message. --- employee/Makefile | 27 ----------------- employee/Makefile.common | 50 +++++++++++++++++++++++++++++++ employee/Makefile.morello-purecap | 7 +++++ employee/Makefile.riscv64-purecap | 7 +++++ employee/README.md | 42 ++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 27 deletions(-) delete mode 100644 employee/Makefile create mode 100644 employee/Makefile.common create mode 100644 employee/Makefile.morello-purecap create mode 100644 employee/Makefile.riscv64-purecap create mode 100644 employee/README.md diff --git a/employee/Makefile b/employee/Makefile deleted file mode 100644 index 4431570..0000000 --- a/employee/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -CC=$(HOME)/cheri/output/sdk/bin/clang -CFORMAT=$(HOME)/cheri/output/sdk/bin/clang-format -CFLAGS=-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg -SSHPORT=10021 -export - -cfiles := full_privileges.c read_only.c sealed.c -employee := $(patsubst %.c,bin/%,$(cfiles)) - -.PHONY: all run clean - -all: $(employee) - -bin/%: %.c - @mkdir -p $(@D) - $(CC) $(CFLAGS) $< -o $@ - -.SECONDEXPANSION: -run-%: bin/% $$(wildcard %.c) - scp -P $(SSHPORT) $(word 2,$^) bin/$(` (from a directory like `.../cheri-examples/` looks like +this: + +``` +$ make -f Makefile. +``` + +## Prerequisites + +- An SDK for your ``, like those built by [`cheribuild`][cheribuild]. + By default, the makefiles will search `~/cheri/` for an appropriate SDK, but + this can be overridden by defining `CHERIBASE`. Alternatively, a direct path + to the SDK can be given in `SDKBASE`. +- For `run-` targets, a machine or model that can run the result, + reachable by SSH. + +[cheribuild]: https://github.com/CTSRD-CHERI/cheribuild + +## Usage + +Available targets are: + +- `all`, the default, which builds all binaries, +- `bin/`, to build a specific binary, +- `run-`, to `scp` a binary and run it, +- `clang-format`, to format all example code, +- `clean`, to remove generated files, for this `` only. + +All build output is stored in `bin/`. + +Note that the `run-` targets require `SSHPORT` to be defined. `RUNUSER` +and `RUNHOST` can also be specified if required, but default to `root` and +`localhost`, respectively, since this is convenient for CheriBSD models. + +For example (in `.../cheri-examples/employee`): + +``` +$ SSH_PORT=12345 make -f Makefile.morello-purecap run-sealed +``` From d9f2e37c4958f015cd60f0d80549741d5aa4c995 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Thu, 26 Aug 2021 20:19:14 +0100 Subject: [PATCH 47/98] Fix the 'seal' example for all platforms. Previously, this example only worked for CHERI-MIPS, but CheriBSD now has a more portable way to retrieve a sealing capability, so we can use that to support all CHERI platforms. This also fixes a misuse of `cheri_seal` and removes some superfluous data structures. --- employee/sealed.c | 2 +- seal.c | 63 +++++++++++++---------------------------------- 2 files changed, 18 insertions(+), 47 deletions(-) diff --git a/employee/sealed.c b/employee/sealed.c index 8c5f650..3b18c6f 100644 --- a/employee/sealed.c +++ b/employee/sealed.c @@ -19,7 +19,7 @@ int main() size_t sealcap_size = sizeof(sealcap); if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) { - error("Fatal error. Cannot get `securiy.cheri.sealcap`."); + error("Fatal error. Cannot get `security.cheri.sealcap`."); exit(1); } assert(cheri_perms_get(sealcap) & CHERI_PERM_SEAL); diff --git a/seal.c b/seal.c index 47b7eab..c23eae1 100644 --- a/seal.c +++ b/seal.c @@ -1,61 +1,32 @@ -/*** - * This is mostly an experiment and it doens't work - * The CHERI_GET_SEALCAP syscall is mips only - ***/ - #include "include/common.h" -#include -#include #include #include #include #include #include - -typedef struct cap -{ - uint32_t perms : 16; - uint32_t padding : 4; - uint32_t otype : 18; - uint32_t internal_e : 1; - uint32_t T : 11; - uint32_t B : 14; - uint64_t address : 64; -} cap_t; - -typedef union -{ - void *ptr; - cap_t str; - uint64_t arr[2]; -} cheri_pointer; +#include int main() { - void *pcc = cheri_pcc_get(); - pp_cap(pcc); + void *sealcap; + size_t sealcap_size = sizeof(sealcap); + if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) + { + error("Fatal error. Cannot get `security.cheri.sealcap`."); + exit(1); + } - void *searling_root; - if (sysarch(CHERI_GET_SEALCAP, &searling_root) < 0) - searling_root = NULL; + printf("---- sealcap ----\n"); + pp_cap(sealcap); - cheri_pointer data; - void *first = malloc(64); - void *other = malloc(64); - data.ptr = first; - data.ptr = cheri_tag_clear(data.ptr); - data.arr[0] = 0; - data.arr[1] = 0; + void *buffer = malloc(64); + printf("---- buffer (before sealing) ----\n"); + pp_cap(buffer); - first = cheri_seal(first, other); - pp_cap(first); + void *sealed = cheri_seal(buffer, sealcap); + printf("---- sealed ----\n"); + pp_cap(sealed); + free(buffer); return 0; - for (uint32_t i = 0; i < 64; i++) - { - data.arr[0] |= (1 << i); - printf("i: %d ", i); - pp_cap(data.ptr); - data.arr[0] = 0; - } } From 4d47c64f5018030740be1799732c58cb3dce2de0 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Thu, 2 Sep 2021 19:53:28 +0100 Subject: [PATCH 48/98] Fix 'shared_objects', by removing 'cheric.h'. The old 'cheric.h' is incompatible with the preferred 'cheriintrin.h', which is now used in our 'common.h'. This fix updates the shared_objects examples to use the new API. --- shared_objects/include/find_sentries.h | 15 +++++++-------- shared_objects/main.c | 6 +----- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index 70f79b3..9278bc9 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -1,11 +1,10 @@ -#include #include #include "../../include/common.h" bool is_pointer(void *ptr) { - if (cheri_gettag(ptr)) + if (cheri_tag_get(ptr)) { return true; } @@ -15,21 +14,21 @@ bool is_pointer(void *ptr) bool scan_range(void *ptr, void *exact) { bool found = false; - for (void *iter = cheri_setoffset(ptr, 0); cheri_getoffset(iter) < cheri_getlength(iter); - iter = cheri_incoffset(iter, 16)) + for (void *iter = cheri_offset_set(ptr, 0); cheri_offset_get(iter) < cheri_length_get(iter); + iter = cheri_offset_set(iter, cheri_offset_get(iter) + 16)) { void *current = *(void **) iter; if (is_pointer(current)) { - if (cheri_getsealed(current)) + if (cheri_is_sealed(current)) { - if (cheri_getaddress(current) == cheri_getaddress(exact)) + if (cheri_address_get(current) == cheri_address_get(exact)) { found = true; printf("[Exact match] "); } - else if (cheri_gettop(current) == cheri_gettop(exact) && - cheri_getbase(current) == cheri_getbase(exact)) + else if (cheri_length_get(current) == cheri_length_get(exact) && + cheri_base_get(current) == cheri_base_get(exact)) { printf("[Range match] "); } diff --git a/shared_objects/main.c b/shared_objects/main.c index e17f03a..527ad05 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -3,7 +3,6 @@ */ #include -#include #include #include #include @@ -17,9 +16,6 @@ void static_variables() { increment(); -#include -#include -#include increment(); increment(); @@ -50,7 +46,7 @@ void unexported_functions() printf("test: %p\n", function_to_search_for); printf(" ------- scanning pcc for sealed pointers ------- \n"); - bool found = scan_range(cheri_getpcc(), function_to_search_for); + bool found = scan_range(cheri_pcc_get(), function_to_search_for); assert(found == false); } From 3f258beda38512dc575a612e4b2709926f4d4987 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Thu, 26 Aug 2021 20:39:02 +0100 Subject: [PATCH 49/98] Unify and document example makefiles. This simplifies the build process for each group of examples, giving a consistent set of commands for each. In addition, the makefiles have been split up to minimise duplication of configuration between examples, platforms, etc. This reduces duplication, whilst remaining flexible enough to handle those examples that need more control over the build process. All examples are covered except for 'example_allocators', which will be addressed later. --- Makefile.morello | 33 ------- Makefile.morello-purecap | 6 ++ Makefile.riscv64 | 34 ------- Makefile.riscv64-purecap | 6 ++ README.md | 89 ++++++++++++------- .../Makefile.common => build/Makefile.simple | 46 +++++----- build/Makefile.vars.common | 23 +++++ build/Makefile.vars.morello-purecap | 10 +++ build/Makefile.vars.riscv64-purecap | 11 +++ employee/Makefile.morello-purecap | 11 ++- employee/Makefile.riscv64-purecap | 11 ++- employee/README.md | 40 +-------- employee/build/Makefile.employee | 11 +++ shared_objects/Makefile | 19 ---- shared_objects/Makefile.morello-purecap | 6 ++ shared_objects/Makefile.riscv64-purecap | 6 ++ shared_objects/README.md | 14 +++ shared_objects/build/Makefile.shared_objects | 51 +++++++++++ timsort/Makefile | 27 ------ timsort/Makefile.morello-purecap | 10 +++ timsort/Makefile.riscv64-purecap | 10 +++ timsort/README.md | 16 ++++ timsort/build/Makefile.timsort | 11 +++ 23 files changed, 285 insertions(+), 216 deletions(-) delete mode 100644 Makefile.morello create mode 100644 Makefile.morello-purecap delete mode 100644 Makefile.riscv64 create mode 100644 Makefile.riscv64-purecap rename employee/Makefile.common => build/Makefile.simple (50%) create mode 100644 build/Makefile.vars.common create mode 100644 build/Makefile.vars.morello-purecap create mode 100644 build/Makefile.vars.riscv64-purecap create mode 100644 employee/build/Makefile.employee delete mode 100644 shared_objects/Makefile create mode 100644 shared_objects/Makefile.morello-purecap create mode 100644 shared_objects/Makefile.riscv64-purecap create mode 100644 shared_objects/README.md create mode 100644 shared_objects/build/Makefile.shared_objects delete mode 100644 timsort/Makefile create mode 100644 timsort/Makefile.morello-purecap create mode 100644 timsort/Makefile.riscv64-purecap create mode 100644 timsort/README.md create mode 100644 timsort/build/Makefile.timsort diff --git a/Makefile.morello b/Makefile.morello deleted file mode 100644 index 9f3c1cd..0000000 --- a/Makefile.morello +++ /dev/null @@ -1,33 +0,0 @@ -CC=$(HOME)/cheri/output/morello-sdk/bin/clang -CFORMAT=$(HOME)/cheri/output/morello-sdk/bin/clang-format -CXX=$(HOME)/cheri/output/morello-sdk/bin/clang++ -CFLAGS=-fuse-ld=lld --config cheribsd-morello-purecap.cfg -SSHPORT?=10085 -export - -cfiles := $(wildcard *.c) -cppfiles := $(wildcard *.cpp) -examples := $(patsubst %.c,bin/%,$(cfiles)) $(patsubst %.cpp,bin/%,$(cfiles)) - -.PHONY: all run clean - -all: $(examples) - -bin/%: %.c - @mkdir -p $(@D) - $(CC) $(CFLAGS) $< -o $@ - -bin/%: %.cpp - @mkdir -p $(@D) - $(CXX) $(CFLAGS) $< -o $@ - -.SECONDEXPANSION: -run-%: bin/% $$(wildcard %.c) $$(wildcard %.cpp) - scp -P $(SSHPORT) $(word 2,$^) bin/$() outside the source directory (\). -2. Generate the build files using either **Unix Makefiles** (*default*) or **Ninja** (specfied - with **-G**). Currently only the *RISC-V* and *Morello* cheri-purecap toolchain files have - been created. - 1. If executing from inside the build directory : - - `cmake -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake ` - - 2. If executing from outside the build directory : - - `cmake -B -S -DCMAKE_TOOLCHAIN_FILE=riscv64-purecap.cmake` - -3. Build all the examples - 1. If executing from inside the build directory, `make all` *OR* `ninja all` - 2. If executing from outside the build directory, `cmake --build ` - -#### CMake options: -* **-DCMAKE_TOOLCHAIN_FILE**: Use `-DCMAKE_TOOLCHAIN_FILE=` to select - architecture to compile binary for. Only *riscv64-purecap.cmake* and *morello-purecap.cmake* - are currently supported. -* **-DSDK**: Use `-DSDK=` to point to the *CHERI* SDK directory. - Default paths: - 1. *RISC-V*: ${HOME}/cheri/output/sdk - 2. *Morello*: ${HOME}/cheri/output/morello-sdk -* **-G**: Choose preferred build system - - Use `-G "Unix Makefiles"` to build using *makefiles* (*default*). - - Use `-G Ninja` to build using *ninja*. + +Examples of CHERI fundamental operations, interesting corner cases, and simple +demonstrative applications. + +A makefile is provided for each supported platform, and a basic build command +looks like this: + +``` +$ make -f Makefile. +``` + +For most examples, each `.c` file compiles to a single, self-contained example, +and compiles to a binary with the same name (without the `.c`). + +Note that this only builds examples at the top level. Some examples are grouped +into their own directories, with their own makefiles. However, the available +make targets are consistent, where possible. Refer to their respective README +files for details. + +## Prerequisites + +- An SDK for your ``, like those built by [`cheribuild`][cheribuild]. + By default, the makefiles will search `~/cheri/` for an appropriate SDK, but + this can be overridden by defining `CHERIBASE`. Alternatively, a direct path + to the SDK can be given in `SDKBASE`. +- For `run-` targets, a machine or model that can run the result, + reachable by SSH. + +[cheribuild]: https://github.com/CTSRD-CHERI/cheribuild + +## Usage + +Targets available for all example directories are: + +- `all`, the default, which builds all binaries, +- `clang-format`, to format all example code, +- `clean`, to remove generated files, for this `` only. + +The top level examples, and most examples in sub-directories, also allow +individual examples to be built or run: + +- `bin/`, to build a specific binary, +- `run-`, to `scp` a binary and run it. + +Refer to their respective README files for details. + +All build output is stored in `bin/`. + +Note that the `run-` targets require `SSHPORT` to be defined. `RUNUSER` +and `RUNHOST` can also be specified if required, but default to `root` and +`localhost`, respectively, since this is convenient for CheriBSD models. + +For example: + +``` +$ SSH_PORT=12345 make -f Makefile.morello-purecap run-seal +``` diff --git a/employee/Makefile.common b/build/Makefile.simple similarity index 50% rename from employee/Makefile.common rename to build/Makefile.simple index 1345ba9..35dd9b0 100644 --- a/employee/Makefile.common +++ b/build/Makefile.simple @@ -1,24 +1,16 @@ -# Set by platform-specific makefiles. For example: ~/cheri/output/sdk -ifndef SDKBASE -$(error SDKBASE is not set) -endif - -# For example: morello-purecap -ifndef PLATFORM -$(error PLATFORM is not set) -endif +# Copyright (c) 2020-2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 -CC := $(SDKBASE)/bin/clang -CFORMAT := $(SDKBASE)/bin/clang-format +# Common Makefile for simple examples, which compile each %.c file to a single +# bin//% executable. +# This should not be invoked directly. -RUNUSER ?= root -RUNHOST ?= localhost -RUNDIR ?= -CFLAGS ?= +ifndef BINDIR +$(error build/Makefile.vars should set BINDIR) +endif -BINDIR := bin/$(PLATFORM) -CFILES := $(wildcard *.c) -HFILES := $(wildcard include/*.h) +CFILES ?= $(wildcard *.c) +HFILES ?= $(wildcard include/*.h) BINS := $(patsubst %.c,$(BINDIR)/%,$(CFILES)) RUNTGTS := $(patsubst $(BINDIR)/%,run-%,$(BINS)) @@ -26,15 +18,17 @@ RUNTGTS := $(patsubst $(BINDIR)/%,run-%,$(BINS)) all: $(BINS) +clang-format: + $(CFORMAT) -i $(CFILES) $(HFILES) + # Remove outputs, but only remove the top-level 'bin/' if it's empty. This # mitigates the risk of accidental damage, e.g. if run from a user's home when # they have a ~/bin. clean: - rm -rf $(BINDIR) - rmdir bin --ignore-fail-on-non-empty - -clang-format: - $(CFORMAT) -i $(CFILES) $(HFILES) + if [ -d bin ]; then \ + rm -rf $(BINDIR); \ + rmdir bin --ignore-fail-on-non-empty; \ + fi $(BINDIR)/%: %.c $(HFILES) @mkdir -p $(BINDIR) @@ -42,8 +36,14 @@ $(BINDIR)/%: %.c $(HFILES) $(RUNTGTS): run-%: $(BINDIR)/% ifdef SSHPORT +ifdef RUNDIR + ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' scp -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t 'cd $(RUNDIR) && ./$(` (from a directory like `.../cheri-examples/` looks like -this: +These examples are not automatically built by the top-level makefiles, but are +built in a similar manner: ``` $ make -f Makefile. ``` -## Prerequisites - -- An SDK for your ``, like those built by [`cheribuild`][cheribuild]. - By default, the makefiles will search `~/cheri/` for an appropriate SDK, but - this can be overridden by defining `CHERIBASE`. Alternatively, a direct path - to the SDK can be given in `SDKBASE`. -- For `run-` targets, a machine or model that can run the result, - reachable by SSH. - -[cheribuild]: https://github.com/CTSRD-CHERI/cheribuild - -## Usage - -Available targets are: - -- `all`, the default, which builds all binaries, -- `bin/`, to build a specific binary, -- `run-`, to `scp` a binary and run it, -- `clang-format`, to format all example code, -- `clean`, to remove generated files, for this `` only. - -All build output is stored in `bin/`. - -Note that the `run-` targets require `SSHPORT` to be defined. `RUNUSER` -and `RUNHOST` can also be specified if required, but default to `root` and -`localhost`, respectively, since this is convenient for CheriBSD models. - -For example (in `.../cheri-examples/employee`): - -``` -$ SSH_PORT=12345 make -f Makefile.morello-purecap run-sealed -``` +Refer to the top-level [README][../README.md] for usage details. diff --git a/employee/build/Makefile.employee b/employee/build/Makefile.employee new file mode 100644 index 0000000..920e49e --- /dev/null +++ b/employee/build/Makefile.employee @@ -0,0 +1,11 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the employee examples. +# This should not be invoked directly. + +RUNDIR := $(RUNDIR)/employee +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +include ../build/Makefile.simple diff --git a/shared_objects/Makefile b/shared_objects/Makefile deleted file mode 100644 index 5dfb054..0000000 --- a/shared_objects/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -CC=$(HOME)/cheri/output/sdk/bin/clang -CFLAGS=-g -fuse-ld=lld --config cheribsd-riscv64-purecap.cfg -SSHPORT=10007 - -main: main.c - $(CC) $(CFLAGS) -O2 -fpie -L. -Wl,-rpath,. -ldl -lstatic_variable -lunexported_function -lcompartment_per_object -g $< -o main - -libstatic_variable.so: static_variable.c include/static_variable.h - $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libstatic_variable.so - -libunexported_function.so: unexported_function.c include/unexported_function.h - $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libunexported_function.so - -libcompartment_per_object.so: compartment_per_object.c include/compartment_per_object.h - $(CC) $(CFLAGS) -O2 -g -fPIC -shared $< -o libcompartment_per_object.so - -run: - scp -r -P $(SSHPORT) * root@127.0.0.1:/root/demo/ - ssh -p $(SSHPORT) root@127.0.0.1 -t 'cd /root/demo; /root/demo/main' diff --git a/shared_objects/Makefile.morello-purecap b/shared_objects/Makefile.morello-purecap new file mode 100644 index 0000000..d5222e7 --- /dev/null +++ b/shared_objects/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.morello-purecap +include ../build/Makefile.vars.common +include build/Makefile.shared_objects diff --git a/shared_objects/Makefile.riscv64-purecap b/shared_objects/Makefile.riscv64-purecap new file mode 100644 index 0000000..70cec9c --- /dev/null +++ b/shared_objects/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.riscv64-purecap +include ../build/Makefile.vars.common +include build/Makefile.shared_objects diff --git a/shared_objects/README.md b/shared_objects/README.md new file mode 100644 index 0000000..84c062b --- /dev/null +++ b/shared_objects/README.md @@ -0,0 +1,14 @@ +# "shared\_objects" examples + +These examples are not automatically built by the top-level makefiles, but are +built in a similar manner: + +``` +$ make -f Makefile. +``` + +Unlike most other examples, these compile to a single executable (with +accompanying shared objects). There is only a single `run-shared_objects` +target, and the example is selected interactively. + +Otherwise, refer to the top-level [README][../README.md] for usage details. diff --git a/shared_objects/build/Makefile.shared_objects b/shared_objects/build/Makefile.shared_objects new file mode 100644 index 0000000..af692fe --- /dev/null +++ b/shared_objects/build/Makefile.shared_objects @@ -0,0 +1,51 @@ +# Copyright (c) 2020-2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the shared_object example. +# This should not be invoked directly. + +ifndef BINDIR +$(error build/Makefile.vars should set BINDIR) +endif + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/shared_objects +else +RUNDIR := shared_objects +endif + +CFILES := $(wildcard *.c) +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +LIBNAMES := static_variable unexported_function compartment_per_object +SOFILES := $(patsubst %,$(BINDIR)/lib%.so,$(LIBNAMES)) + +.PHONY: all clang-format clean run-shared_objects + +all: $(BINDIR)/main + +clang-format: + $(CFORMAT) -i $(CFILES) $(HFILES) + +clean: + if [ -d bin ]; then \ + rm -rf $(BINDIR); \ + rmdir bin --ignore-fail-on-non-empty; \ + fi + +$(BINDIR)/main: main.c $(HFILES) $(SOFILES) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) -fpie -L $(BINDIR) -Wl,-rpath,. -ldl $(patsubst %,-l%,$(LIBNAMES)) $< -o $(BINDIR)/main + +$(BINDIR)/lib%.so: %.c $(HFILES) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) -fPIC -shared $< -o $@ + +run-shared_objects: $(BINDIR)/main $(SOFILES) +ifdef SSHPORT + ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' + scp -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t 'cd $(RUNDIR) && ./$( +``` + +Refer to the top-level [README][../README.md] for usage details. + +For example: + +``` +$ SSHPORT=1234 make -f Makefile.morello-purecap run-timsort +``` diff --git a/timsort/build/Makefile.timsort b/timsort/build/Makefile.timsort new file mode 100644 index 0000000..971d19c --- /dev/null +++ b/timsort/build/Makefile.timsort @@ -0,0 +1,11 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the timsort example. +# This should not be invoked directly. + +RUNDIR := $(RUNDIR)/timsort +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +include ../build/Makefile.simple From ef5f218b0ae0a333dce0860551dd112fff3f1ece Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Fri, 17 Sep 2021 12:10:08 +0100 Subject: [PATCH 50/98] Update buildbot scripts to use the new makefiles. --- .buildbot.sh | 73 ++++++++-------- build/Makefile.simple | 10 +-- shared_objects/build/Makefile.shared_objects | 6 +- tests/run_cheri_examples.py | 34 ++++++-- tests/run_tests.sh | 92 ++++++++++---------- 5 files changed, 117 insertions(+), 98 deletions(-) diff --git a/.buildbot.sh b/.buildbot.sh index e7afaf0..43e1ea4 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -1,23 +1,45 @@ #!/bin/bash # vim: expandtab sts=0 sw=4 smarttab -set -e +# +# Copyright (c) 2020-2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 -find . -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" -o -iname "*.hpp" | xargs ~/cheri/output/sdk/bin/clang-format --dry-run -Werror +set -e -mkdir -p ./bin -for example in *.c; do - if [ "${example}" == "seal.c" ]; then - continue - fi - make -f Makefile.riscv64 bin/"${example%%.*}" +export CHERIBASE=~/cheri +CHERIBUILD=~/build + +echo "Checking clang-format..." +# Note that `sdk` and `morello-sdk` contain different versions of clang-format, +# so we have to pick one and use it consistently. +CLANG_FORMAT="$CHERIBASE"/output/sdk/bin/clang-format +find . -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" -o -iname "*.hpp" | xargs "$CLANG_FORMAT" --dry-run -Werror + +echo "Checking that all examples build for all platforms..." +PLATFORMS='riscv64-purecap morello-purecap' +# TODO: Add "example_allocators". +for dir in . employee shared_objects timsort; do + pushd "$dir" + for platform in $PLATFORMS; do + make -f Makefile.$platform clean + if [ -d bin/$platform ]; then + echo "Error: 'make clean' did not clean the output directory." + false + fi + make -f Makefile.$platform all + if [ ! -d bin/$platform ]; then + echo "Error: 'make all' did not produce the expected output." + false + fi + done + popd done -# Copy the test-script to `cheribuild` -cp ./tests/run_cheri_examples.py $HOME/build/test-scripts/. +export SSHPORT=10021 +export PYTHONPATH="$CHERIBUILD"/test-scripts -# Run the tests on the `qemu-system riscv64` +echo "Running tests for 'riscv64' using QEMU." args=( - # Architecture --architecture riscv64 # Qemu System to use --qemu-cmd $HOME/cheri/output/sdk/bin/qemu-system-riscv64cheri @@ -25,33 +47,12 @@ args=( --kernel $HOME/cheri/output/rootfs-riscv64-purecap/boot/kernel/kernel # Bios (to avoid the default one) --bios bbl-riscv64cheri-virt-fw_jump.bin - # Disk Image --disk-image $HOME/cheri/output/cheribsd-riscv64-purecap.img # Required build-dir in CheriBSD --build-dir . - --ssh-port 10021 + --ssh-port $SSHPORT --ssh-key $HOME/.ssh/id_ed25519.pub ) +BUILDBOT_PLATFORM=riscv64-purecap python3 tests/run_cheri_examples.py "${args[@]}" -python3 $HOME/build/test-scripts/run_cheri_examples.py "${args[@]}" -rm -rfv bin/* - -BUILD_DIR="bin" - -# arg-1 : Source directory -# arg-2 : cmake toolchain file -function build_cheri_examples() -{ - ARCH=$(echo ${2} | cut -d '.' -f 1) - BUILD_DIR=${1}/build/${ARCH} - - mkdir -p ${BUILD_DIR} - cmake -B ${BUILD_DIR} -S ${1} -DCMAKE_TOOLCHAIN_FILE=${2} - cmake --build ${BUILD_DIR} -} - -build_cheri_examples $(pwd) riscv64-purecap.cmake - -# FIXME: EXCLUDING MORELLO to close PR#26 -# build_cheri_examples $(pwd) morello-purecap.cmake -rm -rfv $(pwd)/build +# TODO: Run tests for Morello too. diff --git a/build/Makefile.simple b/build/Makefile.simple index 35dd9b0..854750e 100644 --- a/build/Makefile.simple +++ b/build/Makefile.simple @@ -37,12 +37,12 @@ $(BINDIR)/%: %.c $(HFILES) $(RUNTGTS): run-%: $(BINDIR)/% ifdef SSHPORT ifdef RUNDIR - ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' - scp -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) - ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t 'cd $(RUNDIR) && ./$(/dev/null) ]; then - cp $EMPLOYEE_DIR/read_only $BUILD_DIR/read_only -else - echo "WARNING: you have not built the employee example yet!" - echo "SKIPPING employee/read_only..." +ERROR=false +if [ -z "$SSHPORT" ]; then + echo "SSHPORT is empty or not set" >&2 + ERROR=true +fi +if [ -z "$BUILDBOT_PLATFORM" ]; then + echo "BUILDBOT_PLATFORM is empty or not set" >&2 + ERROR=true fi +$ERROR && exit 1 + +export SSH_OPTIONS='-o "StrictHostKeyChecking no"' +export SCP_OPTIONS='-o "StrictHostKeyChecking no"' + +function run { + pushd "$1" + for name in "${@:2}"; do + make -f Makefile.$BUILDBOT_PLATFORM run-$name + done + popd +} + +# TODO: `mmap` also fails, but looks like it is intended to pass. +run . check_length check_mask seal setjmp stackscan -for example in ${EXAMPLES}; do - if [ ! $(find $BUILD_DIR -prune -empty 2>/dev/null) ]; then - if [ ! -z "${ARCH##*riscv*}" ] && [ "$example" = "set_bounds" ]; then - echo "Skipping '$example' because you are not on riscv64..." - continue - fi - scp -o "StrictHostKeyChecking no" -P $SSHPORT "$BUILD_DIR/$example" root@127.0.0.1:/root - exit_status=0 - RESULT={{$(ssh -o "StrictHostKeyChecking no" -p $SSHPORT -t root@127.0.0.1 "/root/$example 68")} && exit_status=1} || true - if [ "$example" = "read_only" ]; then - example=employee/$example - fi - echo -n ""$example"... " - if [ $exit_status != 0 ]; then - echo "FAILED! See below for more details." - echo $RESULT | tr -d "{}" - else - echo "ok" - fi - else - echo "Please, build first the examples before running the tests." - exit 1 - fi -done \ No newline at end of file +# TODO: 'timsort' works, but takes a very long time. Is it useful to test a +# smaller data set? From f4f02f9e295a74285fdde9831266205a681e9769 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Fri, 17 Sep 2021 15:33:03 +0100 Subject: [PATCH 51/98] Remove CMake files. --- CMakeLists.txt | 26 -------------------------- morello-purecap.cmake | 20 -------------------- riscv64-purecap.cmake | 20 -------------------- 3 files changed, 66 deletions(-) delete mode 100644 CMakeLists.txt delete mode 100644 morello-purecap.cmake delete mode 100644 riscv64-purecap.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 9fef3b4..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required (VERSION 3.13.0) - -project(CHERI_EXAMPLES - VERSION 0.1 - DESCRIPTION "Stub examples showing how to exploit capabilities" - LANGUAGES C) - -get_filename_component(COMP_NAME ${CMAKE_C_COMPILER} NAME_WE) - -add_executable(allocate allocate.c) -add_executable(check_length check_length.c) -add_executable(check_mask check_mask.c) -add_executable(function function.c) -add_executable(bounds bounds.c) -add_executable(general_bounds general_bounds.c) -add_executable(mmap mmap.c) -if( ${COMP_NAME} MATCHES "^mips*" ) - add_executable(seal seal.c) -endif() -add_executable(sentry sentry.c) -add_executable(set_bounds set_bounds.c) -add_executable(setjmp setjmp.c) -add_executable(stackscan stackscan.c) -add_executable(xor_pointers xor_pointers.c) -add_executable(bitmap_alloc_good example_allocators/bitmap_allocator/good_client.c - example_allocators/bitmap_allocator/bitmap_alloc.c) diff --git a/morello-purecap.cmake b/morello-purecap.cmake deleted file mode 100644 index b7a2ab8..0000000 --- a/morello-purecap.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) - -# Default SDK path -set(SDK "$ENV{HOME}/cheri/output/morello-sdk" CACHE PATH "path to cheri SDK") - -# Set toolchain compilers -set(CMAKE_C_COMPILER ${SDK}/bin/clang) -set(CMAKE_CXX_COMPILER ${SDK}/bin/clang++) - -# Don't run the linker on compiler check -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - -# Use only cross compiler tools for compilation and linking -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -# Set correct machine and abi flags -add_compile_options(--config cheribsd-morello-purecap.cfg) -add_link_options(-fuse-ld=lld --config cheribsd-morello-purecap.cfg) diff --git a/riscv64-purecap.cmake b/riscv64-purecap.cmake deleted file mode 100644 index 995b9a1..0000000 --- a/riscv64-purecap.cmake +++ /dev/null @@ -1,20 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) - -# Default SDK path -set(SDK "$ENV{HOME}/cheri/output/sdk" CACHE PATH "path to cheri SDK") - -# Set toolchain compilers -set(CMAKE_C_COMPILER ${SDK}/bin/clang) -set(CMAKE_CXX_COMPILER ${SDK}/bin/clang++) - -# Don't run the linker on compiler check -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - -# Use only cross compiler tools for compilation and linking -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - -# Set correct machine and abi flags -add_compile_options(--config cheribsd-riscv64-purecap.cfg) -add_link_options(-fuse-ld=lld --config cheribsd-riscv64-purecap.cfg) From 1725a4e49f690072c31d92e05906596764e5561a Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Thu, 23 Sep 2021 10:50:13 +0100 Subject: [PATCH 52/98] Typo fix. Missed by my review. --- build/Makefile.vars.morello-purecap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Makefile.vars.morello-purecap b/build/Makefile.vars.morello-purecap index 63c74dc..e723ead 100644 --- a/build/Makefile.vars.morello-purecap +++ b/build/Makefile.vars.morello-purecap @@ -1,7 +1,7 @@ # Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 -# Common configuration for 'riscv64-purecap' platforms. +# Common configuration for 'morello-purecap' platforms. # This should not be invoked directly. CHERIBASE ?= $(HOME)/cheri From 72754822adb73a623ccee44688e6de187b6d7d0f Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Fri, 24 Sep 2021 10:49:20 +0100 Subject: [PATCH 53/98] Make sure we use the system ld --- build/Makefile.vars.riscv64-purecap | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/Makefile.vars.riscv64-purecap b/build/Makefile.vars.riscv64-purecap index 3b61e10..8e3b0b4 100644 --- a/build/Makefile.vars.riscv64-purecap +++ b/build/Makefile.vars.riscv64-purecap @@ -6,6 +6,5 @@ CHERIBASE ?= $(HOME)/cheri SDKBASE ?= $(CHERIBASE)/output/sdk -# TODO: This doesn't work without lld. Why not? -CFLAGS := --config cheribsd-riscv64-purecap.cfg -fuse-ld=lld $(CFLAGS) +CFLAGS := --config cheribsd-riscv64-purecap.cfg $(CFLAGS) PLATFORM := riscv64-purecap From 3ba9258a2f51d64c5a64bf525f5def160dc7331d Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Tue, 21 Sep 2021 15:43:48 +0100 Subject: [PATCH 54/98] Some simple interactions with the Default Data Capability register Note, the following examples only work on Morello-hybrid: - basic_ddc.c: very basic compartment switching - ddc_null.c: crash when writing NULL - ddc_invalid.c: crash when writing an invalid capability --- hybrid/Makefile.common | 50 ++++++++++++++++++++++++++++++++++ hybrid/Makefile.morello-hybrid | 7 +++++ hybrid/basic_ddc.c | 31 +++++++++++++++++++++ hybrid/ddc_invalid.c | 23 ++++++++++++++++ hybrid/ddc_null.c | 23 ++++++++++++++++ hybrid/include/utils.h | 25 +++++++++++++++++ include/common.h | 2 +- 7 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 hybrid/Makefile.common create mode 100644 hybrid/Makefile.morello-hybrid create mode 100644 hybrid/basic_ddc.c create mode 100644 hybrid/ddc_invalid.c create mode 100644 hybrid/ddc_null.c create mode 100644 hybrid/include/utils.h diff --git a/hybrid/Makefile.common b/hybrid/Makefile.common new file mode 100644 index 0000000..1345ba9 --- /dev/null +++ b/hybrid/Makefile.common @@ -0,0 +1,50 @@ +# Set by platform-specific makefiles. For example: ~/cheri/output/sdk +ifndef SDKBASE +$(error SDKBASE is not set) +endif + +# For example: morello-purecap +ifndef PLATFORM +$(error PLATFORM is not set) +endif + +CC := $(SDKBASE)/bin/clang +CFORMAT := $(SDKBASE)/bin/clang-format + +RUNUSER ?= root +RUNHOST ?= localhost +RUNDIR ?= +CFLAGS ?= + +BINDIR := bin/$(PLATFORM) +CFILES := $(wildcard *.c) +HFILES := $(wildcard include/*.h) +BINS := $(patsubst %.c,$(BINDIR)/%,$(CFILES)) +RUNTGTS := $(patsubst $(BINDIR)/%,run-%,$(BINS)) + +.PHONY: all clang-format clean $(RUNTGTS) + +all: $(BINS) + +# Remove outputs, but only remove the top-level 'bin/' if it's empty. This +# mitigates the risk of accidental damage, e.g. if run from a user's home when +# they have a ~/bin. +clean: + rm -rf $(BINDIR) + rmdir bin --ignore-fail-on-non-empty + +clang-format: + $(CFORMAT) -i $(CFILES) $(HFILES) + +$(BINDIR)/%: %.c $(HFILES) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) $< -o $@ + +$(RUNTGTS): run-%: $(BINDIR)/% +ifdef SSHPORT + scp -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t ./$( +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +int main() +{ + uint16_t *some_int_ptr = (uint16_t *) malloc(sizeof(uint16_t)); + *some_int_ptr = 200; + assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); + uint32_t *some_other_int_ptr = (uint32_t *) malloc(sizeof(uint32_t)); + write_ddc((void *__capability) some_other_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) == some_other_int_ptr); + // Note: this program is very simple and writing to the DDC in this fashion + // would cause a crash if the program were to execute much further. + write_ddc((void *__capability) some_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) != some_other_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) == some_int_ptr); + return 0; +} \ No newline at end of file diff --git a/hybrid/ddc_invalid.c b/hybrid/ddc_invalid.c new file mode 100644 index 0000000..8dc6d13 --- /dev/null +++ b/hybrid/ddc_invalid.c @@ -0,0 +1,23 @@ +/*** + * This program causes an "In-address space security Exception" by clearing the + * tag of the capability in the DDC + ***/ + +#include "../include/common.h" +#include "include/utils.h" +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +int main() +{ + // Before clearing the tag we ensure we have a valid one + assert(cheri_tag_get(cheri_ddc_get())); + // Clearing the tag will cause the exception + write_ddc(cheri_tag_clear(cheri_ddc_get())); + return 0; +} \ No newline at end of file diff --git a/hybrid/ddc_null.c b/hybrid/ddc_null.c new file mode 100644 index 0000000..db0a156 --- /dev/null +++ b/hybrid/ddc_null.c @@ -0,0 +1,23 @@ +/*** + * This program shows that putting "NULL" into the DDC causes an + * "In-address space security exception" + ***/ + +#include "../include/common.h" +#include "include/utils.h" +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +int main() +{ + // Ensure the DDC contains a valid capability + assert(cheri_tag_get(cheri_ddc_get())); + // Putting a NULL will cause the exception + write_ddc(NULL); + return 0; +} \ No newline at end of file diff --git a/hybrid/include/utils.h b/hybrid/include/utils.h new file mode 100644 index 0000000..784cb25 --- /dev/null +++ b/hybrid/include/utils.h @@ -0,0 +1,25 @@ +/*** + * Inline Assembly utility functions to read/write to/from the Default Data + * Capability Register (DDC) + ***/ + +#if !defined(__aarch64__) +#error "This utility functions are Morello only" +#endif + +void write_ddc(void *__capability cap); +void *__capability read_ddc(); + +// Write a capability to the DDC +inline void write_ddc(void *__capability cap) +{ + asm("MSR DDC, %[cap]\n\t" : : [cap] "C"(cap) : "memory"); +} + +// Read a capability from the DDC +inline void *__capability read_ddc() +{ + void *__capability ddc_cap; + asm("MRS %[ddc_cap], DDC\n\t" : [ddc_cap] "=C"(ddc_cap) : :); + return ddc_cap; +} \ No newline at end of file diff --git a/include/common.h b/include/common.h index 4a2b2af..e11f488 100644 --- a/include/common.h +++ b/include/common.h @@ -7,7 +7,7 @@ * Print information about a capability */ -void pp_cap(void *ptr) +void pp_cap(void *__capability ptr) { uint64_t length = cheri_length_get(ptr); uint64_t address = cheri_address_get(ptr); From 9024ca0b6022eb3f345cf8c77efd798c5e501583 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Wed, 29 Sep 2021 16:12:17 +0100 Subject: [PATCH 55/98] Add buildbot build check for the hybrid examples --- .buildbot.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.buildbot.sh b/.buildbot.sh index 43e1ea4..b287704 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -15,7 +15,7 @@ echo "Checking clang-format..." CLANG_FORMAT="$CHERIBASE"/output/sdk/bin/clang-format find . -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" -o -iname "*.hpp" | xargs "$CLANG_FORMAT" --dry-run -Werror -echo "Checking that all examples build for all platforms..." +echo "Checking that all the purecap examples build for all platforms..." PLATFORMS='riscv64-purecap morello-purecap' # TODO: Add "example_allocators". for dir in . employee shared_objects timsort; do @@ -35,6 +35,15 @@ for dir in . employee shared_objects timsort; do popd done +echo "Checking that all the hybrid examples build on Morello..." +platform='morello-hybrid' +for dir in hybrid; do + pushd "$dir" + make -f Makefile.$platform clean + make -f Makefile.$platform all + popd +done + export SSHPORT=10021 export PYTHONPATH="$CHERIBUILD"/test-scripts From 8daff19d67b1a44920abebd9a352ed2b0d018270 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Wed, 29 Sep 2021 16:34:23 +0100 Subject: [PATCH 56/98] Remove redundant checks We have "set -e" that does an excellent job. --- .buildbot.sh | 8 ----- build/Makefile.vars.morello-hybrid | 10 ++++++ hybrid/Makefile.common | 50 ------------------------------ hybrid/Makefile.morello-hybrid | 11 +++---- 4 files changed, 15 insertions(+), 64 deletions(-) create mode 100644 build/Makefile.vars.morello-hybrid delete mode 100644 hybrid/Makefile.common diff --git a/.buildbot.sh b/.buildbot.sh index b287704..f691849 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -22,15 +22,7 @@ for dir in . employee shared_objects timsort; do pushd "$dir" for platform in $PLATFORMS; do make -f Makefile.$platform clean - if [ -d bin/$platform ]; then - echo "Error: 'make clean' did not clean the output directory." - false - fi make -f Makefile.$platform all - if [ ! -d bin/$platform ]; then - echo "Error: 'make all' did not produce the expected output." - false - fi done popd done diff --git a/build/Makefile.vars.morello-hybrid b/build/Makefile.vars.morello-hybrid new file mode 100644 index 0000000..99ec093 --- /dev/null +++ b/build/Makefile.vars.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common configuration for 'morello-purecap' platforms. +# This should not be invoked directly. + +CHERIBASE ?= $(HOME)/cheri +SDKBASE ?= $(CHERIBASE)/output/morello-sdk +CFLAGS := --config cheribsd-morello-hybrid.cfg $(CFLAGS) +PLATFORM := morello-hybrid diff --git a/hybrid/Makefile.common b/hybrid/Makefile.common deleted file mode 100644 index 1345ba9..0000000 --- a/hybrid/Makefile.common +++ /dev/null @@ -1,50 +0,0 @@ -# Set by platform-specific makefiles. For example: ~/cheri/output/sdk -ifndef SDKBASE -$(error SDKBASE is not set) -endif - -# For example: morello-purecap -ifndef PLATFORM -$(error PLATFORM is not set) -endif - -CC := $(SDKBASE)/bin/clang -CFORMAT := $(SDKBASE)/bin/clang-format - -RUNUSER ?= root -RUNHOST ?= localhost -RUNDIR ?= -CFLAGS ?= - -BINDIR := bin/$(PLATFORM) -CFILES := $(wildcard *.c) -HFILES := $(wildcard include/*.h) -BINS := $(patsubst %.c,$(BINDIR)/%,$(CFILES)) -RUNTGTS := $(patsubst $(BINDIR)/%,run-%,$(BINS)) - -.PHONY: all clang-format clean $(RUNTGTS) - -all: $(BINS) - -# Remove outputs, but only remove the top-level 'bin/' if it's empty. This -# mitigates the risk of accidental damage, e.g. if run from a user's home when -# they have a ~/bin. -clean: - rm -rf $(BINDIR) - rmdir bin --ignore-fail-on-non-empty - -clang-format: - $(CFORMAT) -i $(CFILES) $(HFILES) - -$(BINDIR)/%: %.c $(HFILES) - @mkdir -p $(BINDIR) - $(CC) $(CFLAGS) $< -o $@ - -$(RUNTGTS): run-%: $(BINDIR)/% -ifdef SSHPORT - scp -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) - ssh -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t ./$( Date: Fri, 1 Oct 2021 12:35:35 +0100 Subject: [PATCH 57/98] Intermediate level of compartment creation via an assembly trampoline Given some allocated memory, e.g. 5000 B, we allow compartment_simple_fun access to the first 2000 Bytes and then we verify such a function cannot go over the bounds we set or that it can effectively access the data in its compartment. --- hybrid/ddc_compartment_switching.c | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 hybrid/ddc_compartment_switching.c diff --git a/hybrid/ddc_compartment_switching.c b/hybrid/ddc_compartment_switching.c new file mode 100644 index 0000000..f9a51dd --- /dev/null +++ b/hybrid/ddc_compartment_switching.c @@ -0,0 +1,36 @@ +/*** + * TODO: something like intermediate level of compartment switching or ... + ***/ + +#include "../include/common.h" +#include "include/utils.h" +#include +#include +#include + + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +// Simple function with some data which will be part of the compartment +int compartment_simple_fun(int a, int b); +// Temp hack to "design" an interface +int switch_compartment(int a, int b, void * stack, size_t size); +// TODO: this will call assembly code +// extern int switch_compartment(int a, int b, void * stack, size_t size); + +int main(){ + void * simple_block = malloc(5000); + size_t compartment_size = 2000; + switch_compartment(5, 3, simple_block, compartment_size); + return 0; +} + +int switch_compartment(int a, int b, void * stack, size_t size){ + return compartment_simple_fun(a, b); // FIXME: this is a temp hack +} + +int compartment_simple_fun(int a, int b) { + return a + b; +} \ No newline at end of file From 00c71504206c2408eb32ee52bbb08533992c7960 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Fri, 1 Oct 2021 20:06:39 +0100 Subject: [PATCH 58/98] Basic assembler DDC switcher. --- build/Makefile.simple | 5 +- .../Makefile.morello-hybrid | 9 ++ .../ddc_compartment_switching.c | 15 +- hybrid/ddc_compartment_switching/shared.s | 141 ++++++++++++++++++ 4 files changed, 157 insertions(+), 13 deletions(-) create mode 100644 hybrid/ddc_compartment_switching/Makefile.morello-hybrid rename hybrid/{ => ddc_compartment_switching}/ddc_compartment_switching.c (60%) create mode 100644 hybrid/ddc_compartment_switching/shared.s diff --git a/build/Makefile.simple b/build/Makefile.simple index 854750e..9fc3a37 100644 --- a/build/Makefile.simple +++ b/build/Makefile.simple @@ -9,6 +9,7 @@ ifndef BINDIR $(error build/Makefile.vars should set BINDIR) endif +SHARED_SOURCES ?= CFILES ?= $(wildcard *.c) HFILES ?= $(wildcard include/*.h) BINS := $(patsubst %.c,$(BINDIR)/%,$(CFILES)) @@ -30,9 +31,9 @@ clean: rmdir bin --ignore-fail-on-non-empty; \ fi -$(BINDIR)/%: %.c $(HFILES) +$(BINDIR)/%: %.c $(SHARED_SOURCES) $(HFILES) @mkdir -p $(BINDIR) - $(CC) $(CFLAGS) $< -o $@ + $(CC) $(CFLAGS) $< $(SHARED_SOURCES) -o $@ $(RUNTGTS): run-%: $(BINDIR)/% ifdef SSHPORT diff --git a/hybrid/ddc_compartment_switching/Makefile.morello-hybrid b/hybrid/ddc_compartment_switching/Makefile.morello-hybrid new file mode 100644 index 0000000..a6eccb1 --- /dev/null +++ b/hybrid/ddc_compartment_switching/Makefile.morello-hybrid @@ -0,0 +1,9 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := shared.s +CFILES := $(wildcard *.c) + +include ../../build/Makefile.vars.morello-hybrid +include ../../build/Makefile.vars.common +include ../../build/Makefile.simple diff --git a/hybrid/ddc_compartment_switching.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c similarity index 60% rename from hybrid/ddc_compartment_switching.c rename to hybrid/ddc_compartment_switching/ddc_compartment_switching.c index f9a51dd..95facb3 100644 --- a/hybrid/ddc_compartment_switching.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c @@ -2,8 +2,8 @@ * TODO: something like intermediate level of compartment switching or ... ***/ -#include "../include/common.h" -#include "include/utils.h" +#include "../../include/common.h" +#include "../include/utils.h" #include #include #include @@ -15,10 +15,7 @@ // Simple function with some data which will be part of the compartment int compartment_simple_fun(int a, int b); -// Temp hack to "design" an interface -int switch_compartment(int a, int b, void * stack, size_t size); -// TODO: this will call assembly code -// extern int switch_compartment(int a, int b, void * stack, size_t size); +extern int switch_compartment(int a, int b, void * stack, size_t size); int main(){ void * simple_block = malloc(5000); @@ -27,10 +24,6 @@ int main(){ return 0; } -int switch_compartment(int a, int b, void * stack, size_t size){ - return compartment_simple_fun(a, b); // FIXME: this is a temp hack -} - int compartment_simple_fun(int a, int b) { return a + b; -} \ No newline at end of file +} diff --git a/hybrid/ddc_compartment_switching/shared.s b/hybrid/ddc_compartment_switching/shared.s new file mode 100644 index 0000000..5ee8616 --- /dev/null +++ b/hybrid/ddc_compartment_switching/shared.s @@ -0,0 +1,141 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +.global compartment_simple_fun + +.text +.balign 4 +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // int switch_compartment(int a, int b, void * stack, size_t size) + // + // For the purposes of this demo, `stack + size` must be 16-byte-aligned, so + // that it is suitable for use as a stack pointer with no additional + // alignment logic. In addition, the range [stack, stack+size) must be + // exactly representable as capability bounds. + // + // Note that, for this demo, the PCC is not restricted at all. Hybrid + // CheriBSD targets give the PCC very wide bounds, and all 'Load' + // permissions. In a real deployment, the PCC should be suitably restricted + // too, but this is likely to require co-operation with the C toolchain and + // runtime environment, since it will probably use the PCC to access literal + // pools, globals and other data outside the function itself. + // + // The new stack space must be wholly enclosed by the current DDC. + // + // The procedure-call standard is assumed to be AAPCS64 with the Morello + // supplement[1]. This is known as "hybrid" (as opposed to "purecap") mode. + // + // [1]: https://developer.arm.com/documentation/102205/latest + // + // Arguments are arranged as follows: + // + // w0: a + // w1: b + // x2: stack (pointer, not capability, since this is hybrid) + // x3: size + // + // The result will be returned in w0. + + // Derive a new DDC to cover the new stack. + mrs c10, DDC + scvalue c11, c10, x2 + scbndse c11, c11, x3 + msr DDC, c11 + + // Replace the stack pointer. + mov x12, sp + add sp, x2, x3 + + // Save the old DDC, stack pointer and return address on the new stack, so + // we can restore it when we return.. + // This is the leaky part of the compartmentalisation. If strict + // compartments are required, some other technique must be used, such as a + // privileged switcher or sealing mechanism (e.g. using `ldpblr`). + str c10, [sp, #-32]! + stp x12, lr, [sp, #16] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 24 -> [ old LR ] ^ + // sp + 16 -> [ old SP ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + // + // Note that this is _not_ an AAPCS64 frame record, even though it looks a + // bit like one. We don't touch FP here, and since it is not a capability + // (in hybrid mode), unwinding would fail anyway. + + // Clean capabilities left in arguments. + mov w0, w0 // `a` + mov w1, w1 // `b` + bl (clean + 8) + bl compartment_simple_fun + // Clean capabilities left in the return value. + mov w0, w0 + bl (clean + 4) + + // Restore the caller's context and compartment. + ldr c10, [sp] + ldp x12, lr, [sp, #16] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret + + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee.. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + From e6dfff91b0e51ffd166183da93018dbb32ff6884 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Mon, 4 Oct 2021 17:53:44 +0100 Subject: [PATCH 59/98] Add two examples and tests for morello-hybrid only One example switches the compartments and then reads from the allowed part. The other one, ddc_compartment_switching_nok, tries to store a value in the "forbidden" part of the memory causing an exception. Tests cover both cases. --- .buildbot.sh | 20 +++++----- .../ddc_compartment_switching.c | 38 ++++++++++++------ .../ddc_compartment_switching_nok.c | 40 +++++++++++++++++++ hybrid/ddc_compartment_switching/shared.s | 26 +++++------- tests/run_tests.sh | 31 +++++++++++--- 5 files changed, 108 insertions(+), 47 deletions(-) create mode 100644 hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c diff --git a/.buildbot.sh b/.buildbot.sh index f691849..33c9322 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -29,7 +29,7 @@ done echo "Checking that all the hybrid examples build on Morello..." platform='morello-hybrid' -for dir in hybrid; do +for dir in hybrid hybrid/ddc_compartment_switching; do pushd "$dir" make -f Makefile.$platform clean make -f Makefile.$platform all @@ -39,21 +39,19 @@ done export SSHPORT=10021 export PYTHONPATH="$CHERIBUILD"/test-scripts -echo "Running tests for 'riscv64' using QEMU." +echo "Running tests for 'morello-hybrid' using QEMU..." args=( - --architecture riscv64 + --architecture morello-hybrid # Qemu System to use - --qemu-cmd $HOME/cheri/output/sdk/bin/qemu-system-riscv64cheri - # Kernel (to avoid the default one) - --kernel $HOME/cheri/output/rootfs-riscv64-purecap/boot/kernel/kernel - # Bios (to avoid the default one) - --bios bbl-riscv64cheri-virt-fw_jump.bin - --disk-image $HOME/cheri/output/cheribsd-riscv64-purecap.img + --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello + --bios edk2-aarch64-code.fd + --disk-image $HOME/cheri/output/cheribsd-morello-hybrid.img # Required build-dir in CheriBSD --build-dir . --ssh-port $SSHPORT --ssh-key $HOME/.ssh/id_ed25519.pub ) -BUILDBOT_PLATFORM=riscv64-purecap python3 tests/run_cheri_examples.py "${args[@]}" +BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" -# TODO: Run tests for Morello too. +# TODO: Run tests for `morello-purecap` and `riscv64-purecap` too +# This is related to https://github.com/CTSRD-CHERI/cheribuild/issues/182 \ No newline at end of file diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c index 95facb3..0c380d2 100644 --- a/hybrid/ddc_compartment_switching/ddc_compartment_switching.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c @@ -1,5 +1,10 @@ /*** - * TODO: something like intermediate level of compartment switching or ... + * This example shows an intermediate level of compartment creation. + * It allocates a block of memory (5000 bytes) but only the first + * 2000 are accessible by `compartment_simple_fun`. + * + * This program does not raise an exception because it explicitly performs + * loads/stores within the compartment. ***/ #include "../../include/common.h" @@ -7,23 +12,30 @@ #include #include #include - +#include #if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) #error "This example only works on CHERI hybrid mode" #endif -// Simple function with some data which will be part of the compartment -int compartment_simple_fun(int a, int b); -extern int switch_compartment(int a, int b, void * stack, size_t size); +// Simple function accessing the compartment +int compartment_simple_fun(); +// The function connected to the assembly trampoline +extern int switch_compartment(void *stack, size_t size); -int main(){ - void * simple_block = malloc(5000); - size_t compartment_size = 2000; - switch_compartment(5, 3, simple_block, compartment_size); - return 0; +int main() +{ + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[1900] = 80; + switch_compartment(simple_block, compartment_size); + return 0; } -int compartment_simple_fun(int a, int b) { - return a + b; -} +int compartment_simple_fun() +{ + uint8_t *__capability ddc_cap = cheri_ddc_get(); + assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); + assert(ddc_cap[1900] == 80); + return 0; +} \ No newline at end of file diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c new file mode 100644 index 0000000..5043d92 --- /dev/null +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c @@ -0,0 +1,40 @@ +/*** + * This example shows an intermediate level of compartment creation. + * It allocates a block of memory (5000 bytes) but only the first + * 2000 are accessible by `compartment_simple_fun`. + * + * This program raises an exception because we try to access an + * `int32_t` which is out of the compartment's bounds. + ***/ + +#include "../../include/common.h" +#include "../include/utils.h" +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +int compartment_simple_fun(); +extern int switch_compartment(void *stack, size_t size); + +int main() +{ + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[2500] = 8; + switch_compartment(simple_block, compartment_size); + return 0; +} + +int compartment_simple_fun() +{ + uint8_t *__capability ddc_cap = cheri_ddc_get(); + // This function can access only 2000 bytes, i.e. `compartment_size` + // So the following will go over its compartment bounds + ddc_cap[2500] = 12; + return 0; +} \ No newline at end of file diff --git a/hybrid/ddc_compartment_switching/shared.s b/hybrid/ddc_compartment_switching/shared.s index 5ee8616..67d19a7 100644 --- a/hybrid/ddc_compartment_switching/shared.s +++ b/hybrid/ddc_compartment_switching/shared.s @@ -8,8 +8,6 @@ .global switch_compartment .type switch_compartment, "function" switch_compartment: - // int switch_compartment(int a, int b, void * stack, size_t size) - // // For the purposes of this demo, `stack + size` must be 16-byte-aligned, so // that it is suitable for use as a stack pointer with no additional // alignment logic. In addition, the range [stack, stack+size) must be @@ -31,25 +29,23 @@ switch_compartment: // // Arguments are arranged as follows: // - // w0: a - // w1: b - // x2: stack (pointer, not capability, since this is hybrid) - // x3: size + // x0: stack (pointer, not capability, since this is hybrid) + // x1: size // // The result will be returned in w0. // Derive a new DDC to cover the new stack. mrs c10, DDC - scvalue c11, c10, x2 - scbndse c11, c11, x3 + scvalue c11, c10, x0 + scbndse c11, c11, x1 msr DDC, c11 // Replace the stack pointer. mov x12, sp - add sp, x2, x3 + add sp, x0, x1 // Save the old DDC, stack pointer and return address on the new stack, so - // we can restore it when we return.. + // we can restore it when we return. // This is the leaky part of the compartmentalisation. If strict // compartments are required, some other technique must be used, such as a // privileged switcher or sealing mechanism (e.g. using `ldpblr`). @@ -69,15 +65,11 @@ switch_compartment: // Note that this is _not_ an AAPCS64 frame record, even though it looks a // bit like one. We don't touch FP here, and since it is not a capability // (in hybrid mode), unwinding would fail anyway. - - // Clean capabilities left in arguments. - mov w0, w0 // `a` - mov w1, w1 // `b` - bl (clean + 8) + bl clean bl compartment_simple_fun // Clean capabilities left in the return value. mov w0, w0 - bl (clean + 4) + bl clean // Restore the caller's context and compartment. ldr c10, [sp] @@ -91,7 +83,7 @@ switch_compartment: // Inner helper for cleaning capabilities from registers, either side of an // AAPCS64 function call where some level of distrust exists between caller - // and callee.. + // and callee. // // Depending on the trust model, this might not be required, but the process // is included here for demonstration purposes. Note that if data needs to diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 0301160..e9836d3 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -17,8 +17,8 @@ # - SSHUSER (defaults to 'root') # - SSHHOST (defaults to 'localhost') # -# TODO: Extend this to cover interactive examples, or those that are expected to -# fail. For now, we just test examples that run, unattended, to completion. +# TODO: Extend this to cover interactive examples and other architectures. +# For now, we just test examples on `morello-hybrid`. set -e @@ -37,15 +37,34 @@ export SSH_OPTIONS='-o "StrictHostKeyChecking no"' export SCP_OPTIONS='-o "StrictHostKeyChecking no"' function run { - pushd "$1" - for name in "${@:2}"; do - make -f Makefile.$BUILDBOT_PLATFORM run-$name + pushd "$2" + for name in "${@:3}"; do + # These tests should trigger an "In-address space security exception" + # So they should fail, i.e. `exit=1` + if [ "$1" = "to_fail" ]; then + exit_status=0 + RESULT={{$(make -f Makefile.$BUILDBOT_PLATFORM run-$name)} && exit_status=1} || true + if [ $exit_status != 0 ]; then + exit 1 + fi + else + make -f Makefile.$BUILDBOT_PLATFORM run-$name + fi done popd } # TODO: `mmap` also fails, but looks like it is intended to pass. -run . check_length check_mask seal setjmp stackscan + +# TODO: PURECAP tests + +# HYBRID TESTS +# Tests that should fail +run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok +run to_fail hybrid ddc_invalid ddc_null +# Tests that should pass +run OK hybrid/ddc_compartment_switching ddc_compartment_switching +run OK hybrid basic_ddc # TODO: 'timsort' works, but takes a very long time. Is it useful to test a # smaller data set? From 0c0ec894d04ad912337ba7dc36b8fe033751012a Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Thu, 30 Sep 2021 09:59:43 +0100 Subject: [PATCH 60/98] Fix copy/paste typo --- hybrid/include/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hybrid/include/utils.h b/hybrid/include/utils.h index 784cb25..0387400 100644 --- a/hybrid/include/utils.h +++ b/hybrid/include/utils.h @@ -4,7 +4,7 @@ ***/ #if !defined(__aarch64__) -#error "This utility functions are Morello only" +#error "These utility functions are Morello only" #endif void write_ddc(void *__capability cap); From 3d1f3aedef79bbe559ce987c9f179c5f7f1269fb Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Tue, 19 Oct 2021 17:41:26 +0100 Subject: [PATCH 61/98] fixup typo in README build instructions, example command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe15b66..90b8e33 100644 --- a/README.md +++ b/README.md @@ -54,5 +54,5 @@ and `RUNHOST` can also be specified if required, but default to `root` and For example: ``` -$ SSH_PORT=12345 make -f Makefile.morello-purecap run-seal +$ SSHPORT=12345 make -f Makefile.morello-purecap run-seal ``` From f38644c012ae642e8d4295aa3dd56a2b156281a1 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Wed, 20 Oct 2021 14:29:32 +0000 Subject: [PATCH 62/98] Add initial draft of DDC switching sentry example This exemplifies using a sentry to wrap around the compartment switching function. Note that this is **not** required normally, and the compartment switching function can be called directly in hybrid mode without additional security than usual function calls. --- .../ddc_compartment_switching_sentry.c | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c new file mode 100644 index 0000000..b2b3571 --- /dev/null +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c @@ -0,0 +1,51 @@ +/*** + * This is an adaptation of `ddc_compartment_switching.c` to exemplify wrapping + * the switching function within a sentry. Note that the code does not + * **require** the switching code be wrapped in a sentry --- it can be called + * as any normal function in hybrid mode, without any special security checks. + ***/ + +#include "../../include/common.h" +#include "../include/utils.h" + +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +// Simple function accessing the compartment +int compartment_simple_fun(); +// The function connected to the assembly trampoline +extern int switch_compartment(void *stack, size_t size); + +int main() +{ + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[1900] = 80; + + // Make the function within the sentry explicitly executable by + // inheriting the PCC executable permission + void *__capability switch_exec = + cheri_perms_and((void *__capability) switch_compartment, + cheri_perms_get(cheri_pcc_get()) | CHERI_PERM_EXECUTE); + // Wrap our function in a sentry + int (*__capability wrap_fn)(void *, size_t) = cheri_sentry_create(switch_exec); + + assert(cheri_is_valid(wrap_fn)); + assert(cheri_is_sentry(wrap_fn)); + wrap_fn(simple_block, compartment_size); + return 0; +} + +int compartment_simple_fun() +{ + uint8_t *__capability ddc_cap = cheri_ddc_get(); + assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); + assert(ddc_cap[1900] == 80); + return 0; +} From 00ecea11629cb1fc46ef628cc90619111f1a40a2 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Fri, 22 Oct 2021 10:56:32 +0100 Subject: [PATCH 63/98] Remove redundant code The example showing compartment switching with sentries had redundant code. We do not need to explicitly grant execution permission to the function wrapped by the sentry, as this exists. The issue before was casting the wrapped function pointer to a `void*`, rather than directly to `void* __capability`, which removed the necessary permissions. --- .../ddc_compartment_switching_sentry.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c index b2b3571..e96d9b0 100644 --- a/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c @@ -28,13 +28,9 @@ int main() size_t compartment_size = 2000; simple_block[1900] = 80; - // Make the function within the sentry explicitly executable by - // inheriting the PCC executable permission - void *__capability switch_exec = - cheri_perms_and((void *__capability) switch_compartment, - cheri_perms_get(cheri_pcc_get()) | CHERI_PERM_EXECUTE); // Wrap our function in a sentry - int (*__capability wrap_fn)(void *, size_t) = cheri_sentry_create(switch_exec); + int (*__capability wrap_fn)(void *, size_t) = + cheri_sentry_create((void *__capability) switch_compartment); assert(cheri_is_valid(wrap_fn)); assert(cheri_is_sentry(wrap_fn)); From d2edd1728c2c2f1406382828bdc6a0e85fb73689 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Mon, 22 Nov 2021 10:37:31 +0000 Subject: [PATCH 64/98] Format `instructions.h` This applies a `clang-format` to `include/instructions.h` file to ensure no further errors on future PRs. --- include/instructions.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/instructions.h b/include/instructions.h index 7b19947..9f2ba1c 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -1632,14 +1632,13 @@ uint32_t wfi() } uint32_t xor - (uint32_t rd, uint32_t rs1, uint32_t rs2) -{ - uint32_t i = 0x00004033; // 33 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; -} + (uint32_t rd, uint32_t rs1, uint32_t rs2) { + uint32_t i = 0x00004033; // 33 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; + } uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) { From 1a2b0bf4fcd540c6ed814fa8c4abf25e4e8a8b00 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Fri, 19 Nov 2021 13:38:22 +0000 Subject: [PATCH 65/98] Add two compartment examples These examples aim to do two things: * restrict the program capability counter (`PCC`) * call a function outside PCC bounds via a sealed capability entry (sentry) Example `restrict_pcc` shows that we successfully restrict the `PCC`, and that calling a function outside the `PCC` bounds fails, with an expected `In-address space security exception`. Example `call_sentry` shows how to call a function outside of restricted `PCC` bounds via a sentry, as well as one (not fully secure) choice of how to transition between a compartment (i.e., restricted permissions) and a state with full permissions. --- .../Makefile.morello-hybrid | 9 + .../compartment_examples/call_sentry/main.c | 92 +++++++++ .../compartment_examples/call_sentry/shared.s | 174 ++++++++++++++++++ .../compartment_examples/restrict_pcc/main.c | 45 +++++ .../restrict_pcc/shared.s | 155 ++++++++++++++++ 5 files changed, 475 insertions(+) create mode 100644 hybrid/compartment_examples/Makefile.morello-hybrid create mode 100644 hybrid/compartment_examples/call_sentry/main.c create mode 100644 hybrid/compartment_examples/call_sentry/shared.s create mode 100644 hybrid/compartment_examples/restrict_pcc/main.c create mode 100644 hybrid/compartment_examples/restrict_pcc/shared.s diff --git a/hybrid/compartment_examples/Makefile.morello-hybrid b/hybrid/compartment_examples/Makefile.morello-hybrid new file mode 100644 index 0000000..aa99a37 --- /dev/null +++ b/hybrid/compartment_examples/Makefile.morello-hybrid @@ -0,0 +1,9 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := shared.s +CFILES := $(wildcard *.c) + +include ../../../build/Makefile.vars.morello-hybrid +include ../../../build/Makefile.vars.common +include ../../../build/Makefile.simple diff --git a/hybrid/compartment_examples/call_sentry/main.c b/hybrid/compartment_examples/call_sentry/main.c new file mode 100644 index 0000000..0bac847 --- /dev/null +++ b/hybrid/compartment_examples/call_sentry/main.c @@ -0,0 +1,92 @@ +/*** + * This example showcases two particular actions to help compartmentalization: + * - reducing the bounds of the PCC (shared.s:82) + * - calling a sentry from within a bounded compartment (shared.s:114) + * + * One difficulty, and capability leak, is how to return from the sentry + * function back to the caller with correct capabilities (i.e., PCC). In this + * example, we use `clr`, and manually return to it (main.c:49). However, + * having the `clr` available to the sentry function means it could access + * capabilities it should not be privy to (e.g., if the bounds of the sentry + * are set to only cover the specific function). In this example, this is + * further aggravated by the jump between assembly and `C`: the `C` function is + * compiled to assembly which makes use of `lr` (which stores pointers, not + * capabilities), meaning we would, by default, return to a context in which we + * have no execution permissions (more specifically, the `PCC` is set to cover + * the callee, and when we return to the caller, the `PCC` is not affected due + * to returning to a pointer). + ***/ + +#include "../../../include/common.h" +#include "../../include/utils.h" + +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +// Trampoline function to restore caller's PCC after we return from a sentry +// call; this assembly function also includes the sentry containing the +// function to be called from the container +extern int comp_fun_tr(); +// The function connected to the assembly trampoline +extern int switch_compartment(void *stack, size_t size, void *__capability fn_call_start, + void *__capability pcc); + +// Function to be called via a sentry from within a restricted compartment. As +// the `stk` variable is within DDC, it can write to it. +void comp_fun_c(uint8_t *stk) +{ + stk[1800] = 80; + + // The `add` instruction is required to correctly reset the `sp`, which is + // modified by the assembled version of this function; this might be + // compiler-specific. In this example, `sp` is used to store information to + // reset the environment, therefore we must ensure it is reset correctly + // upon return. + // + // The `ret` instruction produced by + // default only uses `lr`, instead of `clr`; using the `lr` means we would + // return with a PCC bound to this function, and not be able to execute + // within the caller. Using `clr` ensures the PCC is reset correctly. + // + // Overall, this example shows it is not a good idea to call C functions + // via sentries in hybrid mode. + // + // NOTE: As the `clr` remains in scope during the execution of this + // function, it essentially leaks the executable capability. The current + // design, and the interfacing between `C` and assembly requires this to be + // available in this function + asm("add sp, sp, #0x10; ret clr"); +} + +int main() +{ + uint8_t *comp_mem = malloc(5000); + size_t comp_size = 2000; + + // Create a capability which we will use to tightly bound the PCC for the + // compartment + void *__capability call_cap = (void *__capability) comp_fun_tr; + call_cap = cheri_bounds_set(call_cap, comp_size); + + // Derive a sentry from the PCC to allow calling a function outside PCC + // bounds + void *__capability comp_fun_c_sentry = cheri_pcc_get(); + comp_fun_c_sentry = cheri_address_set(comp_fun_c_sentry, (unsigned long) &comp_fun_c); + + // Set the bounds on the sentry, which restricts the PCC when executing the + // function. The value `40` here was determined by disassembling + // `comp_fun_c`. + comp_fun_c_sentry = cheri_bounds_set(comp_fun_c_sentry, 40); + + switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c_sentry); + // + // Check the compartment function has been executed. + assert(comp_mem[1800] == 80); + return 0; +} diff --git a/hybrid/compartment_examples/call_sentry/shared.s b/hybrid/compartment_examples/call_sentry/shared.s new file mode 100644 index 0000000..68d7b71 --- /dev/null +++ b/hybrid/compartment_examples/call_sentry/shared.s @@ -0,0 +1,174 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +.text +.balign 4 +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // For the purposes of this demo, `stack + size` must be 16-byte-aligned, so + // that it is suitable for use as a stack pointer with no additional + // alignment logic. In addition, the range [stack, stack+size) must be + // exactly representable as capability bounds. + // + // This example bounds the PCC, but there are potentially other ways to leak + // information. Particularly, during the execution of the compartment + // function, there is a capability in memory, which the function has access + // to, which has greater permissions than provided to the compartment. + // + // The new stack space must be wholly enclosed by the current DDC. + // + // The procedure-call standard is assumed to be AAPCS64 with the Morello + // supplement[1]. This is known as "hybrid" (as opposed to "purecap") mode. + // + // [1]: https://developer.arm.com/documentation/102205/latest + // + // Arguments are arranged as follows: + // + // x0: stack (pointer, not capability, since this is hybrid) + // x1: size + // c2: capability to restrict PCC, containing function to create + // compartment + // c3: function to be sealed to be called from within compartment + // + // The result will be returned in w0. + + // Derive a new DDC to cover the new stack. + mrs c10, DDC + scvalue c11, c10, x0 + scbndse c11, c11, x1 + msr DDC, c11 + + // Replace the stack pointer. + mov x12, sp + add sp, x0, x1 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Save the old DDC, stack pointer and return address on the new stack, so + // we can restore it when we return. + // This is the leaky part of the compartmentalisation. If strict + // compartments are required, some other technique must be used, such as a + // privileged switcher or sealing mechanism (e.g. using `ldpblr`). + stp c10, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + // + // Note that this is _not_ an AAPCS64 frame record, even though it looks a + // bit like one. We don't touch FP here, and since it is not a capability + // (in hybrid mode), unwinding would fail anyway. + + // Seal pointer to function to be executed by compartment within restricted + // PCC/DDC environment; `rb` indicates we will "use with a register based + // branch". + seal c1, c3, rb + bl clean+12 + + // This branch instruction restricts the PCC, as `c2` contains a capability + // with restricted bounds, set within C code. At this point, we "enter" the + // compartment. + blr c2 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +// This wrapper function is required to ensure the PCC is reset after calling +// a sentry. The PCC must change to be able to call the function within the +// sentry. +.global comp_fun_tr +.type comp_fun_tr, "function" +comp_fun_tr: + mov x2, #0 + str clr, [sp, #-16]! + bl comp_fun + ldr clr, [sp], #16 + ret clr + +// Main function to be called from within the compartment. For this example, we +// call a sentry to a C function outside restricted PCC bounds. At this point, +// we are effectively within a compartment. +.global comp_fun +.type comp_fun, "function" +comp_fun: + // Similar to above, derive a new `clr` (`bl` in hybrid mode puts a + // pointer, not a capability, in the `clr`), and store it to use after + // coming from executing the priveleged function + cvtp c10, lr + str c10, [sp, #-16]! + blrs c1 + ldr clr, [sp], #16 + ret clr + + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + diff --git a/hybrid/compartment_examples/restrict_pcc/main.c b/hybrid/compartment_examples/restrict_pcc/main.c new file mode 100644 index 0000000..f54292a --- /dev/null +++ b/hybrid/compartment_examples/restrict_pcc/main.c @@ -0,0 +1,45 @@ +/*** + * This example showcases how to restrict a PCC (`shared.s:81`), and that + * attempting to call a function (`shared.s:103`) lying outside the bounds of + * the restriced PCC yields an `In-address space security exception`. + ***/ + +#include "../../../include/common.h" +#include "../../include/utils.h" + +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +// Wrapper function to be called from within the restricted compartment, within +// PCC bounds. +extern int comp_fun(); +// The function connected to the assembly trampoline. +extern int switch_compartment(void *stack, size_t size, void *__capability fn_call_start, + void *__capability pcc); + +// Function outside of PCC bounds to be called from within the compartment. +void comp_fun_c(uint8_t *stk) +{ + // unreachable +} + +int main() +{ + uint8_t *comp_mem = malloc(5000); + size_t comp_size = 2000; + + // Create a capability which we will use to tightly bound the PCC for the + // compartment. + void *__capability call_cap = (void *__capability) comp_fun; + call_cap = cheri_bounds_set(call_cap, comp_size); + + switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c); + assert(false && "Should not get here"); + return 0; +} diff --git a/hybrid/compartment_examples/restrict_pcc/shared.s b/hybrid/compartment_examples/restrict_pcc/shared.s new file mode 100644 index 0000000..69f7e2d --- /dev/null +++ b/hybrid/compartment_examples/restrict_pcc/shared.s @@ -0,0 +1,155 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +.global comp_fun_c + +.text +.balign 4 +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // For the purposes of this demo, `stack + size` must be 16-byte-aligned, so + // that it is suitable for use as a stack pointer with no additional + // alignment logic. In addition, the range [stack, stack+size) must be + // exactly representable as capability bounds. + // + // This example bounds the PCC, but there is potentially other ways to leak + // information. Particularly, when the compartment function call returns, + // there is a small window where the PCC is unbound due to calling a + // sentry. + // + // The new stack space must be wholly enclosed by the current DDC. + // + // The procedure-call standard is assumed to be AAPCS64 with the Morello + // supplement[1]. This is known as "hybrid" (as opposed to "purecap") mode. + // + // [1]: https://developer.arm.com/documentation/102205/latest + // + // Arguments are arranged as follows: + // + // x0: stack (pointer, not capability, since this is hybrid) + // x1: size + // c2: capability to restrict PCC, containing function to create + // compartment + // c3: function to be sealed to be called from within compartment + // + // The result will be returned in w0. + + // Derive a new DDC to cover the new stack. + mrs c10, DDC + scvalue c11, c10, x0 + scbndse c11, c11, x1 + msr DDC, c11 + + // Replace the stack pointer. + mov x12, sp + add sp, x0, x1 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Save the old DDC, stack pointer and return address on the new stack, so + // we can restore it when we return. + // This is the leaky part of the compartmentalisation. If strict + // compartments are required, some other technique must be used, such as a + // privileged switcher or sealing mechanism (e.g. using `ldpblr`). + stp c10, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + // + // Note that this is _not_ an AAPCS64 frame record, even though it looks a + // bit like one. We don't touch FP here, and since it is not a capability + // (in hybrid mode), unwinding would fail anyway. + + mov x1, x3 + bl clean+12 + + // This branch instruction restricts the PCC, as `c2` contains a capability + // with restricted bounds, set within C code. At this point, we "enter" the + // compartment. + blr c2 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +// Main function to be called from within the compartment. For this example, we +// call a sentry to a C function outside restricted PCC bounds. +.global comp_fun +.type comp_fun, "function" +comp_fun: + // We are trying to call `comp_fun_c`, outside of the boundaries of the PCC + // (which spans only the `comp_fun` function). + bl comp_fun_c + + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + From 63d41fa64754a2604f71474a492376822503b667 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Tue, 30 Nov 2021 14:41:16 +0000 Subject: [PATCH 66/98] Compartment switch via memory stored data This example builds on previous ones, particularly improving the `switch_compartment` (`shared.s:17`) function. This is a step towards inter-compartment transition without the need of a more-privileged entity controlling the switch (but needing to set everything up before control is relinquished to a compartment). Some specific improvements over previous examples: - the compartment switching function now reads information from memory rather than from the stack. This allows compartments to call this function from a constrained environment, without leaking capabilities beyond a few prepared ones; - we showcase the use of the compartment ID (`CID_EL0`) register to control which compartment we are switching into. This is more of an example, as it would entail compartments are aware of the ID of the callee. --- hybrid/compartment_examples/comp_setup/main.c | 136 +++++++++++++++++ hybrid/compartment_examples/comp_setup/main.h | 6 + .../compartment_examples/comp_setup/shared.S | 142 ++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 hybrid/compartment_examples/comp_setup/main.c create mode 100644 hybrid/compartment_examples/comp_setup/main.h create mode 100644 hybrid/compartment_examples/comp_setup/shared.S diff --git a/hybrid/compartment_examples/comp_setup/main.c b/hybrid/compartment_examples/comp_setup/main.c new file mode 100644 index 0000000..ab185a7 --- /dev/null +++ b/hybrid/compartment_examples/comp_setup/main.c @@ -0,0 +1,136 @@ +/* This example builds on previous ones, particularly improving the + * `switch_compartment` (`shared.s:17`) function. This is a step towards + * inter-compartment transition without the need of a more-privileged entity + * controlling the switch (but needing to set everything up before control is + * relinquished to a compartment). + * + * Some specific improvements over previous examples: + * - the compartment switching function now reads information from memory + * rather than from the stack. This allows compartments to call this function + * from a constrained environment, without leaking capabilities beyond a few + * prepared ones; + * - we showcase the use of the compartment ID (`CID_EL0`) register to control + * which compartment we are switching into. This is more of an example, as it + * would entail compartments are aware of the ID of the callee. + */ + +#include "../../../include/common.h" +#include "../../include/utils.h" + +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern int switch_compartment(); +extern void comp_f_fn(); + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 5000; +size_t id = 0; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment's stack (8B) + * the size of the stack (8B) + * - address to the start of the compartment's heap (8B) + * the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *stack_addr; + size_t stack_len; + void *heap_addr; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "main.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* This function stores the compartment data in memory, so that it can be + * accessed by the compartments as needed. + */ +void executive_switch(struct comp c) +{ + void *comps_addr = &comps; + asm("mov x19, %0\n\t" + "mov x0, #0\n\t" + "msr CID_EL0, c0" + : + : "r"(comps_addr)); + + switch_compartment(); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) +{ + assert(id < COMP_COUNT); + + struct comp new_comp; + new_comp.id = id; + + new_comp.stack_addr = (void *) _start_addr; + new_comp.stack_len = comp_stack_size; + new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); + new_comp.heap_len = total_comp_size - comp_stack_size; + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + void *__capability comp_fn = (void *__capability) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, 40); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn); + + executive_switch(comps[0]); + + // Check compartment did indeed execute + assert(comp_f[4000] == 42); +} diff --git a/hybrid/compartment_examples/comp_setup/main.h b/hybrid/compartment_examples/comp_setup/main.h new file mode 100644 index 0000000..b983e8f --- /dev/null +++ b/hybrid/compartment_examples/comp_setup/main.h @@ -0,0 +1,6 @@ +#define COMP_COUNT 1 +#define COMP_SIZE 80 +#define COMP_OFFSET_STK_ADDR 8 +#define COMP_OFFSET_STK_LEN 16 +#define COMP_OFFSET_DDC 48 +#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/comp_setup/shared.S b/hybrid/compartment_examples/comp_setup/shared.S new file mode 100644 index 0000000..208f797 --- /dev/null +++ b/hybrid/compartment_examples/comp_setup/shared.S @@ -0,0 +1,142 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "main.h" + +.global comp_f_fn + +.text +.balign 4 + +/* This function has been updated with a few features: + * - it identifies which compartment to switch to based on the value within the + * `CID_EL0` register; + * - it reads compartment data from memory, rather than it having to be passed + * to it via registers - this includes DDC and PCC of compartment to switch to. + */ +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + + // Get compartment to switch to data + mrs c10, CID_EL0 + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x19, x11] + add x13, x10, #COMP_OFFSET_STK_LEN + ldr x13, [x19, x13] + add sp, x11, x13 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Save old DDC, old SP, old LR on stack + mrs c10, DDC + stp c10, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x19, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c11, [x19, x11] + msr DDC, c11 + + bl clean+4 + + // Jump to the function within the compartment we are switching to + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_f_fn, "function" +comp_f_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr + + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + From 385a82f7b9204945636058d08a6f5a59f37cb168 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Fri, 3 Dec 2021 14:24:12 +0000 Subject: [PATCH 67/98] Add example showing inter-compartment switch This example adds a second compartment, and shows a possible design to achieve inter-compartment switching without (much) privilege escalation. In this file, we setup compartment information within array `comps` (`main.c:84`). This information will be used by the assembly function `switch_compartment`. Further, we save a capability to `switch_compartment()` in a register (`main.c:102`), to be able to call it from a bounded-PCC context, as well as a capability defining the area of memory with compartment information (`main.c:101`). These could be added to the memory area of each created compartment for better security, as well as sealed. Our two compartments provide one entry point each, `comp_f_fn` (`shared.S:93`), and `comp_g_fn` (`shared.S:109`). The design is that compartment `f` will perform a switch to compartment `g`, which performs some observable action (in this instance, sets a specific memory location to a specific integer value). The switching process in (almost) entirely bound within `switch_compartment`, on the execution side, and the memory area with compartment information, for memory access. We note almost, as the executable area is approximated (`main.c:96`). --- .../inter_comp_call/main.c | 152 ++++++++++++++++ .../inter_comp_call/main.h | 6 + .../inter_comp_call/shared.S | 166 ++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 hybrid/compartment_examples/inter_comp_call/main.c create mode 100644 hybrid/compartment_examples/inter_comp_call/main.h create mode 100644 hybrid/compartment_examples/inter_comp_call/shared.S diff --git a/hybrid/compartment_examples/inter_comp_call/main.c b/hybrid/compartment_examples/inter_comp_call/main.c new file mode 100644 index 0000000..dbebfda --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/main.c @@ -0,0 +1,152 @@ +/* This example adds a second compartment, and shows a possible design to + * achieve inter-compartment switching without (much) privilege escalation. In + * this file, we setup compartment information within array `comps` + * (`main.c:84`). This information will be used by the assembly function + * `switch_compartment`. Further, we save a capability to + * `switch_compartment()` in a register (`main.c:102`), to be able to call it + * from a bounded-PCC context, as well as a capability defining the area of + * memory with compartment information (`main.c:101`). These could be added to + * the memory area of each created compartment for better security, as well as + * sealed. + * + * Our two compartments provide one entry point each, `comp_f_fn` + * (`shared.S:93`), and `comp_g_fn` (`shared.S:109`). The design is that + * compartment `f` will perform a switch to compartment `g`, which performs + * some observable action (in this instance, sets a specific memory location to + * a specific integer value). The switching process in (almost) entirely bound + * within `switch_compartment`, on the execution side, and the memory area with + * compartment information, for memory access. We note almost, as the + * executable area is approximated (`main.c:96`). + */ + +#include "../../../include/common.h" +#include "../../include/utils.h" + +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern int switch_compartment(); +extern void comp_f_fn(); +extern void comp_g_fn(); + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 5000; +size_t id = 0; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment's stack (8B) + * the size of the stack (8B) + * - address to the start of the compartment's heap (8B) + * the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *stack_addr; + size_t stack_len; + void *heap_addr; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "main.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* This function stores the compartment data in memory, so that it can be + * accessed by the compartments as needed. + */ +void executive_switch(struct comp c) +{ + void *__capability switch_cap = (void *__capability) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + + asm("mov c19, %w0\n\t" + "mov c20, %w1\n\t" + "mov x0, #0\n\t" + "msr CID_EL0, c0" + : + : "r"(comps_addr), "r"(switch_cap)); + + switch_compartment(); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) +{ + assert(id < COMP_COUNT); + + struct comp new_comp; + new_comp.id = id; + + new_comp.stack_addr = (void *) _start_addr; + new_comp.stack_len = comp_stack_size; + new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); + new_comp.heap_len = total_comp_size - comp_stack_size; + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, 40); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn); + + executive_switch(comps[0]); + + // Check compartment did indeed execute + assert(comp_g[4000] == 42); +} diff --git a/hybrid/compartment_examples/inter_comp_call/main.h b/hybrid/compartment_examples/inter_comp_call/main.h new file mode 100644 index 0000000..93354ec --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/main.h @@ -0,0 +1,6 @@ +#define COMP_COUNT 2 +#define COMP_SIZE 80 +#define COMP_OFFSET_STK_ADDR 8 +#define COMP_OFFSET_STK_LEN 16 +#define COMP_OFFSET_DDC 48 +#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/inter_comp_call/shared.S b/hybrid/compartment_examples/inter_comp_call/shared.S new file mode 100644 index 0000000..9d53164 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/shared.S @@ -0,0 +1,166 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "main.h" + +.global comp_f_fn +.global comp_g_fn + +.text +.balign 4 + +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c19`). + * Performs a compartment switch based on the id saved in `CID_EL0` (currently + * just an integer). + */ +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + msr DDC, c19 + + // Get compartment to switch to data + mrs c10, CID_EL0 + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x19, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x19, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x19, x11] + add x13, x10, #COMP_OFFSET_STK_LEN + ldr x13, [x19, x13] + add sp, x11, x13 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old LR (c11) on stack + stp c2, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ +.type comp_f_fn, "function" +comp_f_fn: + mov x0, #1 + msr CID_EL0, c0 + + // Store the `clr` for exitting `comp_f_fn`; this is overwritten by + // `switch_compartment`. + str clr, [sp, #-16]! + blr c20 + ldr clr, [sp], #16 + + ret clr + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr + + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + // mov x19, x19 + // mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + From 4a9c21bedf1578a4c93db1808a3bf273e9c43af0 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Thu, 16 Dec 2021 15:21:13 +0000 Subject: [PATCH 68/98] Improve compartment security In this example, we tighten security even further. We store the DDC/PCC of `switch_compartment` within its memory region. Then, for each compartment, we create a copy of a capability pointing to this DDC/PCC pair, sealed with `lpb`. This allows compartments to call `switch_compartment` without having access to any information pertaining to it. We provide an additional example (`shared_try_deref.S`), where a malicious compartment attempts to dereference its local copy, to gain access to `switch_compartment`. --- .../base/Makefile.morello-hybrid | 10 + .../inter_comp_call/{ => base}/main.c | 4 +- .../inter_comp_call/{ => base}/main.h | 0 .../inter_comp_call/{ => base}/shared.S | 0 .../secure-try_deref/Makefile.morello-hybrid | 10 + .../inter_comp_call/secure-try_deref/main.c | 203 ++++++++++++++++++ .../inter_comp_call/secure-try_deref/main.h | 6 + .../secure-try_deref/shared_try_deref.S | 187 ++++++++++++++++ .../secure/Makefile.morello-hybrid | 10 + .../inter_comp_call/secure/main.c | 203 ++++++++++++++++++ .../inter_comp_call/secure/main.h | 6 + .../inter_comp_call/secure/shared.S | 194 +++++++++++++++++ tests/run_tests.sh | 3 + 13 files changed, 834 insertions(+), 2 deletions(-) create mode 100644 hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid rename hybrid/compartment_examples/inter_comp_call/{ => base}/main.c (98%) rename hybrid/compartment_examples/inter_comp_call/{ => base}/main.h (100%) rename hybrid/compartment_examples/inter_comp_call/{ => base}/shared.S (100%) create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/main.c create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/main.h create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/shared.S diff --git a/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid new file mode 100644 index 0000000..84ba7ca --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := shared.s +CFILES := $(wildcard *.c) +ROOTDIR=../../../.. + +include $(ROOTDIR)/build/Makefile.vars.morello-hybrid +include $(ROOTDIR)/build/Makefile.vars.common +include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/main.c b/hybrid/compartment_examples/inter_comp_call/base/main.c similarity index 98% rename from hybrid/compartment_examples/inter_comp_call/main.c rename to hybrid/compartment_examples/inter_comp_call/base/main.c index dbebfda..ef4ada3 100644 --- a/hybrid/compartment_examples/inter_comp_call/main.c +++ b/hybrid/compartment_examples/inter_comp_call/base/main.c @@ -19,8 +19,8 @@ * executable area is approximated (`main.c:96`). */ -#include "../../../include/common.h" -#include "../../include/utils.h" +#include "../../../../include/common.h" +#include "../../../include/utils.h" #include #include diff --git a/hybrid/compartment_examples/inter_comp_call/main.h b/hybrid/compartment_examples/inter_comp_call/base/main.h similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/main.h rename to hybrid/compartment_examples/inter_comp_call/base/main.h diff --git a/hybrid/compartment_examples/inter_comp_call/shared.S b/hybrid/compartment_examples/inter_comp_call/base/shared.S similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/shared.S rename to hybrid/compartment_examples/inter_comp_call/base/shared.S diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid new file mode 100644 index 0000000..b8e60df --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := shared_try_deref.s +CFILES := $(wildcard *.c) +ROOTDIR=../../../.. + +include $(ROOTDIR)/build/Makefile.vars.morello-hybrid +include $(ROOTDIR)/build/Makefile.vars.common +include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c new file mode 100644 index 0000000..12475c6 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c @@ -0,0 +1,203 @@ +/* In this example, we tighten security even further, building on previous + * examples [1]. We store the DDC and the PCC of `switch_compartment` + * consecutively within its own bounds. Then, we create a capability pointing + * to the DDC, which is sealed such that it can only be used in an `lpb`-type + * call, and provide local copies of this capability to each compartment. Thus, + * compartments are allowed to call `switch_compartment` via `ldpblr`, without + * access to either its PCC or DDC (by nature of capabilities). + * + * The local copies are stored in the heap space of each compartment (as we do + * not implement memory management at this point, this has no bearing at + * present, but most likely will in the future). This could essentially be + * considered a separate region of memory. Further, when using the local + * capabilities to call `switch_compartment` from a compartment, the + * destination register *must* be `c29`. + * + * Note that in the current design, each compartment has a single function it + * executes when it is called. Allowing multiple entry points can be modelled + * by having more capabilities be saved, in addition to the compartment + * switching one, in each compartment's heap. Alternatively, we could, within + * the compartment switcher, determine in which compartment we need to switch + * to call a given function, based on PCC bounds for each compartment. + + * Additionally, we also changed the expected memory layout in the + * compartments. In the absence of a custom memory allocator, we note that the + * heap is not used, except for manually storing the local capability copy as + * detailed above. Current compartment memory layout: + * + * ----------------- < DDC + total_comp_size + * | switcher_call | + * | | + * | ^ | + * | HEAP | + * sp > |-----------------| < DDC + comp_stack_size + * | STACK | + * | v | + * ----------------- < `DDC value` + * + * [1] + https://github.com/capablevms/cheri-examples/tree/master/hybrid/compartment_examples/inter_comp_call + */ + +#include "../../../../include/common.h" +#include "../../../include/utils.h" + +#include +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern void executive_switch(void *__capability); +extern int switch_compartment(); +extern void comp_f_fn(); +extern void comp_g_fn(); +extern void *comp_f_fn_end; +extern void *comp_g_fn_end; + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 4992; +size_t id = 0; + +/* Capabilities representing DDC and PCC of the switcher, stored in + * `switcher_caps`. They are saved successively to facilitate calling via + * `ldpblr`, with the first being the DDC, and the second, the PCC. + */ +const size_t switcher_caps_count = 2; +void *__capability switcher_caps[switcher_caps_count]; + +/* Capability required by compartments to call `switch_compartment` securely. + * Duplicated in each compartment. */ +void *__capability switcher_call; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment memory area (also lowest address + * of the stack) (8B) + * - address to the start (highest address) of the compartment's stack + * (corresponding to the lowest address of the heap) (8B) + * the size of the stack (8B) + * - address to the top (highest addressof the compartment's heap (8B) + * the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "main.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* Create and save required capabilities. Currently, this means: + * - PCC of `switch_compartment` + * - DDC of `switch_compartment` + * - capability to allow compartments to call `switch_compartment` via `lpdblr` + */ +void init_comps() +{ + void *__capability switch_cap = (void *__capability) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) +{ + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throught the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + // 40 is arbitary; meant to be the size of the executable function within + // compartment + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + init_comps(); + + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + + executive_switch(switcher_caps[0]); + + // Check compartment did indeed execute + assert(comp_g[4000] == 42); +} diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h new file mode 100644 index 0000000..50a8dc8 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h @@ -0,0 +1,6 @@ +#define COMP_COUNT 2 +#define COMP_SIZE 80 +#define COMP_OFFSET_STK_ADDR 16 +#define COMP_OFFSET_STK_LEN 24 +#define COMP_OFFSET_DDC 48 +#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S new file mode 100644 index 0000000..6e37606 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S @@ -0,0 +1,187 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "main.h" + +.global comp_f_fn +.global comp_g_fn +.global comp_f_fn_end +.global comp_g_fn_end + +.text +.balign 4 + +.global executive_switch +.type executive_switch, "function" +executive_switch: + mov c29, c0 + mov x0, #0 + bl switch_compartment + +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c29`). + * Performs a compartment switch based on the id saved in `x0` (currently just + * an integer index into the `comps` array). + */ +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old LR (c11) on stack + stp c2, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, except register used to call function within + // compartment we are transitioning to + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +.type get_comp_switcher_ref, "function" +get_comp_switcher_ref: + mov x1, #5000 + sub x1, x1, #16 + add c0, c0, x1 + ret + +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ +.type comp_f_fn, "function" +comp_f_fn: + // Retrieve local capability containing switcher information + mrs c1, DDC + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + + // Try to dereference it to retrieve switcher DDC; this is expected to fail + // due to the local capability being sealed (`main.c:143`). + ldr c1, [c1] + + ldr clr, [sp], #16 + + ret clr +comp_f_fn_end: + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + diff --git a/hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid new file mode 100644 index 0000000..84ba7ca --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := shared.s +CFILES := $(wildcard *.c) +ROOTDIR=../../../.. + +include $(ROOTDIR)/build/Makefile.vars.morello-hybrid +include $(ROOTDIR)/build/Makefile.vars.common +include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.c b/hybrid/compartment_examples/inter_comp_call/secure/main.c new file mode 100644 index 0000000..12475c6 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/main.c @@ -0,0 +1,203 @@ +/* In this example, we tighten security even further, building on previous + * examples [1]. We store the DDC and the PCC of `switch_compartment` + * consecutively within its own bounds. Then, we create a capability pointing + * to the DDC, which is sealed such that it can only be used in an `lpb`-type + * call, and provide local copies of this capability to each compartment. Thus, + * compartments are allowed to call `switch_compartment` via `ldpblr`, without + * access to either its PCC or DDC (by nature of capabilities). + * + * The local copies are stored in the heap space of each compartment (as we do + * not implement memory management at this point, this has no bearing at + * present, but most likely will in the future). This could essentially be + * considered a separate region of memory. Further, when using the local + * capabilities to call `switch_compartment` from a compartment, the + * destination register *must* be `c29`. + * + * Note that in the current design, each compartment has a single function it + * executes when it is called. Allowing multiple entry points can be modelled + * by having more capabilities be saved, in addition to the compartment + * switching one, in each compartment's heap. Alternatively, we could, within + * the compartment switcher, determine in which compartment we need to switch + * to call a given function, based on PCC bounds for each compartment. + + * Additionally, we also changed the expected memory layout in the + * compartments. In the absence of a custom memory allocator, we note that the + * heap is not used, except for manually storing the local capability copy as + * detailed above. Current compartment memory layout: + * + * ----------------- < DDC + total_comp_size + * | switcher_call | + * | | + * | ^ | + * | HEAP | + * sp > |-----------------| < DDC + comp_stack_size + * | STACK | + * | v | + * ----------------- < `DDC value` + * + * [1] + https://github.com/capablevms/cheri-examples/tree/master/hybrid/compartment_examples/inter_comp_call + */ + +#include "../../../../include/common.h" +#include "../../../include/utils.h" + +#include +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern void executive_switch(void *__capability); +extern int switch_compartment(); +extern void comp_f_fn(); +extern void comp_g_fn(); +extern void *comp_f_fn_end; +extern void *comp_g_fn_end; + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 4992; +size_t id = 0; + +/* Capabilities representing DDC and PCC of the switcher, stored in + * `switcher_caps`. They are saved successively to facilitate calling via + * `ldpblr`, with the first being the DDC, and the second, the PCC. + */ +const size_t switcher_caps_count = 2; +void *__capability switcher_caps[switcher_caps_count]; + +/* Capability required by compartments to call `switch_compartment` securely. + * Duplicated in each compartment. */ +void *__capability switcher_call; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment memory area (also lowest address + * of the stack) (8B) + * - address to the start (highest address) of the compartment's stack + * (corresponding to the lowest address of the heap) (8B) + * the size of the stack (8B) + * - address to the top (highest addressof the compartment's heap (8B) + * the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "main.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* Create and save required capabilities. Currently, this means: + * - PCC of `switch_compartment` + * - DDC of `switch_compartment` + * - capability to allow compartments to call `switch_compartment` via `lpdblr` + */ +void init_comps() +{ + void *__capability switch_cap = (void *__capability) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) +{ + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throught the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + // 40 is arbitary; meant to be the size of the executable function within + // compartment + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + init_comps(); + + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + + executive_switch(switcher_caps[0]); + + // Check compartment did indeed execute + assert(comp_g[4000] == 42); +} diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.h b/hybrid/compartment_examples/inter_comp_call/secure/main.h new file mode 100644 index 0000000..50a8dc8 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/main.h @@ -0,0 +1,6 @@ +#define COMP_COUNT 2 +#define COMP_SIZE 80 +#define COMP_OFFSET_STK_ADDR 16 +#define COMP_OFFSET_STK_LEN 24 +#define COMP_OFFSET_DDC 48 +#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/inter_comp_call/secure/shared.S b/hybrid/compartment_examples/inter_comp_call/secure/shared.S new file mode 100644 index 0000000..a863750 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/shared.S @@ -0,0 +1,194 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "main.h" + +.global comp_f_fn +.global comp_g_fn +.global comp_f_fn_end +.global comp_g_fn_end + +.text +.balign 4 + +.global executive_switch +.type executive_switch, "function" +executive_switch: + mov c29, c0 + mov x0, #0 + b switch_compartment + ret + +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c29`). + * Performs a compartment switch based on the id saved in `x0` (currently just + * an integer index into the `comps` array). + */ +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Derive a new clr to restore PCC, and store it. + cvtp c11, lr + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old LR (c11) on stack + stp c2, c11, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, except register used to call function within + // compartment we are transitioning to + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + +.type get_comp_switcher_ref, "function" +get_comp_switcher_ref: + mov x1, #5000 + sub x1, x1, #16 + add c0, c0, x1 + ret + +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ +.type comp_f_fn, "function" +comp_f_fn: + // Set compartment ID we want to switch to + mov x0, #1 + + // Store the `clr` for exitting `comp_f_fn`; this is overwritten by + // `switch_compartment`. + str clr, [sp, #-16]! + + // Retrieve local capability containing switcher information for `pdlblr` + // instruction (DDC is used as it contains the address where the capability + // is stored in this particular example) + mrs c1, DDC + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + ldpblr c29, [c1] + + ldr clr, [sp], #16 + + ret clr +comp_f_fn_end: + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret + diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e9836d3..4e5309e 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -62,9 +62,12 @@ function run { # Tests that should fail run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok run to_fail hybrid ddc_invalid ddc_null +run to_fail hybrid/compartment_examples/inter_comp_call/secure-try_deref main # Tests that should pass run OK hybrid/ddc_compartment_switching ddc_compartment_switching run OK hybrid basic_ddc +run OK hybrid/compartment_examples/inter_comp_call/base main +run OK hybrid/compartment_examples/inter_comp_call/secure main # TODO: 'timsort' works, but takes a very long time. Is it useful to test a # smaller data set? From f0b26c193c71b248e83f16deb6fb0c79d2811376 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Thu, 13 Jan 2022 10:59:12 +0000 Subject: [PATCH 69/98] Fix `switch_compartment` PCC bounds A hardcoded value of the bounds over `switch_compartment` meant that the bounds were much wider than expected. This meant two things: - the `clean` function called within `switch_compartment` was reachable and executable; - when deriving the `clr` within `switch_compartment`, we would derive with the larger bound, and return to a compartment with much more executable permissions than intended. We now tightly bind the PCC to `switch_compartment`. This means moving `clean` within these bounds (a better way would be to create capabilities allowing this function to be called, and provide local copies to each compartment, but that is beyond the scope of this example, and an exercise in engineering), and not deriving `clr` within `switch_compartment`, but just retaining the provided `clr`. Additional small cleanups, such as code-base splitting, and comment-fixing. --- .../secure-try_deref/Makefile.morello-hybrid | 2 +- .../secure-try_deref/compartments-try_deref.s | 31 +++ .../inter_comp_call/secure-try_deref/main.c | 10 +- .../inter_comp_call/secure-try_deref/shared.S | 29 +++ ...hared_try_deref.S => switch_compartment.s} | 68 +------ .../inter_comp_call/secure/compartments.s | 38 ++++ .../inter_comp_call/secure/main.c | 10 +- .../inter_comp_call/secure/shared.S | 181 +----------------- .../secure/switch_compartment.s | 125 ++++++++++++ 9 files changed, 245 insertions(+), 249 deletions(-) create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S rename hybrid/compartment_examples/inter_comp_call/secure-try_deref/{shared_try_deref.S => switch_compartment.s} (69%) create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/compartments.s create mode 100644 hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid index b8e60df..79b20f2 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid @@ -1,7 +1,7 @@ # Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 -SHARED_SOURCES := shared_try_deref.s +SHARED_SOURCES := shared.S CFILES := $(wildcard *.c) ROOTDIR=../../../.. diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s new file mode 100644 index 0000000..2d86023 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s @@ -0,0 +1,31 @@ +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ +comp_f_fn: + // Retrieve local capability containing switcher information + mrs c1, DDC + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + + // Try to dereference it to retrieve switcher DDC; this is expected to fail + // due to the local capability being sealed (`main.c:143`). + ldr c1, [c1] + + ldr clr, [sp], #16 + + ret clr +comp_f_fn_end: + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c index 12475c6..0e20bb7 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c @@ -61,8 +61,9 @@ extern void executive_switch(void *__capability); extern int switch_compartment(); extern void comp_f_fn(); extern void comp_g_fn(); -extern void *comp_f_fn_end; -extern void *comp_g_fn_end; +extern void comp_f_fn_end(); +extern void comp_g_fn_end(); +extern void switch_compartment_end(); /******************************************************************************* * Types & Consts @@ -133,7 +134,8 @@ struct comp comps[COMP_COUNT]; void init_comps() { void *__capability switch_cap = (void *__capability) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); switcher_caps[1] = switch_cap; void *__capability comps_addr = (void *__capability) &comps; @@ -173,8 +175,6 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) // Set up a capability pointing to the function we want to call within the // compartment. This will be loaded as the PCC when the function is called. void *__capability comp_fn = (void *__capability) _comp_fn; - // 40 is arbitary; meant to be the size of the executable function within - // compartment size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); new_comp.comp_fn = comp_fn; diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S new file mode 100644 index 0000000..5036672 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S @@ -0,0 +1,29 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include "main.h" + +// Compartment functions +.global comp_f_fn +.global comp_g_fn + +// Labels for size computations +.global comp_f_fn_end +.global comp_g_fn_end +.global switch_compartment_end + +.text +.balign 4 + +.global executive_switch +.type executive_switch, "function" +executive_switch: + mov c29, c0 + mov x0, #0 + cvtp clr, lr + b switch_compartment + ret clr + +#include "switch_compartment.s" + +#include "compartments-try_deref.s" diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s similarity index 69% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S rename to hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s index 6e37606..a9557b9 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared_try_deref.S +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s @@ -1,23 +1,3 @@ -// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. -// SPDX-License-Identifier: MIT OR Apache-2.0 - -#include "main.h" - -.global comp_f_fn -.global comp_g_fn -.global comp_f_fn_end -.global comp_g_fn_end - -.text -.balign 4 - -.global executive_switch -.type executive_switch, "function" -executive_switch: - mov c29, c0 - mov x0, #0 - bl switch_compartment - /* The compartment switch function. Expects compartment information to be * stored in memory (defined by the capability stored in register `c29`). * Performs a compartment switch based on the id saved in `x0` (currently just @@ -52,14 +32,11 @@ switch_compartment: ldr x11, [x29, x11] mov sp, x11 - // Derive a new clr to restore PCC, and store it. - cvtp c11, lr - // Install compartment DDC msr DDC, c1 - // Save old DDC (c2), old SP (x12), old LR (c11) on stack - stp c2, c11, [sp, #-48]! + // Save old DDC (c2), old SP (x12), old LR (clr) on stack + stp c2, clr, [sp, #-48]! str x12, [sp, #32] // Stack layout at this point: @@ -95,46 +72,6 @@ switch_compartment: ret clr -.type get_comp_switcher_ref, "function" -get_comp_switcher_ref: - mov x1, #5000 - sub x1, x1, #16 - add c0, c0, x1 - ret - -/* Compartment from which we call the switcher to perform inter-compartment - * transition. The call is via a capability, to update the PCC bounds - * appropriately to cover `switch_compartment`. - */ -.type comp_f_fn, "function" -comp_f_fn: - // Retrieve local capability containing switcher information - mrs c1, DDC - gclim x1, c1 - sub x1, x1, #16 - ldr c1, [x1] - - // Try to dereference it to retrieve switcher DDC; this is expected to fail - // due to the local capability being sealed (`main.c:143`). - ldr c1, [c1] - - ldr clr, [sp], #16 - - ret clr -comp_f_fn_end: - -/* The function in this compartment just writes to some memory within its - * bounds, to ensure it is properly called. - */ -.type comp_g_fn, "function" -comp_g_fn: - mrs c10, DDC - mov x11, 42 - str x11, [x10, #4000] - - ret clr -comp_g_fn_end: - // Inner helper for cleaning capabilities from registers, either side of an // AAPCS64 function call where some level of distrust exists between caller // and callee. @@ -184,4 +121,5 @@ clean: // We need LR (x30) to return. The call to this helper already cleaned it. // Don't replace SP; this needs special handling by the caller anyway. ret +switch_compartment_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure/compartments.s b/hybrid/compartment_examples/inter_comp_call/secure/compartments.s new file mode 100644 index 0000000..f482ac4 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/compartments.s @@ -0,0 +1,38 @@ +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ +.type comp_f_fn, "function" +comp_f_fn: + // Set compartment ID we want to switch to + mov x0, #1 + + // Store the `clr` for exitting `comp_f_fn`; this is overwritten by + // `switch_compartment`. + str clr, [sp, #-16]! + + // Retrieve local capability containing switcher information for `pdlblr` + // instruction (DDC is used as it contains the address where the capability + // is stored in this particular example) + mrs c1, DDC + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + ldpblr c29, [c1] + + ldr clr, [sp], #16 + + ret clr +comp_f_fn_end: + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.c b/hybrid/compartment_examples/inter_comp_call/secure/main.c index 12475c6..0e20bb7 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/main.c +++ b/hybrid/compartment_examples/inter_comp_call/secure/main.c @@ -61,8 +61,9 @@ extern void executive_switch(void *__capability); extern int switch_compartment(); extern void comp_f_fn(); extern void comp_g_fn(); -extern void *comp_f_fn_end; -extern void *comp_g_fn_end; +extern void comp_f_fn_end(); +extern void comp_g_fn_end(); +extern void switch_compartment_end(); /******************************************************************************* * Types & Consts @@ -133,7 +134,8 @@ struct comp comps[COMP_COUNT]; void init_comps() { void *__capability switch_cap = (void *__capability) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); switcher_caps[1] = switch_cap; void *__capability comps_addr = (void *__capability) &comps; @@ -173,8 +175,6 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) // Set up a capability pointing to the function we want to call within the // compartment. This will be loaded as the PCC when the function is called. void *__capability comp_fn = (void *__capability) _comp_fn; - // 40 is arbitary; meant to be the size of the executable function within - // compartment size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); new_comp.comp_fn = comp_fn; diff --git a/hybrid/compartment_examples/inter_comp_call/secure/shared.S b/hybrid/compartment_examples/inter_comp_call/secure/shared.S index a863750..da7dd6b 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/secure/shared.S @@ -3,10 +3,14 @@ #include "main.h" +// Compartment functions .global comp_f_fn .global comp_g_fn + +// Labels for size computations .global comp_f_fn_end .global comp_g_fn_end +.global switch_compartment_end .text .balign 4 @@ -16,179 +20,10 @@ executive_switch: mov c29, c0 mov x0, #0 + cvtp clr, lr b switch_compartment - ret - -/* The compartment switch function. Expects compartment information to be - * stored in memory (defined by the capability stored in register `c29`). - * Performs a compartment switch based on the id saved in `x0` (currently just - * an integer index into the `comps` array). - */ -.global switch_compartment -.type switch_compartment, "function" -switch_compartment: - // Store entering compartment's DDC, and move to memory containing - // compartment info - mrs c2, DDC - mov x10, x0 - - // Expect switcher DDC in c29 - msr DDC, c29 - - // Get compartment to switch to data - mov x11, #COMP_SIZE - mul x10, x10, x11 - - // Load PCC, including function we are jumping to within compartment - add x11, x10, #COMP_OFFSET_PCC - ldr c0, [x29, x11] - - // Load DDC - add x11, x10, #COMP_OFFSET_DDC - ldr c1, [x29, x11] - - // Setup SP - mov x12, sp - add x11, x10, #COMP_OFFSET_STK_ADDR - ldr x11, [x29, x11] - mov sp, x11 - - // Derive a new clr to restore PCC, and store it. - cvtp c11, lr - - // Install compartment DDC - msr DDC, c1 - - // Save old DDC (c2), old SP (x12), old LR (c11) on stack - stp c2, c11, [sp, #-48]! - str x12, [sp, #32] - - // Stack layout at this point: - // - // `stack + size` -> ________________________ - // sp + 40 -> [ ] ^ - // sp + 32 -> [ old SP ] | - // sp + 24 -> [ old CLR (hi64) ] | - // sp + 16 -> [ old CLR (lo64) ] | - // sp + 8 -> [ old DDC (high 64) ] | DDC bounds - // sp + 0 -> [ old DDC (low 64) ] | - // : : - // `stack` -> ________________________v - - // Clean all registers, except register used to call function within - // compartment we are transitioning to - bl clean+4 - - // Jump to the function within the compartment we are switching to (this - // also sets PCC) - blr c0 - - // Clean capabilities left in the return value. - mov w0, w0 - bl clean - - // Restore the caller's context and compartment. - ldp c10, clr, [sp] - ldr x12, [sp, #32] - msr DDC, c10 - mov x10, #0 - mov sp, x12 - - ret clr - -.type get_comp_switcher_ref, "function" -get_comp_switcher_ref: - mov x1, #5000 - sub x1, x1, #16 - add c0, c0, x1 - ret - -/* Compartment from which we call the switcher to perform inter-compartment - * transition. The call is via a capability, to update the PCC bounds - * appropriately to cover `switch_compartment`. - */ -.type comp_f_fn, "function" -comp_f_fn: - // Set compartment ID we want to switch to - mov x0, #1 - - // Store the `clr` for exitting `comp_f_fn`; this is overwritten by - // `switch_compartment`. - str clr, [sp, #-16]! - - // Retrieve local capability containing switcher information for `pdlblr` - // instruction (DDC is used as it contains the address where the capability - // is stored in this particular example) - mrs c1, DDC - gclim x1, c1 - sub x1, x1, #16 - ldr c1, [x1] - ldpblr c29, [c1] - - ldr clr, [sp], #16 - - ret clr -comp_f_fn_end: - -/* The function in this compartment just writes to some memory within its - * bounds, to ensure it is properly called. - */ -.type comp_g_fn, "function" -comp_g_fn: - mrs c10, DDC - mov x11, 42 - str x11, [x10, #4000] - - ret clr -comp_g_fn_end: + ret clr - // Inner helper for cleaning capabilities from registers, either side of an - // AAPCS64 function call where some level of distrust exists between caller - // and callee. - // - // Depending on the trust model, this might not be required, but the process - // is included here for demonstration purposes. Note that if data needs to - // be scrubbed as well as capabilities, then NEON registers also need to be - // cleaned. - // - // Callers should enter at an appropriate offset so that live registers - // holding arguments and return values (c0-c7) are preserved. -clean: - mov x0, #0 - mov x1, #0 - mov x2, #0 - mov x3, #0 - mov x4, #0 - mov x5, #0 - mov x6, #0 - mov x7, #0 - mov x8, #0 - mov x9, #0 - mov x10, #0 - mov x11, #0 - mov x12, #0 - mov x13, #0 - mov x14, #0 - mov x15, #0 - mov x16, #0 - mov x17, #0 - // x18 is the "platform register" (for some platforms). If so, it needs to - // be preserved, but here we assume that only the lower 64 bits are - // required. - mov x18, x18 - // x19-x29 are callee-saved, but only the lower 64 bits. - mov x19, x19 - mov x20, x20 - mov x21, x21 - mov x22, x22 - mov x23, x23 - mov x24, x24 - mov x25, x25 - mov x26, x26 - mov x27, x27 - mov x28, x28 - mov x29, x29 // FP - // We need LR (x30) to return. The call to this helper already cleaned it. - // Don't replace SP; this needs special handling by the caller anyway. - ret +#include "switch_compartment.s" +#include "compartments.s" diff --git a/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s b/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s new file mode 100644 index 0000000..5db007a --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s @@ -0,0 +1,125 @@ +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c29`). + * Performs a compartment switch based on the id saved in `x0` (currently just + * an integer index into the `comps` array). + */ +.global switch_compartment +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old CLR (clr) on stack + stp c2, clr, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, except register used to call function within + // compartment we are transitioning to + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret +switch_compartment_end: + From 738f8e4b514ea0f2edb1d5a402bbbae425479ca9 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Thu, 20 Jan 2022 10:51:35 +0000 Subject: [PATCH 70/98] Add two negative examples Adds two initial negative examples, expected to fail to show that the security works. These contain: * redirect_clr writes a capability to the clr pointing to some malicious code, in order for switch_compartment to jump to that code on return; * update_ddc leaks the address of the DDC capability used by switch_compartment, allowing a container to attempt to load, modify, and update this capability. --- hybrid/compartment_examples/comp_setup/main.h | 6 - .../inter_comp_call/base/main.c | 2 +- .../inter_comp_call/base/shared.S | 2 +- .../{base/main.h => include/base.h} | 0 .../main.h => include/secure.h} | 0 .../Makefile.morello-hybrid | 10 + .../compartments-redirect_clr.S | 51 +++++ .../secure-redirect_clr/main.c | 174 ++++++++++++++++++ .../secure-redirect_clr/shared.S | 14 ++ .../secure-redirect_clr/switch_compartment.S | 130 +++++++++++++ .../inter_comp_call/secure-try_deref/main.c | 2 +- .../inter_comp_call/secure-try_deref/shared.S | 2 +- .../secure-update_ddc/Makefile.morello-hybrid | 10 + .../compartments-update_ddc.S | 53 ++++++ .../inter_comp_call/secure-update_ddc/main.c | 169 +++++++++++++++++ .../secure-update_ddc/shared-update_ddc.S | 15 ++ .../switch_compartment-update_ddc.S | 130 +++++++++++++ .../inter_comp_call/secure/main.c | 2 +- .../inter_comp_call/secure/main.h | 6 - .../inter_comp_call/secure/shared.S | 2 +- tests/run_tests.sh | 2 + 21 files changed, 764 insertions(+), 18 deletions(-) delete mode 100644 hybrid/compartment_examples/comp_setup/main.h rename hybrid/compartment_examples/inter_comp_call/{base/main.h => include/base.h} (100%) rename hybrid/compartment_examples/inter_comp_call/{secure-try_deref/main.h => include/secure.h} (100%) create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/compartments-redirect_clr.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/main.c create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S create mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure/main.h diff --git a/hybrid/compartment_examples/comp_setup/main.h b/hybrid/compartment_examples/comp_setup/main.h deleted file mode 100644 index b983e8f..0000000 --- a/hybrid/compartment_examples/comp_setup/main.h +++ /dev/null @@ -1,6 +0,0 @@ -#define COMP_COUNT 1 -#define COMP_SIZE 80 -#define COMP_OFFSET_STK_ADDR 8 -#define COMP_OFFSET_STK_LEN 16 -#define COMP_OFFSET_DDC 48 -#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/inter_comp_call/base/main.c b/hybrid/compartment_examples/inter_comp_call/base/main.c index ef4ada3..7cab5e0 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/main.c +++ b/hybrid/compartment_examples/inter_comp_call/base/main.c @@ -70,7 +70,7 @@ struct comp }; // ASM offsets, included here for validation -#include "main.h" +#include "../include/base.h" static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), diff --git a/hybrid/compartment_examples/inter_comp_call/base/shared.S b/hybrid/compartment_examples/inter_comp_call/base/shared.S index 9d53164..e45618f 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/base/shared.S @@ -1,7 +1,7 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "main.h" +#include "../include/base.h" .global comp_f_fn .global comp_g_fn diff --git a/hybrid/compartment_examples/inter_comp_call/base/main.h b/hybrid/compartment_examples/inter_comp_call/include/base.h similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/base/main.h rename to hybrid/compartment_examples/inter_comp_call/include/base.h diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h b/hybrid/compartment_examples/inter_comp_call/include/secure.h similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.h rename to hybrid/compartment_examples/inter_comp_call/include/secure.h diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid new file mode 100644 index 0000000..0eb03fd --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := $(wildcard *.S) +HFILES := $(wildcard ../include/*.h) $(wildcard ../../../include/*.h) $(wildcard ../../../../include/*.h) +ROOTDIR=../../../.. + +include $(ROOTDIR)/build/Makefile.vars.morello-hybrid +include $(ROOTDIR)/build/Makefile.vars.common +include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/compartments-redirect_clr.S b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/compartments-redirect_clr.S new file mode 100644 index 0000000..562edbf --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/compartments-redirect_clr.S @@ -0,0 +1,51 @@ +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ + +// Compartment functions +.global comp_f_fn +.global comp_g_fn + +// Labels for size computations +.global comp_f_fn_end +.global comp_g_fn_end + +.type comp_f_fn, "function" +comp_f_fn: + // Set compartment ID we want to switch to + mov x0, #1 + + // Try to set the CLR to jump to a malicious function + adr x10, comp_f_malicious + cvtp clr, x10 + + // Retrieve local capability containing switcher information for `pdlbr` + // instruction (DDC is used as it contains the address where the capability + // is stored in this particular example) + mrs c1, DDC + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + // We use `ldpbr` to not overwrite the `clr`, and use our maliciously + // installed one + ldpbr c29, [c1] + + ret clr +comp_f_fn_end: + +comp_f_malicious: + mov x0, #2000 + ret + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/main.c b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/main.c new file mode 100644 index 0000000..42a38be --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/main.c @@ -0,0 +1,174 @@ +/* This example attempts to set a `clr` (`cvtp` instruction in `comp_f_fn` in + * `compartments.S`) which would jump to a malicious function + * (`comp_f_malicious` in `compartments.S`) after `switch_compartment` returns. + * The execution faults when entering the malicious function via returning from + * `switch_compartment` (final `ret` of `switch_compartment()`) as the bounds + * on the capability loaded into `clr` are inherited from the PCC of the + * compartment via the `cvtp` instruction, being the only way the compartment + * itself can create new capabilities from scratch (assuming no access to other + * capabilities). + * + * Note that `compartments.S` refers to `compartments-redirect_clr.S`. + */ + +#include "../../../../include/common.h" +#include "../../../include/utils.h" + +#include +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern void executive_switch(void *__capability); +extern int switch_compartment(); +extern void comp_f_fn(); +extern void comp_g_fn(); +extern void comp_f_fn_end(); +extern void comp_g_fn_end(); +extern void switch_compartment_end(); + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 4992; +size_t id = 0; + +/* Capabilities representing DDC and PCC of the switcher, stored in + * `switcher_caps`. They are saved successively to facilitate calling via + * `ldpblr`, with the first being the DDC, and the second, the PCC. + */ +const size_t switcher_caps_count = 2; +void *__capability switcher_caps[switcher_caps_count]; + +/* Capability required by compartments to call `switch_compartment` securely. + * Duplicated in each compartment. */ +void *__capability switcher_call; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment memory area (also lowest address + * of the stack) (8B) + * - address to the start (highest address) of the compartment's stack + * (corresponding to the lowest address of the heap) (8B) + * - the size of the stack (8B) + * - the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "../include/secure.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* Create and save required capabilities. Currently, this means: + * - PCC of `switch_compartment` + * - DDC of `switch_compartment` + * - capability to allow compartments to call `switch_compartment` via `lpdblr` + */ +void init_comps() +{ + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) +{ + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throughout the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + init_comps(); + + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + + executive_switch(switcher_caps[0]); + + // Check compartment did indeed execute + assert(comp_g[4000] == 42); +} diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S new file mode 100644 index 0000000..7bdb6dd --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S @@ -0,0 +1,14 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +.text +.balign 4 + +.global executive_switch +.type executive_switch, "function" +executive_switch: + mov c29, c0 + mov x0, #0 + cvtp clr, lr + b switch_compartment + ret clr diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S new file mode 100644 index 0000000..b61f908 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S @@ -0,0 +1,130 @@ +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c29`). + * Performs a compartment switch based on the id saved in `x0` (currently just + * an integer index into the `comps` array). + */ + +#include "../include/secure.h" + +.global switch_compartment +.global switch_compartment_end + +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old CLR (clr) on stack + stp c2, clr, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, except register used to call function within + // compartment we are transitioning to + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret +switch_compartment_end: + diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c index 0e20bb7..485c476 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c @@ -109,7 +109,7 @@ struct comp }; // ASM offsets, included here for validation -#include "main.h" +#include "../include/secure.h" static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S index 5036672..b44cb99 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S @@ -1,7 +1,7 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "main.h" +#include "../include/secure.h" // Compartment functions .global comp_f_fn diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid new file mode 100644 index 0000000..0eb03fd --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid @@ -0,0 +1,10 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +SHARED_SOURCES := $(wildcard *.S) +HFILES := $(wildcard ../include/*.h) $(wildcard ../../../include/*.h) $(wildcard ../../../../include/*.h) +ROOTDIR=../../../.. + +include $(ROOTDIR)/build/Makefile.vars.morello-hybrid +include $(ROOTDIR)/build/Makefile.vars.common +include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S new file mode 100644 index 0000000..a0fa0e4 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S @@ -0,0 +1,53 @@ +/* Compartment from which we call the switcher to perform inter-compartment + * transition. The call is via a capability, to update the PCC bounds + * appropriately to cover `switch_compartment`. + */ + +// Compartment functions +.global comp_f_fn +.global comp_g_fn + +// Labels for size computations +.global comp_f_fn_end +.global comp_g_fn_end + +.type comp_f_fn, "function" +comp_f_fn: + // Set compartment ID we want to switch to + mov x0, #1 + + // Store the `clr` for exitting `comp_f_fn`; this is overwritten by + // `switch_compartment`. + str clr, [sp, #-16]! + + // Attempt to modify DDC of `switch_compartment` + mrs c1, DDC + ldr c2, [x20] + gcvalue x3, c1 + scvalue c2, c2, x3 + str c2, [x20] + + // Retrieve local capability containing switcher information for `pdlblr` + // instruction (DDC is used as it contains the address where the capability + // is stored in this particular example) + gclim x1, c1 + sub x1, x1, #16 + ldr c1, [x1] + ldpblr c29, [c1] + + ldr clr, [sp], #16 + + ret clr +comp_f_fn_end: + +/* The function in this compartment just writes to some memory within its + * bounds, to ensure it is properly called. + */ +.type comp_g_fn, "function" +comp_g_fn: + mrs c10, DDC + mov x11, 42 + str x11, [x10, #4000] + + ret clr +comp_g_fn_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c new file mode 100644 index 0000000..8b5da7f --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c @@ -0,0 +1,169 @@ +/* This example attempts to modify the DDC of `switch_compartment`, assuming it + * knows the address where it resides (leaked via `c20` in `executive_switch()` + * in `shared-update_ddc.S`, and excluded from the `clean()` call in + * `switch_compartment()). However, as it lies outside the DDC of the + * compartment, attempting to load from the respective memory address causes a + * fault (as attempted in `comp_f_fn` of `compartments-update_ddc.S`). + */ + +#include "../../../../include/common.h" +#include "../../../include/utils.h" + +#include +#include +#include +#include +#include +#include + +#if !defined(__CHERI_CAPABILITY_WIDTH__) || defined(__CHERI_PURE_CAPABILITY__) +#error "This example only works on CHERI hybrid mode" +#endif + +/******************************************************************************* + * Assembly Functions & Consts + ******************************************************************************/ + +extern void executive_switch(void *__capability); +extern int switch_compartment(); +extern void comp_f_fn(); +extern void comp_g_fn(); +extern void comp_f_fn_end(); +extern void comp_g_fn_end(); +extern void switch_compartment_end(); + +/******************************************************************************* + * Types & Consts + ******************************************************************************/ + +const size_t comp_stack_size = 2000; +const size_t total_comp_size = 4992; +size_t id = 0; + +/* Capabilities representing DDC and PCC of the switcher, stored in + * `switcher_caps`. They are saved successively to facilitate calling via + * `ldpblr`, with the first being the DDC, and the second, the PCC. + */ +const size_t switcher_caps_count = 2; +void *__capability switcher_caps[switcher_caps_count]; + +/* Capability required by compartments to call `switch_compartment` securely. + * Duplicated in each compartment. */ +void *__capability switcher_call; + +/* Abstract representation of a compartment. Within 80 bytes we represent: + * - an id (8B) + * - address to the start of the compartment memory area (also lowest address + * of the stack) (8B) + * - address to the start (highest address) of the compartment's stack + * (corresponding to the lowest address of the heap) (8B) + * - the size of the stack (8B) + * - the size of the heap (8B) + * - alignment padding (8B) + * - the ddc corresponding to the compartment (16B) + * - the function within the compartment to be executed upon switching (16B) + */ +struct comp +{ + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; +}; + +// ASM offsets, included here for validation +#include "../include/secure.h" + +static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); +static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), + "Invalid `COMP_OFFSET_STK_ADDR` provided."); +static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), + "Invalid `COMP_OFFSET_STK_LEN` provided."); +static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); +static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), + "Invalid `COMP_OFFSET_PCC` provided."); + +struct comp comps[COMP_COUNT]; + +/******************************************************************************* + * Privileged Functions + ******************************************************************************/ + +/* Create and save required capabilities. Currently, this means: + * - PCC of `switch_compartment` + * - DDC of `switch_compartment` + * - capability to allow compartments to call `switch_compartment` via `lpdblr` + */ +void init_comps() +{ + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); +} + +void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) +{ + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throughout the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; +} + +/******************************************************************************* + * Main + ******************************************************************************/ + +int main() +{ + init_comps(); + + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + + executive_switch(switcher_caps[0]); + + // Check compartment did indeed execute + assert(comp_g[4000] == 42); +} diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S new file mode 100644 index 0000000..4148c9d --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S @@ -0,0 +1,15 @@ +// Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +.text +.balign 4 + +.global executive_switch +.type executive_switch, "function" +executive_switch: + mov c20, c0 + mov c29, c0 + mov x0, #0 + cvtp clr, lr + b switch_compartment + ret clr diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S new file mode 100644 index 0000000..25b5026 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S @@ -0,0 +1,130 @@ +/* The compartment switch function. Expects compartment information to be + * stored in memory (defined by the capability stored in register `c29`). + * Performs a compartment switch based on the id saved in `x0` (currently just + * an integer index into the `comps` array). + */ + +#include "../include/secure.h" + +.global switch_compartment +.global switch_compartment_end + +.type switch_compartment, "function" +switch_compartment: + // Store entering compartment's DDC, and move to memory containing + // compartment info + mrs c2, DDC + mov x10, x0 + + // Expect switcher DDC in c29 + msr DDC, c29 + + // Get compartment to switch to data + mov x11, #COMP_SIZE + mul x10, x10, x11 + + // Load PCC, including function we are jumping to within compartment + add x11, x10, #COMP_OFFSET_PCC + ldr c0, [x29, x11] + + // Load DDC + add x11, x10, #COMP_OFFSET_DDC + ldr c1, [x29, x11] + + // Setup SP + mov x12, sp + add x11, x10, #COMP_OFFSET_STK_ADDR + ldr x11, [x29, x11] + mov sp, x11 + + // Install compartment DDC + msr DDC, c1 + + // Save old DDC (c2), old SP (x12), old CLR (clr) on stack + stp c2, clr, [sp, #-48]! + str x12, [sp, #32] + + // Stack layout at this point: + // + // `stack + size` -> ________________________ + // sp + 40 -> [ ] ^ + // sp + 32 -> [ old SP ] | + // sp + 24 -> [ old CLR (hi64) ] | + // sp + 16 -> [ old CLR (lo64) ] | + // sp + 8 -> [ old DDC (high 64) ] | DDC bounds + // sp + 0 -> [ old DDC (low 64) ] | + // : : + // `stack` -> ________________________v + + // Clean all registers, but leak address of switcher DDC in `x20` + bl clean+4 + + // Jump to the function within the compartment we are switching to (this + // also sets PCC) + blr c0 + + // Clean capabilities left in the return value. + mov w0, w0 + bl clean + + // Restore the caller's context and compartment. + ldp c10, clr, [sp] + ldr x12, [sp, #32] + msr DDC, c10 + mov x10, #0 + mov sp, x12 + + ret clr + + // Inner helper for cleaning capabilities from registers, either side of an + // AAPCS64 function call where some level of distrust exists between caller + // and callee. + // + // Depending on the trust model, this might not be required, but the process + // is included here for demonstration purposes. Note that if data needs to + // be scrubbed as well as capabilities, then NEON registers also need to be + // cleaned. + // + // Callers should enter at an appropriate offset so that live registers + // holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + // Simulate leaking adress of switcher DDC + // mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret +switch_compartment_end: + diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.c b/hybrid/compartment_examples/inter_comp_call/secure/main.c index 0e20bb7..485c476 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/main.c +++ b/hybrid/compartment_examples/inter_comp_call/secure/main.c @@ -109,7 +109,7 @@ struct comp }; // ASM offsets, included here for validation -#include "main.h" +#include "../include/secure.h" static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.h b/hybrid/compartment_examples/inter_comp_call/secure/main.h deleted file mode 100644 index 50a8dc8..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure/main.h +++ /dev/null @@ -1,6 +0,0 @@ -#define COMP_COUNT 2 -#define COMP_SIZE 80 -#define COMP_OFFSET_STK_ADDR 16 -#define COMP_OFFSET_STK_LEN 24 -#define COMP_OFFSET_DDC 48 -#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/inter_comp_call/secure/shared.S b/hybrid/compartment_examples/inter_comp_call/secure/shared.S index da7dd6b..29b105d 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/secure/shared.S @@ -1,7 +1,7 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "main.h" +#include "../include/secure.h" // Compartment functions .global comp_f_fn diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 4e5309e..267cbef 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -63,6 +63,8 @@ function run { run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok run to_fail hybrid ddc_invalid ddc_null run to_fail hybrid/compartment_examples/inter_comp_call/secure-try_deref main +run to_fail hybrid/compartment_examples/inter_comp_call/secure-redirect_clr main +run to_fail hybrid/compartment_examples/inter_comp_call/secure-update_ddc main # Tests that should pass run OK hybrid/ddc_compartment_switching ddc_compartment_switching run OK hybrid basic_ddc From e3705fa31acf22f77f71ee8dcfb6dc974d7557d1 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Wed, 26 Jan 2022 16:09:11 +0000 Subject: [PATCH 71/98] Add utility function and assertions to verify PCC bounds in shared objects We want to ensure there are no situations where the PCC covers more than one function at a certain point in the program. --- .buildbot.sh | 51 ++++++++++++++------ shared_objects/build/Makefile.shared_objects | 8 +-- shared_objects/include/find_sentries.h | 17 +++++++ shared_objects/include/static_function.h | 5 ++ shared_objects/main.c | 1 + shared_objects/pcc_bounds_check_main.c | 14 ++++++ shared_objects/static_function.c | 39 +++++++++++++++ shared_objects/static_variable.c | 7 +++ tests/run_cheri_examples.py | 3 +- tests/run_tests.sh | 37 ++++++++------ 10 files changed, 147 insertions(+), 35 deletions(-) create mode 100644 shared_objects/include/static_function.h create mode 100644 shared_objects/pcc_bounds_check_main.c create mode 100644 shared_objects/static_function.c diff --git a/.buildbot.sh b/.buildbot.sh index 33c9322..dc9e7f3 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -8,6 +8,7 @@ set -e export CHERIBASE=~/cheri CHERIBUILD=~/build +export TEST_TARGET=$1 echo "Checking clang-format..." # Note that `sdk` and `morello-sdk` contain different versions of clang-format, @@ -31,27 +32,47 @@ echo "Checking that all the hybrid examples build on Morello..." platform='morello-hybrid' for dir in hybrid hybrid/ddc_compartment_switching; do pushd "$dir" - make -f Makefile.$platform clean - make -f Makefile.$platform all + make -f Makefile.$platform clean + make -f Makefile.$platform all popd done export SSHPORT=10021 export PYTHONPATH="$CHERIBUILD"/test-scripts -echo "Running tests for 'morello-hybrid' using QEMU..." -args=( - --architecture morello-hybrid - # Qemu System to use - --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello - --bios edk2-aarch64-code.fd - --disk-image $HOME/cheri/output/cheribsd-morello-hybrid.img - # Required build-dir in CheriBSD - --build-dir . - --ssh-port $SSHPORT - --ssh-key $HOME/.ssh/id_ed25519.pub +if [ "$1" = "riscv64" ]; then + echo "Running tests for 'riscv64-purecap' using QEMU." + args=( + --architecture riscv64 + # Qemu System to use + --qemu-cmd $HOME/cheri/output/sdk/bin/qemu-system-riscv64cheri + --kernel $HOME/cheri/output/rootfs-riscv64-purecap/boot/kernel/kernel + --bios bbl-riscv64cheri-virt-fw_jump.bin + --disk-image $HOME/cheri/output/cheribsd-riscv64-purecap.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub + ) + BUILDBOT_PLATFORM=riscv64-purecap python3 tests/run_cheri_examples.py "${args[@]}" +elif [ "$1" = "morello-hybrid" ]; then + echo "Running tests for 'morello-hybrid' using QEMU..." + args=( + --architecture morello-hybrid + # Qemu System to use + --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello + --bios edk2-aarch64-code.fd + --disk-image $HOME/cheri/output/cheribsd-morello-hybrid.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub ) -BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" + BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" +else + echo "$1 not supported (yet)." + exit 1 +fi # TODO: Run tests for `morello-purecap` and `riscv64-purecap` too -# This is related to https://github.com/CTSRD-CHERI/cheribuild/issues/182 \ No newline at end of file +# This is related to https://github.com/CTSRD-CHERI/cheribuild/issues/182 diff --git a/shared_objects/build/Makefile.shared_objects b/shared_objects/build/Makefile.shared_objects index 4cb10a3..b105706 100644 --- a/shared_objects/build/Makefile.shared_objects +++ b/shared_objects/build/Makefile.shared_objects @@ -16,7 +16,7 @@ endif CFILES := $(wildcard *.c) HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) -LIBNAMES := static_variable unexported_function compartment_per_object +LIBNAMES := static_variable unexported_function compartment_per_object static_function SOFILES := $(patsubst %,$(BINDIR)/lib%.so,$(LIBNAMES)) .PHONY: all clang-format clean run-shared_objects @@ -32,15 +32,15 @@ clean: rmdir bin --ignore-fail-on-non-empty; \ fi -$(BINDIR)/main: main.c $(HFILES) $(SOFILES) +$(BINDIR)/%: %.c $(HFILES) $(SOFILES) @mkdir -p $(BINDIR) - $(CC) $(CFLAGS) -fpie -L $(BINDIR) -Wl,-rpath,. -ldl $(patsubst %,-l%,$(LIBNAMES)) $< -o $(BINDIR)/main + $(CC) $(CFLAGS) -fpie -L $(BINDIR) -Wl,-rpath,. -ldl $(patsubst %,-l%,$(LIBNAMES)) $< -o $@ $(BINDIR)/lib%.so: %.c $(HFILES) @mkdir -p $(BINDIR) $(CC) $(CFLAGS) -fPIC -shared $< -o $@ -run-shared_objects: $(BINDIR)/main $(SOFILES) +run-shared_objects-%: $(BINDIR)/% $(SOFILES) ifdef SSHPORT ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' scp $(SCP_OPTIONS) -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index 9278bc9..5d90a10 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -1,3 +1,8 @@ +/* + * Some utility functions to check range of addresses + * and whether a capability is the only one in the PCC + */ + #include #include "../../include/common.h" @@ -39,3 +44,15 @@ bool scan_range(void *ptr, void *exact) } return found; } + +/** + * Check whether ptr1 and ptr2 share the same base and + * bounds. If the latter is true and, e.g., ptr1 is the PCC, + * then the PCC contains only ptr2. + */ +bool base_and_bounds_eq(void *__capability ptr1, void *__capability ptr2) +{ + void *__capability pcc = cheri_pcc_get(); + return cheri_base_get(ptr1) == cheri_base_get(ptr2) && + cheri_length_get(ptr1) == cheri_length_get(ptr2); +} diff --git a/shared_objects/include/static_function.h b/shared_objects/include/static_function.h new file mode 100644 index 0000000..5f64857 --- /dev/null +++ b/shared_objects/include/static_function.h @@ -0,0 +1,5 @@ +static int static_function(); +static double another_static_function(); +void non_static_fun(); +void non_static_fun2(); +void check_static_and_non_static(); \ No newline at end of file diff --git a/shared_objects/main.c b/shared_objects/main.c index 527ad05..75c72e0 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -10,6 +10,7 @@ #include "include/compartment_per_object.h" #include "include/find_sentries.h" +#include "include/static_function.h" #include "include/static_variable.h" #include "include/unexported_function.h" diff --git a/shared_objects/pcc_bounds_check_main.c b/shared_objects/pcc_bounds_check_main.c new file mode 100644 index 0000000..d0838ff --- /dev/null +++ b/shared_objects/pcc_bounds_check_main.c @@ -0,0 +1,14 @@ +/** + * Check whether the PCC covers only one function + * in shared objects (static_variable, static_function) + */ +#include "include/find_sentries.h" +#include "include/static_function.h" +#include "include/static_variable.h" + +int main() +{ + increment(); + check_static_and_non_static(); + return 0; +} \ No newline at end of file diff --git a/shared_objects/static_function.c b/shared_objects/static_function.c new file mode 100644 index 0000000..2c7b03c --- /dev/null +++ b/shared_objects/static_function.c @@ -0,0 +1,39 @@ +#include "include/static_function.h" +#include "include/find_sentries.h" +#include +#include + +static int static_function() +{ + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + return 0; +} + +static double another_static_function() +{ + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + return 1.0; +} + +void non_static_fun() +{ + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + printf("Called static function with result: %d\n", static_function()); +} + +void non_static_fun2() +{ + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + printf("Called another function with result: %f\n", another_static_function()); +} + +void check_static_and_non_static() +{ + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + assert(base_and_bounds_eq(cheri_pcc_get(), &check_static_and_non_static)); +} diff --git a/shared_objects/static_variable.c b/shared_objects/static_variable.c index 1a434b1..73b8e38 100644 --- a/shared_objects/static_variable.c +++ b/shared_objects/static_variable.c @@ -1,19 +1,26 @@ /** * A basic example showing how CHERI sealed entries can be used to protect data. In this case the * cont variable will be inaccessible from the things that link with this shared library. + * Furthermore, each sentry when executed will be the only one in the PCC and so will have no + * access over its bounds. */ #include "include/static_variable.h" +#include "include/find_sentries.h" +#include static int32_t count = -5; void increment() { count += 1; + assert(base_and_bounds_eq(cheri_pcc_get(), &increment)); } int get_count() { + assert(base_and_bounds_eq(cheri_pcc_get(), &get_count)); + if (count > 0) { return count; diff --git a/tests/run_cheri_examples.py b/tests/run_cheri_examples.py index 6e45621..666726f 100755 --- a/tests/run_cheri_examples.py +++ b/tests/run_cheri_examples.py @@ -34,7 +34,8 @@ def run_cheri_examples_tests(qemu: boot_cheribsd.QemuCheriBSDInstance, args: arg boot_cheribsd.set_ld_library_path_with_sysroot(qemu) boot_cheribsd.info("Running tests for cheri-examples") - return os.system("./tests/run_tests.sh") == 0 + # Run the tests according to the architecture + return os.system(f"./tests/run_tests.sh {args.architecture}") == 0 if __name__ == '__main__': diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 267cbef..b9ecd6c 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -17,7 +17,7 @@ # - SSHUSER (defaults to 'root') # - SSHHOST (defaults to 'localhost') # -# TODO: Extend this to cover interactive examples and other architectures. +# TODO: Extend this to cover interactive examples and other architectures. # For now, we just test examples on `morello-hybrid`. set -e @@ -56,20 +56,27 @@ function run { # TODO: `mmap` also fails, but looks like it is intended to pass. -# TODO: PURECAP tests - -# HYBRID TESTS -# Tests that should fail -run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok -run to_fail hybrid ddc_invalid ddc_null -run to_fail hybrid/compartment_examples/inter_comp_call/secure-try_deref main -run to_fail hybrid/compartment_examples/inter_comp_call/secure-redirect_clr main -run to_fail hybrid/compartment_examples/inter_comp_call/secure-update_ddc main -# Tests that should pass -run OK hybrid/ddc_compartment_switching ddc_compartment_switching -run OK hybrid basic_ddc -run OK hybrid/compartment_examples/inter_comp_call/base main -run OK hybrid/compartment_examples/inter_comp_call/secure main +# PURECAP tests +# TODO: add previous examples +if [ "$1" = "riscv64" ]; then + run OK shared_objects shared_objects-pcc_bounds_check_main +elif [ "$1" = "morello-hybrid" ]; then + # HYBRID TESTS + # Tests that should fail + run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok + run to_fail hybrid ddc_invalid ddc_null + run to_fail hybrid/compartment_examples/inter_comp_call/secure-try_deref main + run to_fail hybrid/compartment_examples/inter_comp_call/secure-redirect_clr main + run to_fail hybrid/compartment_examples/inter_comp_call/secure-update_ddc main + # Tests that should pass + run OK hybrid/ddc_compartment_switching ddc_compartment_switching + run OK hybrid basic_ddc + run OK hybrid/compartment_examples/inter_comp_call/base main + run OK hybrid/compartment_examples/inter_comp_call/secure main +else + echo "$1 not supported (yet)." + exit 1 +fi # TODO: 'timsort' works, but takes a very long time. Is it useful to test a # smaller data set? From 0b51ab89f8d002848c6ca5824a10fdaa4a9297a1 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Wed, 16 Mar 2022 12:47:39 +0000 Subject: [PATCH 72/98] Add demonstration of CHERI_PERM_SYSCALL. --- .buildbot.sh | 4 +- syscall-restrict/Makefile.morello-hybrid | 6 ++ syscall-restrict/README.md | 38 +++++++++++ syscall-restrict/syscall-restrict.c | 87 ++++++++++++++++++++++++ tests/run_tests.sh | 1 + 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 syscall-restrict/Makefile.morello-hybrid create mode 100644 syscall-restrict/README.md create mode 100644 syscall-restrict/syscall-restrict.c diff --git a/.buildbot.sh b/.buildbot.sh index dc9e7f3..715ec27 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -1,7 +1,7 @@ #!/bin/bash # vim: expandtab sts=0 sw=4 smarttab # -# Copyright (c) 2020-2021 The CapableVMs "CHERI Examples" Contributors. +# Copyright (c) 2020-2022 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 set -e @@ -30,7 +30,7 @@ done echo "Checking that all the hybrid examples build on Morello..." platform='morello-hybrid' -for dir in hybrid hybrid/ddc_compartment_switching; do +for dir in hybrid hybrid/ddc_compartment_switching syscall-restrict; do pushd "$dir" make -f Makefile.$platform clean make -f Makefile.$platform all diff --git a/syscall-restrict/Makefile.morello-hybrid b/syscall-restrict/Makefile.morello-hybrid new file mode 100644 index 0000000..de962c4 --- /dev/null +++ b/syscall-restrict/Makefile.morello-hybrid @@ -0,0 +1,6 @@ +# Copyright (c) 2021-2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.morello-hybrid +include ../build/Makefile.vars.common +include ../build/Makefile.simple diff --git a/syscall-restrict/README.md b/syscall-restrict/README.md new file mode 100644 index 0000000..0c3374b --- /dev/null +++ b/syscall-restrict/README.md @@ -0,0 +1,38 @@ +# "syscall-restrict" example + +## Description + +This example demonstrates `CHERI_PERM_SYSCALL`. If this permission is absent +from the user-space caller's PCC, the CheriBSD kernel will reject all syscalls. + +Many syscalls have the potential to be a compartment escape. For example, +mmap-like syscalls can be used to change what is reachable by existing +capabilities (for example by changing virtual address mappings). In addition, +signal handlers cannot currently be safely registered by any compartment other +than the top level, root compartment. + +Finally, it may also be desirable to limit access to system-wide resources not +explicitly protected by virtual address compartmentalisation. Examples include +file systems, or file descriptors. + +Blocking access to syscalls permits a conservative, allow-list-based approach. +This example does not demonstrate such an allow-list, nor the necessary +compartmentalisation, but it demonstrates the behaviour of `CHERI_PERM_SYSCALL` +handling in the CheriBSD kernel. + +## Building and running + +This example is not automatically built by the top-level makefiles, but is +built in a similar manner: + +``` +$ make -f Makefile. +``` + +Refer to the top-level [README][../README.md] for usage details. + +For example: + +``` +$ SSHPORT=1234 make -f Makefile.morello-hybrid run-syscall-restrict +``` diff --git a/syscall-restrict/syscall-restrict.c b/syscall-restrict/syscall-restrict.c new file mode 100644 index 0000000..22e5e6c --- /dev/null +++ b/syscall-restrict/syscall-restrict.c @@ -0,0 +1,87 @@ +// Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#include +#include +#include +#include + +#include + +#include + +#define STR(s) #s +#define STR_VALUE(s) STR(s) + +typedef int (*stat_fn)(const char *restrict path, struct stat *restrict statbuf); + +void try_stat(stat_fn fn, char const *file) +{ + struct stat statbuf; + printf(" stat(\"%s\", &statbuf)\n", file); + if (fn(file, &statbuf) == 0) + { + // Print some easily-verifiable information to demonstrate that + // the syscall actually worked. + printf(" - Success, size is %zu bytes.\n", statbuf.st_size); + } + else + { + printf(" - Failed, errno = %d: %s\n", errno, strerror(errno)); + } +} + +int stat_without_perm_syscall(const char *restrict path, struct stat *restrict statbuf); +#if __has_feature(capabilities) && defined(__aarch64__) +#ifdef __CHERI_PURE_CAPABILITY__ +// This probably requires a static build, or at least non-sealed access to the +// underlying 'stat'. Even then, whilst it demonstrates CHERI_PERM_SYSCALL, its +// security is incomplete because the sentry (with the permission) is present in +// every compartment's GOT. +#error Unimplemented: purecap support. +#else +// clang-format off +asm( ".type stat_without_perm_syscall, @function\n" + "stat_without_perm_syscall:\n" + // x0: path (passed through unmodified) + // x1: statbuf (passed through unmodified) + // First, preserve the current PCC. + " adr x16, stat\n" + " cvtp c16, x16\n" + // Note that c16 actually refers to the PLT, not to `stat()` itself. + // That doesn't matter for hybrid mode, since only the PCC address is + // changed, but it makes this difficult to implement for purecap. + " str c16, [sp, #-32]!\n" + " str lr, [sp, #16]\n" + // Now drop the permission and call 'stat'. Note that this is not + // secure; a malicious callee could retrieve the old PCC from the stack. + // However, it's sufficient for a demonstration. + " mov x17, #" STR_VALUE(CHERI_PERM_SYSCALL) "\n" + " clrperm c16, c16, x17\n" + " blr c16\n" + // Restore registers saved for the call. + " ldr lr, [sp, #16]\n" + " ldr c16, [sp], #32\n" + // Combine the return address (in lr) with the preserved PCC metadata + // (in c16) to form a capability that can restore PCC and return at the + // same time. + " scvalue clr, c16, lr\n" + " ret clr\n"); +// clang-format on +#endif +#else +#error This example only supports Morello. +#endif + +int main(int argc, char *argv[]) +{ + // Arbitrarily pick the executable itself as the subject. + char const *file = argv[0]; + + printf("\nInitially, we can call syscalls as usual:\n"); + try_stat(stat, file); + printf("\nIf we remove CHERI_PERM_SYSCALL, CheriBSD rejects them:\n"); + try_stat(stat_without_perm_syscall, file); + printf("\nThe error is conventional (not an exception), so we can recover:\n"); + try_stat(stat, file); +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index b9ecd6c..b7b53e6 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -73,6 +73,7 @@ elif [ "$1" = "morello-hybrid" ]; then run OK hybrid basic_ddc run OK hybrid/compartment_examples/inter_comp_call/base main run OK hybrid/compartment_examples/inter_comp_call/secure main + run OK syscall-restrict syscall-restrict else echo "$1 not supported (yet)." exit 1 From b5de2940e0dfb48165f86813b3196d304465a026 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Mon, 30 May 2022 15:36:18 +0100 Subject: [PATCH 73/98] Original Richards benchmark from Stefan Marr's Classic Benchmarking repo --- richards-benchmark/harness.h | 34 +++ richards-benchmark/richards.c | 427 ++++++++++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 richards-benchmark/harness.h create mode 100644 richards-benchmark/richards.c diff --git a/richards-benchmark/harness.h b/richards-benchmark/harness.h new file mode 100644 index 0000000..987133f --- /dev/null +++ b/richards-benchmark/harness.h @@ -0,0 +1,34 @@ +/** A simple harness for the C benchmarks + * + * It is included by every benchmark file and provides the time measuring, + * as well as standardized command-line argument parsing. + */ + +#include + +unsigned long microseconds() { + // Not monotonic + struct timeval t; + gettimeofday(&t, NULL); + return (t.tv_sec * 1000 * 1000) + t.tv_usec; +}; + +void parse_argv(int argc, + char** argv, + int* iterations, + int* warmup, + int* inner_or_problem_size) { + if (argc > 1) { + *iterations = atoi(argv[1]); + } + if (argc > 2) { + *warmup = atoi(argv[2]); + } + if (argc > 3) { + *inner_or_problem_size = atoi(argv[3]); + } + + printf("Overall iterations: %d\n", *iterations); + printf("Warmup iterations: %d\n", *warmup); + printf("Inner it./problem size: %d\n", *inner_or_problem_size); +}; diff --git a/richards-benchmark/richards.c b/richards-benchmark/richards.c new file mode 100644 index 0000000..895cf82 --- /dev/null +++ b/richards-benchmark/richards.c @@ -0,0 +1,427 @@ +/* C version of the systems programming language benchmark +** Author: M. J. Jordan Cambridge Computer Laboratory. +** +** Modified by: M. Richards, Nov 1996 +** to be ANSI C and runnable on 64 bit machines + other minor changes +** Modified by: M. Richards, 20 Oct 1998 +** made minor corrections to improve ANSI compliance (suggested +** by David Levine) +** +** Compile with, say +** +** gcc -o bench bench.c +** +** or +** +** gcc -o bench100 -Dbench100 bench.c (for a version that obeys +** the main loop 100x more often) +*/ + +#include +#include + +#include "harness.h" + +#ifdef bench100 +#define Count 10000*100 +#define Qpktcountval 2326410 +#define Holdcountval 930563 +#else +#define Count 10000 +#define Qpktcountval 23246 +#define Holdcountval 9297 +#endif + +#define TRUE 1 +#define FALSE 0 +#define MAXINT 32767 + +#define BUFSIZE 3 +#define I_IDLE 1 +#define I_WORK 2 +#define I_HANDLERA 3 +#define I_HANDLERB 4 +#define I_DEVA 5 +#define I_DEVB 6 +#define PKTBIT 1 +#define WAITBIT 2 +#define HOLDBIT 4 +#define NOTPKTBIT !1 +#define NOTWAITBIT !2 +#define NOTHOLDBIT 0XFFFB + +#define S_RUN 0 +#define S_RUNPKT 1 +#define S_WAIT 2 +#define S_WAITPKT 3 +#define S_HOLD 4 +#define S_HOLDPKT 5 +#define S_HOLDWAIT 6 +#define S_HOLDWAITPKT 7 + +#define K_DEV 1000 +#define K_WORK 1001 + +struct packet +{ + struct packet *p_link; + int p_id; + int p_kind; + int p_a1; + char p_a2[BUFSIZE+1]; +}; + +struct task +{ + struct task *t_link; + int t_id; + int t_pri; + struct packet *t_wkq; + int t_state; + struct task *(*t_fn)(struct packet *); + long t_v1; + long t_v2; +}; + +char alphabet[28] = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +struct task *tasktab[11] = {(struct task *)10,0,0,0,0,0,0,0,0,0,0}; +struct task *tasklist = 0; +struct task *tcb; +long taskid; +long v1; +long v2; +int qpktcount = 0; +int holdcount = 0; +int tracing = 0; +int layout = 0; + +void append(struct packet *pkt, struct packet *ptr); + +void createtask(int id, + int pri, + struct packet *wkq, + int state, + struct task *(*fn)(struct packet *), + long v1, + long v2) +{ + struct task *t = (struct task *)malloc(sizeof(struct task)); + + tasktab[id] = t; + t->t_link = tasklist; + t->t_id = id; + t->t_pri = pri; + t->t_wkq = wkq; + t->t_state = state; + t->t_fn = fn; + t->t_v1 = v1; + t->t_v2 = v2; + tasklist = t; +} + +struct packet *pkt(struct packet *link, int id, int kind) +{ + int i; + struct packet *p = (struct packet *)malloc(sizeof(struct packet)); + + for (i=0; i<=BUFSIZE; i++) + p->p_a2[i] = 0; + + p->p_link = link; + p->p_id = id; + p->p_kind = kind; + p->p_a1 = 0; + + return (p); +} + +void trace(char a) +{ + if ( --layout <= 0 ) + { + printf("\n"); + layout = 50; + } + + printf("%c", a); +} + +void schedule() +{ + while ( tcb != 0 ) + { + struct packet *pkt; + struct task *newtcb; + + pkt=0; + + switch ( tcb->t_state ) + { + case S_WAITPKT: + pkt = tcb->t_wkq; + tcb->t_wkq = pkt->p_link; + tcb->t_state = tcb->t_wkq == 0 ? S_RUN : S_RUNPKT; + + case S_RUN: + case S_RUNPKT: + taskid = tcb->t_id; + v1 = tcb->t_v1; + v2 = tcb->t_v2; + if (tracing) { + trace(taskid+'0'); + } + newtcb = (*(tcb->t_fn))(pkt); + tcb->t_v1 = v1; + tcb->t_v2 = v2; + tcb = newtcb; + break; + + case S_WAIT: + case S_HOLD: + case S_HOLDPKT: + case S_HOLDWAIT: + case S_HOLDWAITPKT: + tcb = tcb->t_link; + break; + + default: + return; + } + } +} + +struct task *wait_task(void) +{ + tcb->t_state |= WAITBIT; + return (tcb); +} + +struct task *holdself(void) +{ + ++holdcount; + tcb->t_state |= HOLDBIT; + return (tcb->t_link) ; +} + +struct task *findtcb(int id) +{ + struct task *t = 0; + + if (1<=id && id<=(long)tasktab[0]) + t = tasktab[id]; + if (t==0) printf("\nBad task id %d\n", id); + return(t); +} + +struct task *release(int id) +{ + struct task *t; + + t = findtcb(id); + if ( t==0 ) return (0); + + t->t_state &= NOTHOLDBIT; + if ( t->t_pri > tcb->t_pri ) return (t); + + return (tcb) ; +} + + +struct task *qpkt(struct packet *pkt) +{ + struct task *t; + + t = findtcb(pkt->p_id); + if (t==0) return (t); + + qpktcount++; + + pkt->p_link = 0; + pkt->p_id = taskid; + + if (t->t_wkq==0) + { + t->t_wkq = pkt; + t->t_state |= PKTBIT; + if (t->t_pri > tcb->t_pri) return (t); + } + else + { + append(pkt, (struct packet *)&(t->t_wkq)); + } + + return (tcb); +} + +struct task *idlefn(struct packet *pkt) +{ + if ( --v2==0 ) return ( holdself() ); + + if ( (v1&1) == 0 ) + { + v1 = ( v1>>1) & MAXINT; + return ( release(I_DEVA) ); + } + else + { + v1 = ( (v1>>1) & MAXINT) ^ 0XD008; + return ( release(I_DEVB) ); + } +} + +struct task *workfn(struct packet *pkt) +{ + if ( pkt==0 ) return ( wait_task() ); + else + { + int i; + + v1 = I_HANDLERA + I_HANDLERB - v1; + pkt->p_id = v1; + + pkt->p_a1 = 0; + for (i=0; i<=BUFSIZE; i++) + { + v2++; + if ( v2 > 26 ) v2 = 1; + (pkt->p_a2)[i] = alphabet[v2]; + } + return ( qpkt(pkt) ); + } +} + +struct task *handlerfn(struct packet *pkt) +{ + if ( pkt!=0) { + append(pkt, (struct packet *)(pkt->p_kind==K_WORK ? &v1 : &v2)); + } + + if ( v1!=0 ) { + int count; + struct packet *workpkt = (struct packet *)v1; + count = workpkt->p_a1; + + if ( count > BUFSIZE ) { + v1 = (long)(((struct packet *)v1)->p_link); + return ( qpkt(workpkt) ); + } + + if ( v2!=0 ) { + struct packet *devpkt; + + devpkt = (struct packet *)v2; + v2 = (long)(((struct packet *)v2)->p_link); + devpkt->p_a1 = workpkt->p_a2[count]; + workpkt->p_a1 = count+1; + return( qpkt(devpkt) ); + } + } + return wait_task(); +} + +struct task *devfn(struct packet *pkt) +{ + if ( pkt==0 ) + { + if ( v1==0 ) return ( wait_task() ); + pkt = (struct packet *)v1; + v1 = 0; + return ( qpkt(pkt) ); + } + else + { + v1 = (long)pkt; + if (tracing) trace(pkt->p_a1); + return ( holdself() ); + } +} + +void append(struct packet *pkt, struct packet *ptr) +{ + pkt->p_link = 0; + + while ( ptr->p_link ) ptr = ptr->p_link; + + ptr->p_link = pkt; +} + +int bench() { + struct packet *wkq = 0; + + // printf("Bench mark starting\n"); + + createtask(I_IDLE, 0, wkq, S_RUN, idlefn, 1, Count); + + wkq = pkt(0, 0, K_WORK); + wkq = pkt(wkq, 0, K_WORK); + + createtask(I_WORK, 1000, wkq, S_WAITPKT, workfn, I_HANDLERA, 0); + + wkq = pkt(0, I_DEVA, K_DEV); + wkq = pkt(wkq, I_DEVA, K_DEV); + wkq = pkt(wkq, I_DEVA, K_DEV); + + createtask(I_HANDLERA, 2000, wkq, S_WAITPKT, handlerfn, 0, 0); + + wkq = pkt(0, I_DEVB, K_DEV); + wkq = pkt(wkq, I_DEVB, K_DEV); + wkq = pkt(wkq, I_DEVB, K_DEV); + + createtask(I_HANDLERB, 3000, wkq, S_WAITPKT, handlerfn, 0, 0); + + wkq = 0; + createtask(I_DEVA, 4000, wkq, S_WAIT, devfn, 0, 0); + createtask(I_DEVB, 5000, wkq, S_WAIT, devfn, 0, 0); + + tcb = tasklist; + + qpktcount = holdcount = 0; + + // printf("Starting\n"); + + tracing = FALSE; + layout = 0; + + schedule(); + + // printf("\nfinished\n"); + + + + + if (!(qpktcount == Qpktcountval && holdcount == Holdcountval)) { + printf("qpkt count = %d holdcount = %d\n", qpktcount, holdcount); + printf("These results are incorrect"); + exit(1); + } + // printf("\nend of run\n"); + return qpktcount; +} + + +int inner_loop(int inner) { + int r = 0; + while (inner > 0) { + r += bench(); + inner--; + } + return r; +} + +int main(int argc, char* argv[]) +{ + int iterations = 100; + int warmup = 0; + int inner_iterations = 100; + + parse_argv(argc, argv, &iterations, &warmup, &inner_iterations); + + int result = 0; + while (iterations > 0) { + unsigned long start = microseconds(); + result += inner_loop(inner_iterations); + unsigned long elapsed = microseconds() - start; + printf("Richards: iterations=1 runtime: %lu%s\n", elapsed, "us"); + iterations--; + } +} From 2d941eab853e61ad69b941fca279eb3a3d42d0cf Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Mon, 30 May 2022 15:39:00 +0100 Subject: [PATCH 74/98] Added support for Boehm GC in Richards benchmark --- richards-benchmark/richards.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/richards-benchmark/richards.c b/richards-benchmark/richards.c index 895cf82..2e32a65 100644 --- a/richards-benchmark/richards.c +++ b/richards-benchmark/richards.c @@ -22,6 +22,13 @@ #include "harness.h" +#ifdef GC + #include "gc.h" + #define MALLOC GC_MALLOC +#else + #define MALLOC malloc +#endif // GC + #ifdef bench100 #define Count 10000*100 #define Qpktcountval 2326410 @@ -105,7 +112,7 @@ void createtask(int id, long v1, long v2) { - struct task *t = (struct task *)malloc(sizeof(struct task)); + struct task *t = (struct task *)MALLOC(sizeof(struct task)); tasktab[id] = t; t->t_link = tasklist; @@ -122,7 +129,7 @@ void createtask(int id, struct packet *pkt(struct packet *link, int id, int kind) { int i; - struct packet *p = (struct packet *)malloc(sizeof(struct packet)); + struct packet *p = (struct packet *)MALLOC(sizeof(struct packet)); for (i=0; i<=BUFSIZE; i++) p->p_a2[i] = 0; From cd4e10d2a3fc1ede9ae0ae7f292c83aa9bc1541d Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Fri, 27 May 2022 12:19:55 +0100 Subject: [PATCH 75/98] Richards benchmark modifications for CHERI --- richards-benchmark/README.md | 15 +++++++++++++++ richards-benchmark/richards.c | 24 +++++++++++++++--------- 2 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 richards-benchmark/README.md diff --git a/richards-benchmark/README.md b/richards-benchmark/README.md new file mode 100644 index 0000000..064c73b --- /dev/null +++ b/richards-benchmark/README.md @@ -0,0 +1,15 @@ +# Richards benchmark + +Richards is an OS task scheduler simulation benchmark, designed for +comparing system implementation languages. The benchmark was originally +implemented in BCPL by Martin Richards. +See https://www.cl.cam.ac.uk/~mr10/Bench.html for details. +This is a CHERI-compatible version of the Richards benchmark, with +optional support for BoehmGC. The code is derived from Stefan Marr's +classic benchmarks repository at +https://github.com/smarr/Classic-Benchmarks + +The Richards benchmark does not have a licence. Instead, Martin Richards +explicitly encourages people to 'feel free to re-implement this benchmark +in any language you choose'. + diff --git a/richards-benchmark/richards.c b/richards-benchmark/richards.c index 2e32a65..2bfad7c 100644 --- a/richards-benchmark/richards.c +++ b/richards-benchmark/richards.c @@ -6,6 +6,11 @@ ** Modified by: M. Richards, 20 Oct 1998 ** made minor corrections to improve ANSI compliance (suggested ** by David Levine) +** Modified by: Jeremy Singer, 25 May 2022 +** adapted to use uintptr_t for compatibility with architectures +** that do not have word-sized pointers (e.g. CHERI) +** also adapted to use Boehm-Demers-Weiser GC instead of system +** malloc, when compiled with -DGC ** ** Compile with, say ** @@ -18,6 +23,7 @@ */ #include +#include #include #include "harness.h" @@ -86,8 +92,8 @@ struct task struct packet *t_wkq; int t_state; struct task *(*t_fn)(struct packet *); - long t_v1; - long t_v2; + uintptr_t t_v1; + uintptr_t t_v2; }; char alphabet[28] = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -95,8 +101,8 @@ struct task *tasktab[11] = {(struct task *)10,0,0,0,0,0,0,0,0,0,0}; struct task *tasklist = 0; struct task *tcb; long taskid; -long v1; -long v2; +uintptr_t v1; +uintptr_t v2; int qpktcount = 0; int holdcount = 0; int tracing = 0; @@ -109,8 +115,8 @@ void createtask(int id, struct packet *wkq, int state, struct task *(*fn)(struct packet *), - long v1, - long v2) + uintptr_t v1, + uintptr_t v2) { struct task *t = (struct task *)MALLOC(sizeof(struct task)); @@ -309,7 +315,7 @@ struct task *handlerfn(struct packet *pkt) count = workpkt->p_a1; if ( count > BUFSIZE ) { - v1 = (long)(((struct packet *)v1)->p_link); + v1 = (uintptr_t)(((struct packet *)v1)->p_link); return ( qpkt(workpkt) ); } @@ -317,7 +323,7 @@ struct task *handlerfn(struct packet *pkt) struct packet *devpkt; devpkt = (struct packet *)v2; - v2 = (long)(((struct packet *)v2)->p_link); + v2 = (uintptr_t)(((struct packet *)v2)->p_link); devpkt->p_a1 = workpkt->p_a2[count]; workpkt->p_a1 = count+1; return( qpkt(devpkt) ); @@ -337,7 +343,7 @@ struct task *devfn(struct packet *pkt) } else { - v1 = (long)pkt; + v1 = (uintptr_t)pkt; if (tracing) trace(pkt->p_a1); return ( holdself() ); } From ee1daad3a42ddb017113aa2cb377da09ba1f85be Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Tue, 31 May 2022 10:11:32 +0100 Subject: [PATCH 76/98] confirm Public Domain licensing --- richards-benchmark/LICENSE.txt | 3 +++ richards-benchmark/README.md | 5 ----- 2 files changed, 3 insertions(+), 5 deletions(-) create mode 100644 richards-benchmark/LICENSE.txt diff --git a/richards-benchmark/LICENSE.txt b/richards-benchmark/LICENSE.txt new file mode 100644 index 0000000..1391e9c --- /dev/null +++ b/richards-benchmark/LICENSE.txt @@ -0,0 +1,3 @@ +The Richards benchmark has been placed into the public domain by +Martin Richards. The benchmark harness has been placed into the +public domain by Stefan Marr diff --git a/richards-benchmark/README.md b/richards-benchmark/README.md index 064c73b..53a18aa 100644 --- a/richards-benchmark/README.md +++ b/richards-benchmark/README.md @@ -8,8 +8,3 @@ This is a CHERI-compatible version of the Richards benchmark, with optional support for BoehmGC. The code is derived from Stefan Marr's classic benchmarks repository at https://github.com/smarr/Classic-Benchmarks - -The Richards benchmark does not have a licence. Instead, Martin Richards -explicitly encourages people to 'feel free to re-implement this benchmark -in any language you choose'. - From b68dcda3d4a75fe00812e12a5ada2bbcd90a0070 Mon Sep 17 00:00:00 2001 From: Jeremy Singer Date: Tue, 31 May 2022 16:18:39 +0100 Subject: [PATCH 77/98] clang format turned off for C code --- richards-benchmark/harness.h | 2 ++ richards-benchmark/richards.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/richards-benchmark/harness.h b/richards-benchmark/harness.h index 987133f..074ebd4 100644 --- a/richards-benchmark/harness.h +++ b/richards-benchmark/harness.h @@ -1,3 +1,5 @@ +// clang-format off + /** A simple harness for the C benchmarks * * It is included by every benchmark file and provides the time measuring, diff --git a/richards-benchmark/richards.c b/richards-benchmark/richards.c index 2bfad7c..7baebb3 100644 --- a/richards-benchmark/richards.c +++ b/richards-benchmark/richards.c @@ -1,3 +1,5 @@ +// clang-format off + /* C version of the systems programming language benchmark ** Author: M. J. Jordan Cambridge Computer Laboratory. ** From 49626f3f73cf5d6ed754ee4886157479d74f2c28 Mon Sep 17 00:00:00 2001 From: Giuseppe De Ruvo Date: Tue, 16 Aug 2022 12:20:53 +0100 Subject: [PATCH 78/98] Add morello-purecap support and fix naming --- .buildbot.sh | 23 +++++++++++++++++------ tests/run_tests.sh | 5 ++--- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.buildbot.sh b/.buildbot.sh index 715ec27..14c8932 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -40,7 +40,7 @@ done export SSHPORT=10021 export PYTHONPATH="$CHERIBUILD"/test-scripts -if [ "$1" = "riscv64" ]; then +if [ "$1" = "riscv64-purecap" ]; then echo "Running tests for 'riscv64-purecap' using QEMU." args=( --architecture riscv64 @@ -69,10 +69,21 @@ elif [ "$1" = "morello-hybrid" ]; then --ssh-key $HOME/.ssh/id_ed25519.pub ) BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" +elif [ "$1" = "morello-purecap" ]; then + echo "Running tests for 'morello-purecap' using QEMU..." + args=( + --architecture morello-purecap + # Qemu System to use + --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello + --bios edk2-aarch64-code.fd + --disk-image $HOME/cheri/output/cheribsd-morello-purecap.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub + ) + BUILDBOT_PLATFORM=morello-purecap python3 tests/run_cheri_examples.py "${args[@]}" else - echo "$1 not supported (yet)." + echo "$1 not recognised." exit 1 -fi - -# TODO: Run tests for `morello-purecap` and `riscv64-purecap` too -# This is related to https://github.com/CTSRD-CHERI/cheribuild/issues/182 +fi \ No newline at end of file diff --git a/tests/run_tests.sh b/tests/run_tests.sh index b7b53e6..e9e48c5 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -18,7 +18,6 @@ # - SSHHOST (defaults to 'localhost') # # TODO: Extend this to cover interactive examples and other architectures. -# For now, we just test examples on `morello-hybrid`. set -e @@ -58,7 +57,7 @@ function run { # PURECAP tests # TODO: add previous examples -if [ "$1" = "riscv64" ]; then +if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run OK shared_objects shared_objects-pcc_bounds_check_main elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS @@ -75,7 +74,7 @@ elif [ "$1" = "morello-hybrid" ]; then run OK hybrid/compartment_examples/inter_comp_call/secure main run OK syscall-restrict syscall-restrict else - echo "$1 not supported (yet)." + echo "$1 not recognised." exit 1 fi From be52765cc541667ad4d5f8532053d1502332e172 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Thu, 22 Sep 2022 12:02:45 +0100 Subject: [PATCH 79/98] Update buildbot Mirror changes on buildbot side. * update path to `qemu` * do not assume argument is passed to script --- .buildbot.sh | 94 ++++++++++++++++++++++++---------------------------- bors.toml | 2 +- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/.buildbot.sh b/.buildbot.sh index 14c8932..38fa2ce 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -6,14 +6,13 @@ set -e -export CHERIBASE=~/cheri +CHERI_DIR=/home/buildbot/cheri/output CHERIBUILD=~/build -export TEST_TARGET=$1 echo "Checking clang-format..." # Note that `sdk` and `morello-sdk` contain different versions of clang-format, # so we have to pick one and use it consistently. -CLANG_FORMAT="$CHERIBASE"/output/sdk/bin/clang-format +CLANG_FORMAT=$CHERI_DIR/morello-sdk/bin/clang-format find . -iname "*.c" -o -iname "*.h" -o -iname "*.cpp" -o -iname "*.hpp" | xargs "$CLANG_FORMAT" --dry-run -Werror echo "Checking that all the purecap examples build for all platforms..." @@ -40,50 +39,45 @@ done export SSHPORT=10021 export PYTHONPATH="$CHERIBUILD"/test-scripts -if [ "$1" = "riscv64-purecap" ]; then - echo "Running tests for 'riscv64-purecap' using QEMU." - args=( - --architecture riscv64 - # Qemu System to use - --qemu-cmd $HOME/cheri/output/sdk/bin/qemu-system-riscv64cheri - --kernel $HOME/cheri/output/rootfs-riscv64-purecap/boot/kernel/kernel - --bios bbl-riscv64cheri-virt-fw_jump.bin - --disk-image $HOME/cheri/output/cheribsd-riscv64-purecap.img - # Required build-dir in CheriBSD - --build-dir . - --ssh-port $SSHPORT - --ssh-key $HOME/.ssh/id_ed25519.pub - ) - BUILDBOT_PLATFORM=riscv64-purecap python3 tests/run_cheri_examples.py "${args[@]}" -elif [ "$1" = "morello-hybrid" ]; then - echo "Running tests for 'morello-hybrid' using QEMU..." - args=( - --architecture morello-hybrid - # Qemu System to use - --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello - --bios edk2-aarch64-code.fd - --disk-image $HOME/cheri/output/cheribsd-morello-hybrid.img - # Required build-dir in CheriBSD - --build-dir . - --ssh-port $SSHPORT - --ssh-key $HOME/.ssh/id_ed25519.pub - ) - BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" -elif [ "$1" = "morello-purecap" ]; then - echo "Running tests for 'morello-purecap' using QEMU..." - args=( - --architecture morello-purecap - # Qemu System to use - --qemu-cmd $HOME/cheri/output/morello-sdk/bin/qemu-system-morello - --bios edk2-aarch64-code.fd - --disk-image $HOME/cheri/output/cheribsd-morello-purecap.img - # Required build-dir in CheriBSD - --build-dir . - --ssh-port $SSHPORT - --ssh-key $HOME/.ssh/id_ed25519.pub - ) - BUILDBOT_PLATFORM=morello-purecap python3 tests/run_cheri_examples.py "${args[@]}" -else - echo "$1 not recognised." - exit 1 -fi \ No newline at end of file +echo "Running tests for 'riscv64-purecap' using QEMU." +args=( + --architecture riscv64 + # Qemu System to use + --qemu-cmd $CHERI_DIR/sdk/bin/qemu-system-riscv64cheri + --kernel $CHERI_DIR/rootfs-riscv64-purecap/boot/kernel/kernel + --bios bbl-riscv64cheri-virt-fw_jump.bin + --disk-image $CHERI_DIR/cheribsd-riscv64-purecap.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub +) +BUILDBOT_PLATFORM=riscv64-purecap python3 tests/run_cheri_examples.py "${args[@]}" + +echo "Running tests for 'morello-hybrid' using QEMU..." +args=( + --architecture morello-hybrid + # Qemu System to use + --qemu-cmd $CHERI_DIR/sdk/bin/qemu-system-morello + --bios edk2-aarch64-code.fd + --disk-image $CHERI_DIR/cheribsd-morello-hybrid.img + # Required build-dir in CheriBSD + --build-dir . + --ssh-port $SSHPORT + --ssh-key $HOME/.ssh/id_ed25519.pub +) +BUILDBOT_PLATFORM=morello-hybrid python3 tests/run_cheri_examples.py "${args[@]}" + +echo "Running tests for 'morello-purecap' using QEMU..." +args=( +--architecture morello-purecap +# Qemu System to use +--qemu-cmd $CHERI_DIR/sdk/bin/qemu-system-morello +--bios edk2-aarch64-code.fd +--disk-image $CHERI_DIR/cheribsd-morello-purecap.img +# Required build-dir in CheriBSD +--build-dir . +--ssh-port $SSHPORT +--ssh-key $HOME/.ssh/id_ed25519.pub +) +BUILDBOT_PLATFORM=morello-purecap python3 tests/run_cheri_examples.py "${args[@]}" diff --git a/bors.toml b/bors.toml index dcc1523..2fde3d7 100644 --- a/bors.toml +++ b/bors.toml @@ -1,4 +1,4 @@ -status = ["buildbot/capablevms-test-script"] +status = ["buildbot/ci-builder"] timeout_sec = 600 # 10 minutes From 5f8dbc81016b31342212480ad75f32d0d58420db Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Wed, 28 Sep 2022 15:37:15 +0100 Subject: [PATCH 80/98] Further buildbot updates New `buildbot` infrastructure defines some variables itself, so we remove local definitions. --- .buildbot.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.buildbot.sh b/.buildbot.sh index 38fa2ce..829bcc9 100755 --- a/.buildbot.sh +++ b/.buildbot.sh @@ -6,9 +6,6 @@ set -e -CHERI_DIR=/home/buildbot/cheri/output -CHERIBUILD=~/build - echo "Checking clang-format..." # Note that `sdk` and `morello-sdk` contain different versions of clang-format, # so we have to pick one and use it consistently. @@ -37,7 +34,6 @@ for dir in hybrid hybrid/ddc_compartment_switching syscall-restrict; do done export SSHPORT=10021 -export PYTHONPATH="$CHERIBUILD"/test-scripts echo "Running tests for 'riscv64-purecap' using QEMU." args=( From da74dc7e2dade73e300ad10a63fe36f1742a170f Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Thu, 13 Oct 2022 14:40:51 +0100 Subject: [PATCH 81/98] Add an example that reads a capability from a file. According to the CHERI C/C++ programming guide: > In the CheriABI process environment, only untagged data (not tagged > pointers) may be written to or read from files. If a region of memory > containing valid pointers is written to a file, and then read back, the > pointers in that region24 will no longer be valid. However, as this example demonstrates, this behaviour appears to be broken, as it is possible to read a valid capability from a file. This example writes a capability to a temporary file and then successfully retrieves the original (valid) capability, which should not be possible according to the CheriABI. The problem still reproduces if we simplify the example further, by removing the part where the capability is written to disk and hard-coding the file from which to read the capability (the file could contain `ff5c fff7 ffff 0000 ff5c 7f60 4000 dc5d` for example). --- capability_sharing/README.md | 6 ++ .../cap-to-file/Makefile.morello-purecap | 6 ++ .../cap-to-file/Makefile.riscv64-purecap | 6 ++ capability_sharing/cap-to-file/README.md | 10 ++ .../cap-to-file/build/Makefile.cap-to-file | 11 +++ capability_sharing/cap-to-file/cap_to_file.c | 92 +++++++++++++++++++ tests/run_tests.sh | 1 + 7 files changed, 132 insertions(+) create mode 100644 capability_sharing/README.md create mode 100644 capability_sharing/cap-to-file/Makefile.morello-purecap create mode 100644 capability_sharing/cap-to-file/Makefile.riscv64-purecap create mode 100644 capability_sharing/cap-to-file/README.md create mode 100644 capability_sharing/cap-to-file/build/Makefile.cap-to-file create mode 100644 capability_sharing/cap-to-file/cap_to_file.c diff --git a/capability_sharing/README.md b/capability_sharing/README.md new file mode 100644 index 0000000..af892d7 --- /dev/null +++ b/capability_sharing/README.md @@ -0,0 +1,6 @@ +# Capability sharing examples + +This directory contains various examples that attempt to share capabilities +between processes. + +Refer to the README.md of each example for more details. diff --git a/capability_sharing/cap-to-file/Makefile.morello-purecap b/capability_sharing/cap-to-file/Makefile.morello-purecap new file mode 100644 index 0000000..d93506b --- /dev/null +++ b/capability_sharing/cap-to-file/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.cap-to-file diff --git a/capability_sharing/cap-to-file/Makefile.riscv64-purecap b/capability_sharing/cap-to-file/Makefile.riscv64-purecap new file mode 100644 index 0000000..e80c5ce --- /dev/null +++ b/capability_sharing/cap-to-file/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.cap-to-file diff --git a/capability_sharing/cap-to-file/README.md b/capability_sharing/cap-to-file/README.md new file mode 100644 index 0000000..22cb55c --- /dev/null +++ b/capability_sharing/cap-to-file/README.md @@ -0,0 +1,10 @@ +# Read a capability from a file + +This example is not automatically built by the top-level makefiles, but are +built in a similar manner: + +``` +$ make -f Makefile. run-cap_to_file +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/cap-to-file/build/Makefile.cap-to-file b/capability_sharing/cap-to-file/build/Makefile.cap-to-file new file mode 100644 index 0000000..dec4675 --- /dev/null +++ b/capability_sharing/cap-to-file/build/Makefile.cap-to-file @@ -0,0 +1,11 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the cap-to-file example. +# This should not be invoked directly. + +RUNDIR := $(RUNDIR)/cap-to-file +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +include ../../build/Makefile.simple diff --git a/capability_sharing/cap-to-file/cap_to_file.c b/capability_sharing/cap-to-file/cap_to_file.c new file mode 100644 index 0000000..bcdbe14 --- /dev/null +++ b/capability_sharing/cap-to-file/cap_to_file.c @@ -0,0 +1,92 @@ +/* + * According to the CHERI C/C++ programming guide, "in the CheriABI process + * environment, only untagged data (not tagged pointers) may be written to or + * read from files. If a region of memory containing valid pointers is written + * to a file, and then read back, the pointers in that region will no longer be + * valid.". However, as this example demonstrates, this behaviour appears to be + * broken, as it is possible to read a valid capability from a file. + */ +#include + +#include +#include +#include +#include +#include + +int write_cap(FILE *f, uintptr_t *cap) +{ + size_t res = fwrite(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "write failed: %zu\n", res); + return 1; + } + + return 0; +} + +int read_cap(FILE *f, uintptr_t *cap) +{ + size_t res = fread(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "read failed: %zu\n", res); + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int x = 1; + uintptr_t cap = (uintptr_t) &x; + FILE *f = NULL; + + if (!(f = tmpfile())) + { + perror("failed to create temporary file"); + return 1; + } + + printf("Writing capability to file: %#lp\n", (void *) cap); + assert(cheri_is_valid((void *) cap)); + + if (write_cap(f, &cap)) + { + return 1; + } + + if (fflush(f)) + { + perror("failed to flush"); + return 1; + } + + if (fseek(f, 0, SEEK_SET)) + { + perror("failed to seek"); + return 1; + } + + uintptr_t stored_cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) stored_cap)); + + if (read_cap(f, &stored_cap)) + { + return 1; + } + + printf("Read capability from file: %#lp\n", (void *) stored_cap); + assert(cap == stored_cap); + + // XXX the capability should not be valid but due to a bug in CheriBSD + // this assertion succeeds! + assert(cheri_is_valid((void *) stored_cap)); + + fclose(f); + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e9e48c5..149ce92 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -59,6 +59,7 @@ function run { # TODO: add previous examples if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run OK shared_objects shared_objects-pcc_bounds_check_main + run OK capability_sharing/cap-to-file cap_to_file elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS # Tests that should fail From f105f36d5b4791440f4403515d00189f9c4d152c Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Fri, 14 Oct 2022 12:43:20 +0100 Subject: [PATCH 82/98] Add a simpler example that reads a capability from a file. This example simply reads a capability from `test-cap.bin`. In this case, the capability is invalid (as expected). --- .../Makefile.morello-purecap | 6 ++ .../Makefile.riscv64-purecap | 6 ++ .../read-cap-from-file/README.md | 10 +++ .../build/Makefile.read-cap-from-file | 30 +++++++++ capability_sharing/read-cap-from-file/main.c | 58 ++++++++++++++++++ .../read-cap-from-file/test-cap.bin | Bin 0 -> 20 bytes tests/run_tests.sh | 1 + 7 files changed, 111 insertions(+) create mode 100644 capability_sharing/read-cap-from-file/Makefile.morello-purecap create mode 100644 capability_sharing/read-cap-from-file/Makefile.riscv64-purecap create mode 100644 capability_sharing/read-cap-from-file/README.md create mode 100644 capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file create mode 100644 capability_sharing/read-cap-from-file/main.c create mode 100644 capability_sharing/read-cap-from-file/test-cap.bin diff --git a/capability_sharing/read-cap-from-file/Makefile.morello-purecap b/capability_sharing/read-cap-from-file/Makefile.morello-purecap new file mode 100644 index 0000000..1d12c7d --- /dev/null +++ b/capability_sharing/read-cap-from-file/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-file diff --git a/capability_sharing/read-cap-from-file/Makefile.riscv64-purecap b/capability_sharing/read-cap-from-file/Makefile.riscv64-purecap new file mode 100644 index 0000000..528c54b --- /dev/null +++ b/capability_sharing/read-cap-from-file/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-file diff --git a/capability_sharing/read-cap-from-file/README.md b/capability_sharing/read-cap-from-file/README.md new file mode 100644 index 0000000..6a68878 --- /dev/null +++ b/capability_sharing/read-cap-from-file/README.md @@ -0,0 +1,10 @@ +# Read a capability from a file + +This example is not automatically built by the top-level makefiles, but are +built in a similar manner: + +``` +$ make -f Makefile. run-read-cap-from-file +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file b/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file new file mode 100644 index 0000000..a6a9202 --- /dev/null +++ b/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file @@ -0,0 +1,30 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the read-cap-from-file examples. +# This should not be invoked directly. + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/read-cap-from-file +else +RUNDIR := read-cap-from-file +endif + +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +CAP_FILE := "./test-cap.bin" + +include ../../build/Makefile.simple + +.PHONY: scp-cap-file + +run-read-cap-from-file: scp-cap-file run-main + +scp-cap-file: +ifdef RUNDIR + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' + scp $(SCP_OPTIONS) -P $(SSHPORT) $(CAP_FILE) $(RUNUSER)@$(RUNHOST):$(RUNDIR) +else + scp $(SCP_OPTIONS) -P $(SSHPORT) $(CAP_FILE) $(RUNUSER)@$(RUNHOST): +endif diff --git a/capability_sharing/read-cap-from-file/main.c b/capability_sharing/read-cap-from-file/main.c new file mode 100644 index 0000000..b09626c --- /dev/null +++ b/capability_sharing/read-cap-from-file/main.c @@ -0,0 +1,58 @@ +/* + * According to the CHERI C/C++ programming guide, "in the CheriABI process + * environment, only untagged data (not tagged pointers) may be written to or + * read from files. If a region of memory containing valid pointers is written + * to a file, and then read back, the pointers in that region will no longer be + * valid.". However, as this example demonstrates, this behaviour appears to be + * broken, as it is possible to read a valid capability from a file. + */ +#include + +#include +#include +#include +#include +#include + +#define CAP_FILE "./test-cap.bin" + +int read_cap(FILE *f, uintptr_t *cap) +{ + size_t res = fread(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "read failed: %zu\n", res); + return 1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + int x = 1; + FILE *f = NULL; + + if (!(f = fopen(CAP_FILE, "r"))) + { + perror("failed to open capability file"); + return 1; + } + + uintptr_t stored_cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) stored_cap)); + + if (read_cap(f, &stored_cap)) + { + return 1; + } + + printf("Read capability from file: %#lp\n", (void *) stored_cap); + + assert(!cheri_is_valid((void *) stored_cap)); + + fclose(f); + + return 0; +} diff --git a/capability_sharing/read-cap-from-file/test-cap.bin b/capability_sharing/read-cap-from-file/test-cap.bin new file mode 100644 index 0000000000000000000000000000000000000000..2e04b1959fb0c245e09d6b833699bac61a4b4025 GIT binary patch literal 20 Zcma#~|NZ~}{|pQ<{}bvN9AfW)FaUjP3VZ+n literal 0 HcmV?d00001 diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 149ce92..9ca4d85 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -60,6 +60,7 @@ function run { if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run OK shared_objects shared_objects-pcc_bounds_check_main run OK capability_sharing/cap-to-file cap_to_file + run OK capability_sharing/read-cap-from-file read-cap-from-file elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS # Tests that should fail From 9371a5e562f69e7920f1f3bf270c791ddda7c5ea Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Fri, 14 Oct 2022 14:55:11 +0100 Subject: [PATCH 83/98] An example showing how a process can "leak" capabilities. This example shows how a process can "leak" its capabilities The setup consists of two processes: * a writer process that writes a capability to a file, and * a reader process that reads the capability written by the writer process. The reader processs successfully reads a valid capability According to the CHERI C/C++ programming guide: > in the CheriABI process environment, only untagged data (not tagged pointers) > may be written to or read from files. If a region of memory containing valid > pointers is written to a file, and then read back, the pointers in that region > will no longer be valid However, as this example demonstrates, this behaviour appears to be broken, as it is possible to read a valid capability from a file. --- .../leak-capability/Makefile.morello-purecap | 6 ++ .../leak-capability/Makefile.riscv64-purecap | 6 ++ capability_sharing/leak-capability/README.md | 29 ++++++++ .../build/Makefile.leak-capability | 39 +++++++++++ capability_sharing/leak-capability/read.c | 66 +++++++++++++++++++ capability_sharing/leak-capability/run.sh | 8 +++ capability_sharing/leak-capability/write.c | 61 +++++++++++++++++ tests/run_tests.sh | 1 + 8 files changed, 216 insertions(+) create mode 100644 capability_sharing/leak-capability/Makefile.morello-purecap create mode 100644 capability_sharing/leak-capability/Makefile.riscv64-purecap create mode 100644 capability_sharing/leak-capability/README.md create mode 100644 capability_sharing/leak-capability/build/Makefile.leak-capability create mode 100644 capability_sharing/leak-capability/read.c create mode 100755 capability_sharing/leak-capability/run.sh create mode 100644 capability_sharing/leak-capability/write.c diff --git a/capability_sharing/leak-capability/Makefile.morello-purecap b/capability_sharing/leak-capability/Makefile.morello-purecap new file mode 100644 index 0000000..54e2381 --- /dev/null +++ b/capability_sharing/leak-capability/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.leak-capability diff --git a/capability_sharing/leak-capability/Makefile.riscv64-purecap b/capability_sharing/leak-capability/Makefile.riscv64-purecap new file mode 100644 index 0000000..9e72f90 --- /dev/null +++ b/capability_sharing/leak-capability/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.leak-capability diff --git a/capability_sharing/leak-capability/README.md b/capability_sharing/leak-capability/README.md new file mode 100644 index 0000000..8e19b49 --- /dev/null +++ b/capability_sharing/leak-capability/README.md @@ -0,0 +1,29 @@ +# Leak a capability to another process + +This example shows how a process can "leak" its capabilities + +The setup consists of two processes: +* a writer process that writes a capability to a file, and +* a reader process that reads the capability written by the writer process. The + reader processs successfully reads a valid capability + +According to the CHERI C/C++ programming guide: + +> in the CheriABI process environment, only untagged data (not tagged pointers) +> may be written to or read from files. If a region of memory containing valid +> pointers is written to a file, and then read back, the pointers in that region +> will no longer be valid + +However, as this example demonstrates, this behaviour appears to be broken, as +it is possible to read a valid capability from a file. + +## Running + +This example is not automatically built by the top-level makefiles, but are +built in a similar manner: + +``` +$ make -f Makefile. run-leak-capability +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/leak-capability/build/Makefile.leak-capability b/capability_sharing/leak-capability/build/Makefile.leak-capability new file mode 100644 index 0000000..2e2be4b --- /dev/null +++ b/capability_sharing/leak-capability/build/Makefile.leak-capability @@ -0,0 +1,39 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the leak-capability examples. +# This should not be invoked directly. + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/leak-capability +else +RUNDIR := leak-capability +endif + +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +RUN_SH := ./run.sh + +include ../../build/Makefile.simple + +.PHONY: scp-run-sh + +run-leak-capability: $(BINS) scp-run-sh +ifdef RUNDIR + scp $(SCP_OPTIONS) -P $(SSHPORT) $(BINS) $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'cd $(RUNDIR) && $(RUN_SH)' +else + scp $(SCP_OPTIONS) -P $(SSHPORT) $(BINS) $(RUNUSER)@$(RUNHOST): + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) '$(RUN_SH)' +endif + +scp-run-sh: +ifdef RUNDIR + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' + scp $(SCP_OPTIONS) -P $(SSHPORT) $(RUN_SH) $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'chmod +x $(RUNDIR)/$(RUN_SH)' +else + scp $(SCP_OPTIONS) -P $(SSHPORT) $(RUN_SH) $(RUNUSER)@$(RUNHOST): + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'chmod +x $(RUN_SH)' +endif diff --git a/capability_sharing/leak-capability/read.c b/capability_sharing/leak-capability/read.c new file mode 100644 index 0000000..cd7675e --- /dev/null +++ b/capability_sharing/leak-capability/read.c @@ -0,0 +1,66 @@ +/* + * An example which shows how a process can "leak" its capabilities. + * + * This example shows how a process can "leak" its capabilities + * + * The setup consists of two processes: + * * a writer process that writes a capability to a file, and + * * a reader process that reads the capability written by the writer process. The + * reader processs successfully reads a valid capability + * + * This file contains the source of the reader process. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAP_FILE "/tmp/leaked-cap.bin" + +/* + * Read the value of the capability stored in `CAP_FILE`. + */ +int main(int argc, char **argv) +{ + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); + + FILE *f = fopen(CAP_FILE, "r"); + + if (f == NULL) + { + perror("failed to open file"); + return 1; + } + + size_t n = fread(&cap, sizeof(uintptr_t), 1, f); + + if (n != 1) + { + fprintf(stderr, "read failed: %zu\n", n); + return 1; + } + + if (fclose(f)) + { + perror("failed to close file"); + return 1; + } + + printf("Read capability from %s: %#lp\n", CAP_FILE, (void *) cap); + + // XXX the capability should not be valid but due to a bug in CheriBSD + // this assertion succeeds! + assert(cheri_is_valid((void *) cap)); + + return 0; +} diff --git a/capability_sharing/leak-capability/run.sh b/capability_sharing/leak-capability/run.sh new file mode 100755 index 0000000..3f36f14 --- /dev/null +++ b/capability_sharing/leak-capability/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eou pipefail + +# Write the capability +./write +# Read it back +./read diff --git a/capability_sharing/leak-capability/write.c b/capability_sharing/leak-capability/write.c new file mode 100644 index 0000000..9d20c30 --- /dev/null +++ b/capability_sharing/leak-capability/write.c @@ -0,0 +1,61 @@ +/* + * An example which shows how a process can "leak" its capabilities. + * + * This example shows how a process can "leak" its capabilities + * + * The setup consists of two processes: + * * a writer process that writes a capability to a file, and + * * a reader process that reads the capability written by the writer process. The + * reader processs successfully reads a valid capability + * + * This file contains the source of the writer process. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAP_FILE "/tmp/leaked-cap.bin" + +/* + * Write a capability to CAP_FILE. + */ +int main(int argc, char **argv) +{ + FILE *f = fopen(CAP_FILE, "w"); + + if (f == NULL) + { + perror("failed to open file"); + return 1; + } + + int x = 1; + uintptr_t cap = (uintptr_t) &x; + + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); + + fprintf(stderr, "Wrote capability to %s: %#lp\n", CAP_FILE, (void *) cap); + + if (n != 1) + { + fprintf(stderr, "write failed: %zu\n", n); + return 1; + } + + if (fclose(f)) + { + perror("failed to close file"); + return 1; + } + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 9ca4d85..65d3593 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -61,6 +61,7 @@ if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run OK shared_objects shared_objects-pcc_bounds_check_main run OK capability_sharing/cap-to-file cap_to_file run OK capability_sharing/read-cap-from-file read-cap-from-file + run OK capability_sharing/leak-capability leak-capability elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS # Tests that should fail From 9ce8f6d7ef7981e14581d08aacd23aab7b7db2cc Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Mon, 17 Oct 2022 11:54:57 +0100 Subject: [PATCH 84/98] Fix README typos. --- capability_sharing/cap-to-file/README.md | 2 +- capability_sharing/leak-capability/README.md | 2 +- capability_sharing/read-cap-from-file/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/capability_sharing/cap-to-file/README.md b/capability_sharing/cap-to-file/README.md index 22cb55c..69cb563 100644 --- a/capability_sharing/cap-to-file/README.md +++ b/capability_sharing/cap-to-file/README.md @@ -1,6 +1,6 @@ # Read a capability from a file -This example is not automatically built by the top-level makefiles, but are +This example is not automatically built by the top-level makefiles, but is built in a similar manner: ``` diff --git a/capability_sharing/leak-capability/README.md b/capability_sharing/leak-capability/README.md index 8e19b49..8baf57e 100644 --- a/capability_sharing/leak-capability/README.md +++ b/capability_sharing/leak-capability/README.md @@ -19,7 +19,7 @@ it is possible to read a valid capability from a file. ## Running -This example is not automatically built by the top-level makefiles, but are +This example is not automatically built by the top-level makefiles, but is built in a similar manner: ``` diff --git a/capability_sharing/read-cap-from-file/README.md b/capability_sharing/read-cap-from-file/README.md index 6a68878..a658dab 100644 --- a/capability_sharing/read-cap-from-file/README.md +++ b/capability_sharing/read-cap-from-file/README.md @@ -1,6 +1,6 @@ # Read a capability from a file -This example is not automatically built by the top-level makefiles, but are +This example is not automatically built by the top-level makefiles, but is built in a similar manner: ``` From bf2082214ea5ba3e4d80055cd19a14f46ad992b7 Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Mon, 17 Oct 2022 12:02:14 +0100 Subject: [PATCH 85/98] Add examples that write capabilities to mmap-ed memory. These examples demonstrate what happens when you write tagged pointer values to various types of memory mappings: * Anonymous private mapping (OK) * Anonymous shared mapping (OK) * File-backed private mapping (OK) * File-backed shared mapping (segfault) --- .../Makefile.morello-purecap | 6 +++ .../Makefile.riscv64-purecap | 6 +++ .../mmap-shared-vs-private/README.md | 20 ++++++++ .../build/Makefile.mmap_shared_vs_private | 13 ++++++ .../mmap-shared-vs-private/include/util.h | 28 +++++++++++ .../private_anon_main.c | 11 +++++ .../private_file_main.c | 28 +++++++++++ .../mmap-shared-vs-private/shared_anon_main.c | 11 +++++ .../mmap-shared-vs-private/shared_file_main.c | 29 ++++++++++++ .../mmap-shared-vs-private/util.c | 46 +++++++++++++++++++ tests/run_tests.sh | 6 +++ 11 files changed, 204 insertions(+) create mode 100644 capability_sharing/mmap-shared-vs-private/Makefile.morello-purecap create mode 100644 capability_sharing/mmap-shared-vs-private/Makefile.riscv64-purecap create mode 100644 capability_sharing/mmap-shared-vs-private/README.md create mode 100644 capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private create mode 100644 capability_sharing/mmap-shared-vs-private/include/util.h create mode 100644 capability_sharing/mmap-shared-vs-private/private_anon_main.c create mode 100644 capability_sharing/mmap-shared-vs-private/private_file_main.c create mode 100644 capability_sharing/mmap-shared-vs-private/shared_anon_main.c create mode 100644 capability_sharing/mmap-shared-vs-private/shared_file_main.c create mode 100644 capability_sharing/mmap-shared-vs-private/util.c diff --git a/capability_sharing/mmap-shared-vs-private/Makefile.morello-purecap b/capability_sharing/mmap-shared-vs-private/Makefile.morello-purecap new file mode 100644 index 0000000..7cf333b --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.mmap_shared_vs_private diff --git a/capability_sharing/mmap-shared-vs-private/Makefile.riscv64-purecap b/capability_sharing/mmap-shared-vs-private/Makefile.riscv64-purecap new file mode 100644 index 0000000..bff8604 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.mmap_shared_vs_private diff --git a/capability_sharing/mmap-shared-vs-private/README.md b/capability_sharing/mmap-shared-vs-private/README.md new file mode 100644 index 0000000..0c77ac6 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/README.md @@ -0,0 +1,20 @@ +# Writing capabilities to anonymous and file-backed mappings + +These examples demonstrate what happens when you write tagged pointer +values to various types of memory mappings: + * Anonymous private mapping (OK) + * Anonymous shared mapping (OK) + * File-backed private mapping (OK) + * File-backed shared mapping (segfault) + +These examples are not automatically built by the top-level makefiles, but are +built in a similar manner: + +``` +$ make -f Makefile. run-private_anon_main +$ make -f Makefile. run-private_file_main +$ make -f Makefile. run-shared_anon_main +$ make -f Makefile. run-shared_file_main +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private b/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private new file mode 100644 index 0000000..849c6e6 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private @@ -0,0 +1,13 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the mmap_shared_vs_private example. +# This should not be invoked directly. +CFLAGS += -I./include +RUNDIR := $(RUNDIR)/mmap_shared_vs_private +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +SHARED_SOURCES := util.c + +export + +include ../../build/Makefile.simple diff --git a/capability_sharing/mmap-shared-vs-private/include/util.h b/capability_sharing/mmap-shared-vs-private/include/util.h new file mode 100644 index 0000000..007a5e4 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/include/util.h @@ -0,0 +1,28 @@ +#ifndef __MMAP_SHARED_VS_PRIVATE_UTIL_H__ +#define __MMAP_SHARED_VS_PRIVATE_UTIL_H__ + +#include +#include + +#define MMAP_SIZE 4096 +#define INIT_TEST_CASE(prot_in, flags_in, fd_in) \ + { \ + .prot = prot_in, .flags = flags_in, .fd = fd_in, \ + .details = "flags = " #flags_in ", prot = " #prot_in \ + } + +typedef struct mmap_test_case +{ + /* The memory protection flags */ + int prot; + /* The visibility flags */ + int flags; + /* The file descriptor, if a file mapping was requested. */ + int fd; + /* A string describing the mapping (used for debugging). */ + char *details; +} mmap_test_case_t; + +int run_mmap_test(mmap_test_case_t); + +#endif // __MMAP_SHARED_VS_PRIVATE_UTIL_H__ diff --git a/capability_sharing/mmap-shared-vs-private/private_anon_main.c b/capability_sharing/mmap-shared-vs-private/private_anon_main.c new file mode 100644 index 0000000..8994584 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/private_anon_main.c @@ -0,0 +1,11 @@ +#include "util.h" + +#include +#include + +int main(int argc, char **argv) +{ + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1); + + return run_mmap_test(test); +} diff --git a/capability_sharing/mmap-shared-vs-private/private_file_main.c b/capability_sharing/mmap-shared-vs-private/private_file_main.c new file mode 100644 index 0000000..f791356 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/private_file_main.c @@ -0,0 +1,28 @@ +#include "util.h" + +#include +#include +#include + +int main(int argc, char **argv) +{ + FILE *f = tmpfile(); + + if (f == NULL) + { + perror("failed to create file"); + return 1; + } + + int errno; + + // Make sure the file is large enough + if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) + { + fprintf(stderr, "failed to extend file (error code: %d)\n", errno); + } + + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(f)); + + return run_mmap_test(test); +} diff --git a/capability_sharing/mmap-shared-vs-private/shared_anon_main.c b/capability_sharing/mmap-shared-vs-private/shared_anon_main.c new file mode 100644 index 0000000..999d43a --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/shared_anon_main.c @@ -0,0 +1,11 @@ +#include "util.h" + +#include +#include + +int main(int argc, char **argv) +{ + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1); + + return run_mmap_test(test); +} diff --git a/capability_sharing/mmap-shared-vs-private/shared_file_main.c b/capability_sharing/mmap-shared-vs-private/shared_file_main.c new file mode 100644 index 0000000..6acb880 --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/shared_file_main.c @@ -0,0 +1,29 @@ +#include "util.h" + +#include +#include +#include + +int main(int argc, char **argv) +{ + FILE *f = tmpfile(); + + if (f == NULL) + { + perror("failed to create file"); + return 1; + } + + int errno; + + // Make sure the file is large enough + if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) + { + fprintf(stderr, "failed to extend file (error code: %d)\n", errno); + } + + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED, fileno(f)); + + // Writing a capability to a file-backed mapping will trigger a segfault: + return run_mmap_test(test); +} diff --git a/capability_sharing/mmap-shared-vs-private/util.c b/capability_sharing/mmap-shared-vs-private/util.c new file mode 100644 index 0000000..4fa4e7a --- /dev/null +++ b/capability_sharing/mmap-shared-vs-private/util.c @@ -0,0 +1,46 @@ +#include "util.h" + +#include + +#include +#include +#include +#include + +static void *create_mmap(mmap_test_case_t test) +{ + printf("creating mapping (%s, fd = %d)\n", test.details, test.fd); + + void *addr = mmap(NULL, MMAP_SIZE, test.prot, test.flags, test.fd, 0); + + if (addr == MAP_FAILED) + { + perror("failed to mmap"); + return NULL; + } + + printf("mmap succeeded (requested length = %d, actual length = %zu): %#lp\n", MMAP_SIZE, + cheri_getlength(addr), addr); + + return addr; +} + +int run_mmap_test(mmap_test_case_t test) +{ + uintptr_t *addr = (uintptr_t *) create_mmap(test); + + int x = 0; + uintptr_t cap = (uintptr_t) &x; + *addr = cap; + + printf("original capability: %#lp\n", (void *) cap); + printf("stored capability: %#lp\n", (void *) *addr); + + if (munmap(addr, MMAP_SIZE)) + { + perror("failed to unmap"); + return 1; + } + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 65d3593..9ebb5f0 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -58,10 +58,16 @@ function run { # PURECAP tests # TODO: add previous examples if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then + # Tests that should pass run OK shared_objects shared_objects-pcc_bounds_check_main run OK capability_sharing/cap-to-file cap_to_file run OK capability_sharing/read-cap-from-file read-cap-from-file run OK capability_sharing/leak-capability leak-capability + run OK capability_sharing/mmap-shared-vs-private private_anon_main + run OK capability_sharing/mmap-shared-vs-private private_file_main + run OK capability_sharing/mmap-shared-vs-private shared_anon_main + # Tests that should fail + run to_fail capability_sharing/mmap-shared-vs-private shared_file_main elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS # Tests that should fail From f3e4ebb9d2318481f3480ab6adab1b4315eae800 Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Mon, 17 Oct 2022 14:52:08 +0100 Subject: [PATCH 86/98] A couple of examples which fail to leak capabilities using pipes. The first example (`read-cap-from-pipe`) consists of two sibling processes connected to a pipe: * a writer process that writes a capability to the pipe (`write_to_pipe`), and * a reader process that reads the capability written by the writer process to the pipe (`read_from_pipe`). The reader process reads an invalid capability The second example (`read-cap-from-pipe-sh`) is similar, except it relies on the shell to connect the two processes to a pipe. In both examples, the reader process fails to read a valid capability from the pipe (as expected). --- .../Makefile.morello-purecap | 6 + .../Makefile.riscv64-purecap | 6 + .../read-cap-from-pipe-with-sh/README.md | 19 ++ .../build/Makefile.read-cap-from-pipe-with-sh | 39 ++++ .../read-cap-from-pipe-with-sh/read.c | 49 +++++ .../read-cap-from-pipe-with-sh/run.sh | 6 + .../read-cap-from-pipe-with-sh/write.c | 47 ++++ .../Makefile.morello-purecap | 6 + .../Makefile.riscv64-purecap | 6 + .../read-cap-from-pipe/README.md | 19 ++ .../build/Makefile.read-cap-from-pipe | 18 ++ capability_sharing/read-cap-from-pipe/main.c | 206 ++++++++++++++++++ tests/run_tests.sh | 2 + 13 files changed, 429 insertions(+) create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/Makefile.morello-purecap create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/Makefile.riscv64-purecap create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/README.md create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/build/Makefile.read-cap-from-pipe-with-sh create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/read.c create mode 100755 capability_sharing/read-cap-from-pipe-with-sh/run.sh create mode 100644 capability_sharing/read-cap-from-pipe-with-sh/write.c create mode 100644 capability_sharing/read-cap-from-pipe/Makefile.morello-purecap create mode 100644 capability_sharing/read-cap-from-pipe/Makefile.riscv64-purecap create mode 100644 capability_sharing/read-cap-from-pipe/README.md create mode 100644 capability_sharing/read-cap-from-pipe/build/Makefile.read-cap-from-pipe create mode 100644 capability_sharing/read-cap-from-pipe/main.c diff --git a/capability_sharing/read-cap-from-pipe-with-sh/Makefile.morello-purecap b/capability_sharing/read-cap-from-pipe-with-sh/Makefile.morello-purecap new file mode 100644 index 0000000..72d85d8 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-pipe-with-sh diff --git a/capability_sharing/read-cap-from-pipe-with-sh/Makefile.riscv64-purecap b/capability_sharing/read-cap-from-pipe-with-sh/Makefile.riscv64-purecap new file mode 100644 index 0000000..d72dfa0 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-pipe-with-sh diff --git a/capability_sharing/read-cap-from-pipe-with-sh/README.md b/capability_sharing/read-cap-from-pipe-with-sh/README.md new file mode 100644 index 0000000..db41a78 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/README.md @@ -0,0 +1,19 @@ +# Reading and writing capabilities to a pipe + +The setup consists of two processes spawned by the shell: + * a writer process (whose stdout is redirected to a pipe by the shell), which + writes a capability to stdout + * a reader process which reads the capability from stdin (its stdin is redirected + to the same pipe the writer process writes to). The reader processs reads an + invalid capability + +## Running + +This example is not automatically built by the top-level makefiles, but is +built in a similar manner: + +``` +$ make -f Makefile. run-read-cap-from-pipe-with-sh +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/read-cap-from-pipe-with-sh/build/Makefile.read-cap-from-pipe-with-sh b/capability_sharing/read-cap-from-pipe-with-sh/build/Makefile.read-cap-from-pipe-with-sh new file mode 100644 index 0000000..11eac95 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/build/Makefile.read-cap-from-pipe-with-sh @@ -0,0 +1,39 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the read-cap-from-pipe-with-sh examples. +# This should not be invoked directly. + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/read-cap-from-pipe-with-sh +else +RUNDIR := read-cap-from-pipe-with-sh +endif + +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +RUN_SH := ./run.sh + +include ../../build/Makefile.simple + +.PHONY: scp-run-sh + +run-read-cap-from-pipe-with-sh: $(BINS) scp-run-sh +ifdef RUNDIR + scp $(SCP_OPTIONS) -P $(SSHPORT) $(BINS) $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'cd $(RUNDIR) && $(RUN_SH)' +else + scp $(SCP_OPTIONS) -P $(SSHPORT) $(BINS) $(RUNUSER)@$(RUNHOST): + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) '$(RUN_SH)' +endif + +scp-run-sh: +ifdef RUNDIR + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' + scp $(SCP_OPTIONS) -P $(SSHPORT) $(RUN_SH) $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'chmod +x $(RUNDIR)/$(RUN_SH)' +else + scp $(SCP_OPTIONS) -P $(SSHPORT) $(RUN_SH) $(RUNUSER)@$(RUNHOST): + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'chmod +x $(RUN_SH)' +endif diff --git a/capability_sharing/read-cap-from-pipe-with-sh/read.c b/capability_sharing/read-cap-from-pipe-with-sh/read.c new file mode 100644 index 0000000..67cd868 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/read.c @@ -0,0 +1,49 @@ +/* + * An example which fails to "leak" capabilities using Unix pipes. + * + * The setup consists of two processes: + * * a writer process that writes a capability to stdout, and + * * a reader process that reads the capability written by the writer process. The + * reader processs successfully reads a valid capability + * + * This file contains the source of the reader process. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Read the value of a capability from stdin. + */ +int main(int argc, char **argv) +{ + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); + + freopen(NULL, "rb", stdin); + + size_t n = fread(&cap, sizeof(uintptr_t), 1, stdin); + + if (n != 1) + { + fprintf(stderr, "read failed: %zu\n", n); + return 1; + } + + printf("Read capability from stdin: %#lp\n", (void *) cap); + + // As expected, the capability read from stdin is invalid. + assert(!cheri_is_valid((void *) cap)); + + return 0; +} diff --git a/capability_sharing/read-cap-from-pipe-with-sh/run.sh b/capability_sharing/read-cap-from-pipe-with-sh/run.sh new file mode 100755 index 0000000..2180c9c --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/run.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +set -eou pipefail + +# Attempt to share the capability +./write | ./read diff --git a/capability_sharing/read-cap-from-pipe-with-sh/write.c b/capability_sharing/read-cap-from-pipe-with-sh/write.c new file mode 100644 index 0000000..8ff42b0 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe-with-sh/write.c @@ -0,0 +1,47 @@ +/* + * An example which fails to "leak" capabilities using Unix pipes. + * + * The setup consists of two processes spawned by the shell: + * * a writer process (whose stdout is redirected to a pipe by the shell), which + * writes a capability to stdout + * * a reader process which reads the capability from stdin (its stdin is redirected + * to the same pipe the writer process writes to). The reader processs reads an + * invalid capability + * + * This file contains the source of the writer process. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Write a capability to stdout. + */ +int main(int argc, char **argv) +{ + freopen(NULL, "wb", stdout); + + int x = 1; + uintptr_t cap = (uintptr_t) &x; + + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, stdout); + + fprintf(stderr, "Wrote capability to stdout: %#lp\n", (void *) cap); + + if (n != 1) + { + fprintf(stderr, "write failed: %zu\n", n); + return 1; + } + + return 0; +} diff --git a/capability_sharing/read-cap-from-pipe/Makefile.morello-purecap b/capability_sharing/read-cap-from-pipe/Makefile.morello-purecap new file mode 100644 index 0000000..d302f9e --- /dev/null +++ b/capability_sharing/read-cap-from-pipe/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-pipe diff --git a/capability_sharing/read-cap-from-pipe/Makefile.riscv64-purecap b/capability_sharing/read-cap-from-pipe/Makefile.riscv64-purecap new file mode 100644 index 0000000..8b63e7b --- /dev/null +++ b/capability_sharing/read-cap-from-pipe/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.riscv64-purecap +include ../../build/Makefile.vars.common +include build/Makefile.read-cap-from-pipe diff --git a/capability_sharing/read-cap-from-pipe/README.md b/capability_sharing/read-cap-from-pipe/README.md new file mode 100644 index 0000000..d2ebafd --- /dev/null +++ b/capability_sharing/read-cap-from-pipe/README.md @@ -0,0 +1,19 @@ +# Reading and writing capabilities to a pipe + +An example which fails to "leak" capabilities using Unix pipes. + +The setup consists of two sibling processes connected to a pipe: + * a writer process that writes a capability to the write end of the pipe, and + * a reader process that reads the capability written by the writer process + to the pipe. The reader processs reads an invalid capability + +## Running + +This example is not automatically built by the top-level makefiles, but is +built in a similar manner: + +``` +$ make -f Makefile. run-main +``` + +Refer to the top-level [README][../README.md] for usage details. diff --git a/capability_sharing/read-cap-from-pipe/build/Makefile.read-cap-from-pipe b/capability_sharing/read-cap-from-pipe/build/Makefile.read-cap-from-pipe new file mode 100644 index 0000000..b1b820c --- /dev/null +++ b/capability_sharing/read-cap-from-pipe/build/Makefile.read-cap-from-pipe @@ -0,0 +1,18 @@ +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the read-cap-from-pipe examples. +# This should not be invoked directly. + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/read-cap-from-pipe +else +RUNDIR := read-cap-from-pipe +endif + +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +RUN_SH := ./run.sh + +include ../../build/Makefile.simple diff --git a/capability_sharing/read-cap-from-pipe/main.c b/capability_sharing/read-cap-from-pipe/main.c new file mode 100644 index 0000000..1d09704 --- /dev/null +++ b/capability_sharing/read-cap-from-pipe/main.c @@ -0,0 +1,206 @@ +/* + * An example which fails to "leak" capabilities using Unix pipes. + * + * The setup consists of two sibling processes connected to a pipe: + * * a writer process that writes a capability to the write end of the pipe, and + * * a reader process that reads the capability written by the writer process + * to the pipe. The reader processs reads an invalid capability + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PIPE_READ 0 +#define PIPE_WRITE 1 +#define NUM_CHILDREN 2 +#define NUM_ITERATIONS 10 +#define EXIT_CHILD 0 +#define EXIT_PARENT 1 + +int read_from_pipe(int fds[2]) +{ + if (close(fds[PIPE_WRITE])) + { + perror("failed to close the write end of the pipe"); + return -1; + } + + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); + + FILE *f = fdopen(fds[PIPE_READ], "r"); + if (f == NULL) + { + perror("fdopen fail"); + return -1; + } + + size_t n = fread(&cap, sizeof(uintptr_t), 1, f); + + if (n != 1) + { + fprintf(stderr, "read failed or EOF: %zu\n", n); + return -1; + } + + if (fclose(f)) + { + perror("failed to close stream"); + return -1; + } + + printf("Read capability from pipe: %#lp\n", (void *) cap); + + // As expected, the capability read from the pipe is invalid. + assert(!cheri_is_valid((void *) cap)); + + return 0; +} + +int write_to_pipe(int fds[2]) +{ + if (close(fds[PIPE_READ])) + { + perror("failed to close the read end of the pipe"); + return -1; + } + + int x = 1; + uintptr_t cap = (uintptr_t) &x; + + fprintf(stderr, "Writing capability to pipe: %#lp\n", (void *) cap); + + FILE *f = fdopen(fds[PIPE_WRITE], "w"); + if (f == NULL) + { + perror("fdopen fail"); + return -1; + } + + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); + + if (n != 1) + { + fprintf(stderr, "write failed or EOF: %zu\n", n); + return 1; + } + + if (fclose(f)) + { + perror("failed to close stream"); + return 1; + } + + return 0; +} + +int spawn_child(int (*child_fn)(int[2]), int fds[2]) +{ + pid_t pid = fork(); + switch (pid) + { + case -1: + { + perror("failed to spawn child process"); + return -1; + } + case 0: + { + if (child_fn(fds)) + { + return -1; + } + return 0; + } + default: + { + break; + } + } + + // Parent + return pid; +} + +/* + * Fork two child processes - one that writes to the pipe (`write_to_pipe`), and + * another that reads from the pipe (`read_from_pipe`) + */ +int run_pipe_test() +{ + int fds[2]; + + if (pipe(fds)) + { + perror("failed to create pipe"); + return -1; + } + + // The writer process: + int res = spawn_child(write_to_pipe, fds); + + // Only continue if we're the parent and no error occurred. + if (res == EXIT_CHILD || res == -1) + { + return res; + } + + // The reader process: + res = spawn_child(read_from_pipe, fds); + + // Only continue if we're the parent and no error occurred. + if (res == EXIT_CHILD || res == -1) + { + return res; + } + + return EXIT_PARENT; +} + +/* + * Write a capability to a pipe. + */ +int main(int argc, char **argv) +{ + for (size_t i = 0; i < NUM_ITERATIONS; ++i) + { + switch (run_pipe_test()) + { + case -1: + { + fprintf(stderr, "failed after %zu iterations\n", i); + return -1; + } + case EXIT_CHILD: + { + return 0; + } + default: + { + // We're the parent process (continue to the next iteration). + continue; + } + } + } + + for (size_t i = 0; i < NUM_CHILDREN * NUM_ITERATIONS; ++i) + { + if (wait(NULL) == -1 && errno != ECHILD) + { + perror("failed to wait for child"); + return 1; + } + } + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 9ebb5f0..eceaa62 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -66,6 +66,8 @@ if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run OK capability_sharing/mmap-shared-vs-private private_anon_main run OK capability_sharing/mmap-shared-vs-private private_file_main run OK capability_sharing/mmap-shared-vs-private shared_anon_main + run OK capability_sharing/read-cap-from-pipe main + run OK capability_sharing/read-cap-from-pipe-with-sh read-cap-from-pipe-with-sh # Tests that should fail run to_fail capability_sharing/mmap-shared-vs-private shared_file_main elif [ "$1" = "morello-hybrid" ]; then From 60b653ac5ddf091298d3304fc2f15c5b9ba3b34f Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Wed, 26 Oct 2022 17:27:31 +0100 Subject: [PATCH 87/98] Fix README typos and copyright year. This fixes the copyright year (which I got wrong for some of my examples), as well as some README typos. --- capability_sharing/cap-to-file/README.md | 2 +- capability_sharing/cap-to-file/build/Makefile.cap-to-file | 2 +- capability_sharing/leak-capability/README.md | 2 +- .../leak-capability/build/Makefile.leak-capability | 2 +- capability_sharing/mmap-shared-vs-private/README.md | 2 +- .../build/Makefile.mmap_shared_vs_private | 2 +- capability_sharing/read-cap-from-file/README.md | 2 +- .../read-cap-from-file/build/Makefile.read-cap-from-file | 2 +- capability_sharing/read-cap-from-pipe-with-sh/README.md | 2 +- capability_sharing/read-cap-from-pipe/README.md | 2 +- employee/README.md | 2 +- shared_objects/README.md | 2 +- syscall-restrict/README.md | 2 +- timsort/README.md | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/capability_sharing/cap-to-file/README.md b/capability_sharing/cap-to-file/README.md index 69cb563..5f22324 100644 --- a/capability_sharing/cap-to-file/README.md +++ b/capability_sharing/cap-to-file/README.md @@ -7,4 +7,4 @@ built in a similar manner: $ make -f Makefile. run-cap_to_file ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/capability_sharing/cap-to-file/build/Makefile.cap-to-file b/capability_sharing/cap-to-file/build/Makefile.cap-to-file index dec4675..ccb13dc 100644 --- a/capability_sharing/cap-to-file/build/Makefile.cap-to-file +++ b/capability_sharing/cap-to-file/build/Makefile.cap-to-file @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 # Common Makefile for the cap-to-file example. diff --git a/capability_sharing/leak-capability/README.md b/capability_sharing/leak-capability/README.md index 8baf57e..8c57f9a 100644 --- a/capability_sharing/leak-capability/README.md +++ b/capability_sharing/leak-capability/README.md @@ -26,4 +26,4 @@ built in a similar manner: $ make -f Makefile. run-leak-capability ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/capability_sharing/leak-capability/build/Makefile.leak-capability b/capability_sharing/leak-capability/build/Makefile.leak-capability index 2e2be4b..fb9bfc9 100644 --- a/capability_sharing/leak-capability/build/Makefile.leak-capability +++ b/capability_sharing/leak-capability/build/Makefile.leak-capability @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 # Common Makefile for the leak-capability examples. diff --git a/capability_sharing/mmap-shared-vs-private/README.md b/capability_sharing/mmap-shared-vs-private/README.md index 0c77ac6..4d50368 100644 --- a/capability_sharing/mmap-shared-vs-private/README.md +++ b/capability_sharing/mmap-shared-vs-private/README.md @@ -17,4 +17,4 @@ $ make -f Makefile. run-shared_anon_main $ make -f Makefile. run-shared_file_main ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private b/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private index 849c6e6..b71e52a 100644 --- a/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private +++ b/capability_sharing/mmap-shared-vs-private/build/Makefile.mmap_shared_vs_private @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 # Common Makefile for the mmap_shared_vs_private example. diff --git a/capability_sharing/read-cap-from-file/README.md b/capability_sharing/read-cap-from-file/README.md index a658dab..d5109c8 100644 --- a/capability_sharing/read-cap-from-file/README.md +++ b/capability_sharing/read-cap-from-file/README.md @@ -7,4 +7,4 @@ built in a similar manner: $ make -f Makefile. run-read-cap-from-file ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file b/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file index a6a9202..e48057f 100644 --- a/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file +++ b/capability_sharing/read-cap-from-file/build/Makefile.read-cap-from-file @@ -1,4 +1,4 @@ -# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 # Common Makefile for the read-cap-from-file examples. diff --git a/capability_sharing/read-cap-from-pipe-with-sh/README.md b/capability_sharing/read-cap-from-pipe-with-sh/README.md index db41a78..973acda 100644 --- a/capability_sharing/read-cap-from-pipe-with-sh/README.md +++ b/capability_sharing/read-cap-from-pipe-with-sh/README.md @@ -16,4 +16,4 @@ built in a similar manner: $ make -f Makefile. run-read-cap-from-pipe-with-sh ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/capability_sharing/read-cap-from-pipe/README.md b/capability_sharing/read-cap-from-pipe/README.md index d2ebafd..f4e3c47 100644 --- a/capability_sharing/read-cap-from-pipe/README.md +++ b/capability_sharing/read-cap-from-pipe/README.md @@ -16,4 +16,4 @@ built in a similar manner: $ make -f Makefile. run-main ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../../README.md) for usage details. diff --git a/employee/README.md b/employee/README.md index d19d37a..d8f644e 100644 --- a/employee/README.md +++ b/employee/README.md @@ -7,4 +7,4 @@ built in a similar manner: $ make -f Makefile. ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../README.md) for usage details. diff --git a/shared_objects/README.md b/shared_objects/README.md index 84c062b..d5f7d33 100644 --- a/shared_objects/README.md +++ b/shared_objects/README.md @@ -11,4 +11,4 @@ Unlike most other examples, these compile to a single executable (with accompanying shared objects). There is only a single `run-shared_objects` target, and the example is selected interactively. -Otherwise, refer to the top-level [README][../README.md] for usage details. +Otherwise, refer to the top-level [README](../README.md) for usage details. diff --git a/syscall-restrict/README.md b/syscall-restrict/README.md index 0c3374b..573d3ab 100644 --- a/syscall-restrict/README.md +++ b/syscall-restrict/README.md @@ -29,7 +29,7 @@ built in a similar manner: $ make -f Makefile. ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../README.md) for usage details. For example: diff --git a/timsort/README.md b/timsort/README.md index 32bfacd..77d32b2 100644 --- a/timsort/README.md +++ b/timsort/README.md @@ -7,7 +7,7 @@ built in a similar manner: $ make -f Makefile. ``` -Refer to the top-level [README][../README.md] for usage details. +Refer to the top-level [README](../README.md) for usage details. For example: From 64471c791b74652f5a4f941ccee87d6058bb4295 Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Fri, 28 Oct 2022 14:45:31 +0100 Subject: [PATCH 88/98] Add the missing `base.h` file. The `comp_setup` example doesn't build on `master`: ``` gabi@bencher14 ~/c/h/c/comp_setup > SSHPORT=10085 make -f ../Makefile.morello-hybrid run-main /home/gabi/cheri/output/morello-sdk/bin/clang -E shared.S > shared.s shared.S:4:10: fatal error: 'main.h' file not found ^~~~~~~~ 1 error generated. make: *** [: shared.s] Error 1 rm shared.s ``` It looks like `main.h` was accidentally removed at some point (#57). This patch reinstates it as `base.h` (for consistency with the other examples). --- hybrid/compartment_examples/comp_setup/include/base.h | 6 ++++++ hybrid/compartment_examples/comp_setup/main.c | 2 +- hybrid/compartment_examples/comp_setup/shared.S | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 hybrid/compartment_examples/comp_setup/include/base.h diff --git a/hybrid/compartment_examples/comp_setup/include/base.h b/hybrid/compartment_examples/comp_setup/include/base.h new file mode 100644 index 0000000..b983e8f --- /dev/null +++ b/hybrid/compartment_examples/comp_setup/include/base.h @@ -0,0 +1,6 @@ +#define COMP_COUNT 1 +#define COMP_SIZE 80 +#define COMP_OFFSET_STK_ADDR 8 +#define COMP_OFFSET_STK_LEN 16 +#define COMP_OFFSET_DDC 48 +#define COMP_OFFSET_PCC 64 diff --git a/hybrid/compartment_examples/comp_setup/main.c b/hybrid/compartment_examples/comp_setup/main.c index ab185a7..194e072 100644 --- a/hybrid/compartment_examples/comp_setup/main.c +++ b/hybrid/compartment_examples/comp_setup/main.c @@ -64,7 +64,7 @@ struct comp }; // ASM offsets, included here for validation -#include "main.h" +#include "include/base.h" static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), diff --git a/hybrid/compartment_examples/comp_setup/shared.S b/hybrid/compartment_examples/comp_setup/shared.S index 208f797..73b10af 100644 --- a/hybrid/compartment_examples/comp_setup/shared.S +++ b/hybrid/compartment_examples/comp_setup/shared.S @@ -1,7 +1,7 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "main.h" +#include "include/base.h" .global comp_f_fn From 7c8ca23900efdf0597b2118b76ae3ed3f953f399 Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Mon, 31 Oct 2022 11:41:54 +0000 Subject: [PATCH 89/98] Refactor `switch_compartment.s`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Context As the diffs below show, the `switch_compartment.s`s of the 3 `secure` examples are almost identical: ``` gabi@areca ~/c/cheri-examples > diff hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s 38c38 < // Save old DDC (c2), old SP (x12), old CLR (clr) on stack --- > // Save old DDC (c2), old SP (x12), old LR (clr) on stack gabi@areca ~/c/cheri-examples > diff hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S 5a6,8 > > #include "../include/secure.h" > 6a10,11 > .global switch_compartment_end > ``` Since the only real difference between them is the exported `switch_compartment_end` symbol, it's safe to refactor the 3 examples to use the same `switch_compartment.s`. Deduplicating the code makes it easier to understand the differences between the `secure_*` variants. ### Implementation This change refactors the compartment switcher (`switch_compartment.S`) used by the `secure*` examples. It also changes the build system of the`secure*` examples, which are now built using the `malicious_compartments/build/Makefile.inter_comp_call` makefile (whereas previously each example had its own makefile) Some `inter_comp_call` examples use a correct `clean` implementation, whereas the `secure-update_ddc` example uses one that intentionally fails to clear `x20`. This commit introduces the `INTER_COMP_LEAK_X20` flag, which can be used to include one of the two implementations in `switch_compartment.S` to further deduplicate the code. `INTER_COMP_LEAK_X20` is only needed for the `secure-update_ddc` example, which relies on a "broken" `clean` implementation to validate certain security properties. `INTER_COMP_LEAK_X20` is set in the top-level makefile of the `secure` examples. This change also restructures the `inter_comp_call` directory: ``` . ├── base │   ├── include │   │   └── base.h │   ├── main.c │   ├── Makefile.morello-hybrid │   └── shared.S ├── malicious_compartments │   ├── build │   │   └── Makefile.inter_comp_call │   ├── include │   │   └── secure.h │   ├── Makefile.morello-hybrid │   ├── secure │   │   ├── compartments.s │   │   ├── main.c │   │   └── shared.S │   ├── secure-redirect_clr │   │   ├── compartments-redirect_clr.S │   │   ├── main.c │   │   └── shared.S │   ├── secure-try_deref │   │   ├── compartments-try_deref.s │   │   ├── main.c │   │   └── shared.S │   ├── secure-update_ddc │   │   ├── compartments-update_ddc.S │   │   ├── main.c │   │   └── shared-update_ddc.S │   └── shared │   ├── include │   └── switch_compartment.S └── README.md ``` **Note**: the refactoring of the `base` inter_comp_call example is outside the scope of this change. The `switch_compartment.S` variant from `base` is different from the `switch_compartment.S` version used for the `secure*` examples, so much so that its refactoring warrants a separate PR. --- .../inter_comp_call/README.md | 22 +++ .../base/Makefile.morello-hybrid | 2 +- .../inter_comp_call/{ => base}/include/base.h | 0 .../inter_comp_call/base/main.c | 2 +- .../inter_comp_call/base/shared.S | 2 +- .../Makefile.morello-hybrid | 6 +- .../build/Makefile.inter_comp_call | 58 ++++++++ .../include/secure.h | 0 .../compartments-redirect_clr.S | 0 .../secure-redirect_clr/main.c | 4 +- .../secure-redirect_clr/shared.S | 0 .../secure-try_deref/compartments-try_deref.s | 9 ++ .../secure-try_deref/main.c | 4 +- .../secure-try_deref/shared.S | 15 -- .../compartments-update_ddc.S | 0 .../secure-update_ddc/main.c | 4 +- .../secure-update_ddc/shared-update_ddc.S | 0 .../secure/compartments.s | 6 + .../secure/main.c | 4 +- .../secure/shared.S | 6 - .../shared/include/clean.s | 49 +++++++ .../shared/include/clean_leak_x20.s | 41 ++++++ .../shared/switch_compartment.S} | 66 +++------ .../Makefile.morello-hybrid | 10 -- .../secure-redirect_clr/switch_compartment.S | 130 ------------------ .../secure-try_deref/Makefile.morello-hybrid | 10 -- .../secure-update_ddc/Makefile.morello-hybrid | 10 -- .../switch_compartment-update_ddc.S | 130 ------------------ .../secure/switch_compartment.s | 125 ----------------- tests/run_tests.sh | 8 +- 30 files changed, 218 insertions(+), 505 deletions(-) create mode 100644 hybrid/compartment_examples/inter_comp_call/README.md rename hybrid/compartment_examples/inter_comp_call/{ => base}/include/base.h (100%) rename hybrid/compartment_examples/inter_comp_call/{secure => malicious_compartments}/Makefile.morello-hybrid (65%) create mode 100644 hybrid/compartment_examples/inter_comp_call/malicious_compartments/build/Makefile.inter_comp_call rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/include/secure.h (100%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-redirect_clr/compartments-redirect_clr.S (100%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-redirect_clr/main.c (98%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-redirect_clr/shared.S (100%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-try_deref/compartments-try_deref.s (83%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-try_deref/main.c (98%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-try_deref/shared.S (54%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-update_ddc/compartments-update_ddc.S (100%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-update_ddc/main.c (98%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure-update_ddc/shared-update_ddc.S (100%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure/compartments.s (92%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure/main.c (98%) rename hybrid/compartment_examples/inter_comp_call/{ => malicious_compartments}/secure/shared.S (84%) create mode 100644 hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean.s create mode 100644 hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean_leak_x20.s rename hybrid/compartment_examples/inter_comp_call/{secure-try_deref/switch_compartment.s => malicious_compartments/shared/switch_compartment.S} (59%) delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S delete mode 100644 hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s diff --git a/hybrid/compartment_examples/inter_comp_call/README.md b/hybrid/compartment_examples/inter_comp_call/README.md new file mode 100644 index 0000000..70f7761 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/README.md @@ -0,0 +1,22 @@ +# `inter_comp_call` examples + +These examples are not automatically built by the top-level makefiles, but are +built in a similar manner. + +## `base` + +To run the `base` example: + +``` +$ cd base && make -f Makefile.morello-hybrid run-main +``` + +## `malicious_compartments` + +To run one of the `malicious_compartments` examples: + +``` +$ cd secure && make -f Makefile.morello-hybrid run-inter_comp_call- +``` + +Refer to the top-level [README](../../../README.md) for usage details. diff --git a/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid index 84ba7ca..16fba54 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid +++ b/hybrid/compartment_examples/inter_comp_call/base/Makefile.morello-hybrid @@ -3,7 +3,7 @@ SHARED_SOURCES := shared.s CFILES := $(wildcard *.c) -ROOTDIR=../../../.. +ROOTDIR = ../../../.. include $(ROOTDIR)/build/Makefile.vars.morello-hybrid include $(ROOTDIR)/build/Makefile.vars.common diff --git a/hybrid/compartment_examples/inter_comp_call/include/base.h b/hybrid/compartment_examples/inter_comp_call/base/include/base.h similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/include/base.h rename to hybrid/compartment_examples/inter_comp_call/base/include/base.h diff --git a/hybrid/compartment_examples/inter_comp_call/base/main.c b/hybrid/compartment_examples/inter_comp_call/base/main.c index 7cab5e0..41e9e4f 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/main.c +++ b/hybrid/compartment_examples/inter_comp_call/base/main.c @@ -70,7 +70,7 @@ struct comp }; // ASM offsets, included here for validation -#include "../include/base.h" +#include "include/base.h" static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), diff --git a/hybrid/compartment_examples/inter_comp_call/base/shared.S b/hybrid/compartment_examples/inter_comp_call/base/shared.S index e45618f..4d1e0b7 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/base/shared.S @@ -1,7 +1,7 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "../include/base.h" +#include "include/base.h" .global comp_f_fn .global comp_g_fn diff --git a/hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/Makefile.morello-hybrid similarity index 65% rename from hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/Makefile.morello-hybrid index 84ba7ca..6f30886 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/Makefile.morello-hybrid +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/Makefile.morello-hybrid @@ -1,10 +1,8 @@ # Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. # SPDX-License-Identifier: MIT OR Apache-2.0 -SHARED_SOURCES := shared.s -CFILES := $(wildcard *.c) -ROOTDIR=../../../.. +ROOTDIR = ../../../.. include $(ROOTDIR)/build/Makefile.vars.morello-hybrid include $(ROOTDIR)/build/Makefile.vars.common -include $(ROOTDIR)/build/Makefile.simple +include build/Makefile.inter_comp_call diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/build/Makefile.inter_comp_call b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/build/Makefile.inter_comp_call new file mode 100644 index 0000000..21fc4b0 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/build/Makefile.inter_comp_call @@ -0,0 +1,58 @@ +# Copyright (c) 2020-2022 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the shared_object example. +# This should not be invoked directly. + +ifndef BINDIR +$(error build/Makefile.vars should set BINDIR) +endif + +ifdef RUNDIR +RUNDIR := $(RUNDIR)/inter_comp_call +else +RUNDIR := inter_comp_call +endif + +# Examples that use a a compartment switcher with a broken `clean` +# implementation which "leaks" the x20/c20 register to the compartment. +LEAK_X20_EXAMPLES := secure-update_ddc +LEAK_X20_TARGETS := $(addprefix run-inter_comp_call-, $(LEAK_X20_EXAMPLES)) + +EXAMPLES = secure secure-redirect_clr secure-try_deref secure-update_ddc +CFILES := $(foreach dir, $(EXAMPLES), $(wildcard $(dir)/*.c)) +SHARED_SOURCES := $(wildcard shared/*.S shared/*.s) +HFILES := $(wildcard include/*.h ../include/*.h) + +.PHONY: all clang-format clean run-inter_comp_call + +all: $(BINDIR)/main + +clang-format: + $(CFORMAT) -i $(CFILES) $(HFILES) + +clean: + if [ -d bin ]; then \ + rm -rf $(BINDIR); \ + rmdir bin --ignore-fail-on-non-empty; \ + fi + +.SECONDEXPANSION: +$(BINDIR)/%: $$(wildcard %/*.s) $$(wildcard %/*.S) $$(wildcard %/*.c) $(SHARED_SOURCES) + @mkdir -p $(BINDIR) + $(CC) $(CFLAGS) -L $(BINDIR) $+ -o $@ + +# switch_compartment.S conditionally includes one of the two clean +# implementations. Setting INTER_COMP_LEAK_X20 tells switch-compartment.S to +# select the "broken" version of clean (which simulates leaking a register). +$(LEAK_X20_TARGETS): CFLAGS += -D INTER_COMP_LEAK_X20 + +run-inter_comp_call-%: $(BINDIR)/% +ifdef SSHPORT + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) 'mkdir -p $(RUNDIR)' + scp $(SCP_OPTIONS) -P $(SSHPORT) $^ $(RUNUSER)@$(RUNHOST):$(RUNDIR) + ssh $(SSH_OPTIONS) -p $(SSHPORT) $(RUNUSER)@$(RUNHOST) -t 'cd $(RUNDIR) && ./$( #include diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/shared.S rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/compartments-try_deref.s similarity index 83% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/compartments-try_deref.s index 2d86023..db776b1 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/compartments-try_deref.s +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/compartments-try_deref.s @@ -1,3 +1,12 @@ +// Compartment functions +.global comp_f_fn +.global comp_g_fn + +// Labels for size computations +.global comp_f_fn_end +.global comp_g_fn_end +.global switch_compartment_end + /* Compartment from which we call the switcher to perform inter-compartment * transition. The call is via a capability, to update the PCC bounds * appropriately to cover `switch_compartment`. diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c similarity index 98% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c index 485c476..6337bf4 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c @@ -39,8 +39,8 @@ https://github.com/capablevms/cheri-examples/tree/master/hybrid/compartment_examples/inter_comp_call */ -#include "../../../../include/common.h" -#include "../../../include/utils.h" +#include "../../../../../include/common.h" +#include "../../../../include/utils.h" #include #include diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S similarity index 54% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S index b44cb99..7bdb6dd 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S @@ -1,17 +1,6 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "../include/secure.h" - -// Compartment functions -.global comp_f_fn -.global comp_g_fn - -// Labels for size computations -.global comp_f_fn_end -.global comp_g_fn_end -.global switch_compartment_end - .text .balign 4 @@ -23,7 +12,3 @@ executive_switch: cvtp clr, lr b switch_compartment ret clr - -#include "switch_compartment.s" - -#include "compartments-try_deref.s" diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/compartments-update_ddc.S similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/secure-update_ddc/compartments-update_ddc.S rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/compartments-update_ddc.S diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c similarity index 98% rename from hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c index 8b5da7f..923fd18 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c @@ -6,8 +6,8 @@ * fault (as attempted in `comp_f_fn` of `compartments-update_ddc.S`). */ -#include "../../../../include/common.h" -#include "../../../include/utils.h" +#include "../../../../../include/common.h" +#include "../../../../include/utils.h" #include #include diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S similarity index 100% rename from hybrid/compartment_examples/inter_comp_call/secure-update_ddc/shared-update_ddc.S rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S diff --git a/hybrid/compartment_examples/inter_comp_call/secure/compartments.s b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/compartments.s similarity index 92% rename from hybrid/compartment_examples/inter_comp_call/secure/compartments.s rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/compartments.s index f482ac4..99f81fc 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/compartments.s +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/compartments.s @@ -3,6 +3,9 @@ * appropriately to cover `switch_compartment`. */ .type comp_f_fn, "function" +.global comp_f_fn +.global comp_f_fn_end + comp_f_fn: // Set compartment ID we want to switch to mov x0, #1 @@ -29,6 +32,9 @@ comp_f_fn_end: * bounds, to ensure it is properly called. */ .type comp_g_fn, "function" +.global comp_g_fn +.global comp_g_fn_end + comp_g_fn: mrs c10, DDC mov x11, 42 diff --git a/hybrid/compartment_examples/inter_comp_call/secure/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c similarity index 98% rename from hybrid/compartment_examples/inter_comp_call/secure/main.c rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c index 485c476..6337bf4 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c @@ -39,8 +39,8 @@ https://github.com/capablevms/cheri-examples/tree/master/hybrid/compartment_examples/inter_comp_call */ -#include "../../../../include/common.h" -#include "../../../include/utils.h" +#include "../../../../../include/common.h" +#include "../../../../include/utils.h" #include #include diff --git a/hybrid/compartment_examples/inter_comp_call/secure/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S similarity index 84% rename from hybrid/compartment_examples/inter_comp_call/secure/shared.S rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S index 29b105d..e440f25 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S @@ -1,8 +1,6 @@ // Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. // SPDX-License-Identifier: MIT OR Apache-2.0 -#include "../include/secure.h" - // Compartment functions .global comp_f_fn .global comp_g_fn @@ -23,7 +21,3 @@ executive_switch: cvtp clr, lr b switch_compartment ret clr - -#include "switch_compartment.s" - -#include "compartments.s" diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean.s b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean.s new file mode 100644 index 0000000..7de37d0 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean.s @@ -0,0 +1,49 @@ +// Inner helper for cleaning capabilities from registers, either side of an +// AAPCS64 function call where some level of distrust exists between caller +// and callee. +// +// Depending on the trust model, this might not be required, but the process +// is included here for demonstration purposes. Note that if data needs to +// be scrubbed as well as capabilities, then NEON registers also need to be +// cleaned. +// +// Callers should enter at an appropriate offset so that live registers +// holding arguments and return values (c0-c7) are preserved. +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean_leak_x20.s b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean_leak_x20.s new file mode 100644 index 0000000..3d5a0d1 --- /dev/null +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/include/clean_leak_x20.s @@ -0,0 +1,41 @@ +// A broken version of `clean.S`, which simulates leaking a register, used to +// evaluate security issues +clean: + mov x0, #0 + mov x1, #0 + mov x2, #0 + mov x3, #0 + mov x4, #0 + mov x5, #0 + mov x6, #0 + mov x7, #0 + mov x8, #0 + mov x9, #0 + mov x10, #0 + mov x11, #0 + mov x12, #0 + mov x13, #0 + mov x14, #0 + mov x15, #0 + mov x16, #0 + mov x17, #0 + // x18 is the "platform register" (for some platforms). If so, it needs to + // be preserved, but here we assume that only the lower 64 bits are + // required. + mov x18, x18 + // x19-x29 are callee-saved, but only the lower 64 bits. + mov x19, x19 + // Simulate leaking a register, used to evaluate security issues + // mov x20, x20 + mov x21, x21 + mov x22, x22 + mov x23, x23 + mov x24, x24 + mov x25, x25 + mov x26, x26 + mov x27, x27 + mov x28, x28 + mov x29, x29 // FP + // We need LR (x30) to return. The call to this helper already cleaned it. + // Don't replace SP; this needs special handling by the caller anyway. + ret diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/switch_compartment.S similarity index 59% rename from hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s rename to hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/switch_compartment.S index a9557b9..d1dcf52 100644 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/switch_compartment.s +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/shared/switch_compartment.S @@ -1,9 +1,15 @@ -/* The compartment switch function. Expects compartment information to be +/* + * The compartment switch function. Expects compartment information to be * stored in memory (defined by the capability stored in register `c29`). * Performs a compartment switch based on the id saved in `x0` (currently just * an integer index into the `comps` array). */ + +#include "../include/secure.h" + .global switch_compartment +.global switch_compartment_end + .type switch_compartment, "function" switch_compartment: // Store entering compartment's DDC, and move to memory containing @@ -72,54 +78,14 @@ switch_compartment: ret clr - // Inner helper for cleaning capabilities from registers, either side of an - // AAPCS64 function call where some level of distrust exists between caller - // and callee. - // - // Depending on the trust model, this might not be required, but the process - // is included here for demonstration purposes. Note that if data needs to - // be scrubbed as well as capabilities, then NEON registers also need to be - // cleaned. - // - // Callers should enter at an appropriate offset so that live registers - // holding arguments and return values (c0-c7) are preserved. -clean: - mov x0, #0 - mov x1, #0 - mov x2, #0 - mov x3, #0 - mov x4, #0 - mov x5, #0 - mov x6, #0 - mov x7, #0 - mov x8, #0 - mov x9, #0 - mov x10, #0 - mov x11, #0 - mov x12, #0 - mov x13, #0 - mov x14, #0 - mov x15, #0 - mov x16, #0 - mov x17, #0 - // x18 is the "platform register" (for some platforms). If so, it needs to - // be preserved, but here we assume that only the lower 64 bits are - // required. - mov x18, x18 - // x19-x29 are callee-saved, but only the lower 64 bits. - mov x19, x19 - mov x20, x20 - mov x21, x21 - mov x22, x22 - mov x23, x23 - mov x24, x24 - mov x25, x25 - mov x26, x26 - mov x27, x27 - mov x28, x28 - mov x29, x29 // FP - // We need LR (x30) to return. The call to this helper already cleaned it. - // Don't replace SP; this needs special handling by the caller anyway. - ret +// Select the appropriate `clean` implementation. Some examples use a correct +// `clean` implementation, whereas `secure-update_ddc` uses one that +// intentionally fails to clear `x20`. +#ifdef INTER_COMP_LEAK_X20 + #include "include/clean_leak_x20.s" +#else + #include "include/clean.s" +#endif + switch_compartment_end: diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid deleted file mode 100644 index 0eb03fd..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/Makefile.morello-hybrid +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. -# SPDX-License-Identifier: MIT OR Apache-2.0 - -SHARED_SOURCES := $(wildcard *.S) -HFILES := $(wildcard ../include/*.h) $(wildcard ../../../include/*.h) $(wildcard ../../../../include/*.h) -ROOTDIR=../../../.. - -include $(ROOTDIR)/build/Makefile.vars.morello-hybrid -include $(ROOTDIR)/build/Makefile.vars.common -include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S b/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S deleted file mode 100644 index b61f908..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure-redirect_clr/switch_compartment.S +++ /dev/null @@ -1,130 +0,0 @@ -/* The compartment switch function. Expects compartment information to be - * stored in memory (defined by the capability stored in register `c29`). - * Performs a compartment switch based on the id saved in `x0` (currently just - * an integer index into the `comps` array). - */ - -#include "../include/secure.h" - -.global switch_compartment -.global switch_compartment_end - -.type switch_compartment, "function" -switch_compartment: - // Store entering compartment's DDC, and move to memory containing - // compartment info - mrs c2, DDC - mov x10, x0 - - // Expect switcher DDC in c29 - msr DDC, c29 - - // Get compartment to switch to data - mov x11, #COMP_SIZE - mul x10, x10, x11 - - // Load PCC, including function we are jumping to within compartment - add x11, x10, #COMP_OFFSET_PCC - ldr c0, [x29, x11] - - // Load DDC - add x11, x10, #COMP_OFFSET_DDC - ldr c1, [x29, x11] - - // Setup SP - mov x12, sp - add x11, x10, #COMP_OFFSET_STK_ADDR - ldr x11, [x29, x11] - mov sp, x11 - - // Install compartment DDC - msr DDC, c1 - - // Save old DDC (c2), old SP (x12), old CLR (clr) on stack - stp c2, clr, [sp, #-48]! - str x12, [sp, #32] - - // Stack layout at this point: - // - // `stack + size` -> ________________________ - // sp + 40 -> [ ] ^ - // sp + 32 -> [ old SP ] | - // sp + 24 -> [ old CLR (hi64) ] | - // sp + 16 -> [ old CLR (lo64) ] | - // sp + 8 -> [ old DDC (high 64) ] | DDC bounds - // sp + 0 -> [ old DDC (low 64) ] | - // : : - // `stack` -> ________________________v - - // Clean all registers, except register used to call function within - // compartment we are transitioning to - bl clean+4 - - // Jump to the function within the compartment we are switching to (this - // also sets PCC) - blr c0 - - // Clean capabilities left in the return value. - mov w0, w0 - bl clean - - // Restore the caller's context and compartment. - ldp c10, clr, [sp] - ldr x12, [sp, #32] - msr DDC, c10 - mov x10, #0 - mov sp, x12 - - ret clr - - // Inner helper for cleaning capabilities from registers, either side of an - // AAPCS64 function call where some level of distrust exists between caller - // and callee. - // - // Depending on the trust model, this might not be required, but the process - // is included here for demonstration purposes. Note that if data needs to - // be scrubbed as well as capabilities, then NEON registers also need to be - // cleaned. - // - // Callers should enter at an appropriate offset so that live registers - // holding arguments and return values (c0-c7) are preserved. -clean: - mov x0, #0 - mov x1, #0 - mov x2, #0 - mov x3, #0 - mov x4, #0 - mov x5, #0 - mov x6, #0 - mov x7, #0 - mov x8, #0 - mov x9, #0 - mov x10, #0 - mov x11, #0 - mov x12, #0 - mov x13, #0 - mov x14, #0 - mov x15, #0 - mov x16, #0 - mov x17, #0 - // x18 is the "platform register" (for some platforms). If so, it needs to - // be preserved, but here we assume that only the lower 64 bits are - // required. - mov x18, x18 - // x19-x29 are callee-saved, but only the lower 64 bits. - mov x19, x19 - mov x20, x20 - mov x21, x21 - mov x22, x22 - mov x23, x23 - mov x24, x24 - mov x25, x25 - mov x26, x26 - mov x27, x27 - mov x28, x28 - mov x29, x29 // FP - // We need LR (x30) to return. The call to this helper already cleaned it. - // Don't replace SP; this needs special handling by the caller anyway. - ret -switch_compartment_end: - diff --git a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid deleted file mode 100644 index 79b20f2..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure-try_deref/Makefile.morello-hybrid +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. -# SPDX-License-Identifier: MIT OR Apache-2.0 - -SHARED_SOURCES := shared.S -CFILES := $(wildcard *.c) -ROOTDIR=../../../.. - -include $(ROOTDIR)/build/Makefile.vars.morello-hybrid -include $(ROOTDIR)/build/Makefile.vars.common -include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid deleted file mode 100644 index 0eb03fd..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/Makefile.morello-hybrid +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2022 The CapableVMs "CHERI Examples" Contributors. -# SPDX-License-Identifier: MIT OR Apache-2.0 - -SHARED_SOURCES := $(wildcard *.S) -HFILES := $(wildcard ../include/*.h) $(wildcard ../../../include/*.h) $(wildcard ../../../../include/*.h) -ROOTDIR=../../../.. - -include $(ROOTDIR)/build/Makefile.vars.morello-hybrid -include $(ROOTDIR)/build/Makefile.vars.common -include $(ROOTDIR)/build/Makefile.simple diff --git a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S deleted file mode 100644 index 25b5026..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure-update_ddc/switch_compartment-update_ddc.S +++ /dev/null @@ -1,130 +0,0 @@ -/* The compartment switch function. Expects compartment information to be - * stored in memory (defined by the capability stored in register `c29`). - * Performs a compartment switch based on the id saved in `x0` (currently just - * an integer index into the `comps` array). - */ - -#include "../include/secure.h" - -.global switch_compartment -.global switch_compartment_end - -.type switch_compartment, "function" -switch_compartment: - // Store entering compartment's DDC, and move to memory containing - // compartment info - mrs c2, DDC - mov x10, x0 - - // Expect switcher DDC in c29 - msr DDC, c29 - - // Get compartment to switch to data - mov x11, #COMP_SIZE - mul x10, x10, x11 - - // Load PCC, including function we are jumping to within compartment - add x11, x10, #COMP_OFFSET_PCC - ldr c0, [x29, x11] - - // Load DDC - add x11, x10, #COMP_OFFSET_DDC - ldr c1, [x29, x11] - - // Setup SP - mov x12, sp - add x11, x10, #COMP_OFFSET_STK_ADDR - ldr x11, [x29, x11] - mov sp, x11 - - // Install compartment DDC - msr DDC, c1 - - // Save old DDC (c2), old SP (x12), old CLR (clr) on stack - stp c2, clr, [sp, #-48]! - str x12, [sp, #32] - - // Stack layout at this point: - // - // `stack + size` -> ________________________ - // sp + 40 -> [ ] ^ - // sp + 32 -> [ old SP ] | - // sp + 24 -> [ old CLR (hi64) ] | - // sp + 16 -> [ old CLR (lo64) ] | - // sp + 8 -> [ old DDC (high 64) ] | DDC bounds - // sp + 0 -> [ old DDC (low 64) ] | - // : : - // `stack` -> ________________________v - - // Clean all registers, but leak address of switcher DDC in `x20` - bl clean+4 - - // Jump to the function within the compartment we are switching to (this - // also sets PCC) - blr c0 - - // Clean capabilities left in the return value. - mov w0, w0 - bl clean - - // Restore the caller's context and compartment. - ldp c10, clr, [sp] - ldr x12, [sp, #32] - msr DDC, c10 - mov x10, #0 - mov sp, x12 - - ret clr - - // Inner helper for cleaning capabilities from registers, either side of an - // AAPCS64 function call where some level of distrust exists between caller - // and callee. - // - // Depending on the trust model, this might not be required, but the process - // is included here for demonstration purposes. Note that if data needs to - // be scrubbed as well as capabilities, then NEON registers also need to be - // cleaned. - // - // Callers should enter at an appropriate offset so that live registers - // holding arguments and return values (c0-c7) are preserved. -clean: - mov x0, #0 - mov x1, #0 - mov x2, #0 - mov x3, #0 - mov x4, #0 - mov x5, #0 - mov x6, #0 - mov x7, #0 - mov x8, #0 - mov x9, #0 - mov x10, #0 - mov x11, #0 - mov x12, #0 - mov x13, #0 - mov x14, #0 - mov x15, #0 - mov x16, #0 - mov x17, #0 - // x18 is the "platform register" (for some platforms). If so, it needs to - // be preserved, but here we assume that only the lower 64 bits are - // required. - mov x18, x18 - // x19-x29 are callee-saved, but only the lower 64 bits. - mov x19, x19 - // Simulate leaking adress of switcher DDC - // mov x20, x20 - mov x21, x21 - mov x22, x22 - mov x23, x23 - mov x24, x24 - mov x25, x25 - mov x26, x26 - mov x27, x27 - mov x28, x28 - mov x29, x29 // FP - // We need LR (x30) to return. The call to this helper already cleaned it. - // Don't replace SP; this needs special handling by the caller anyway. - ret -switch_compartment_end: - diff --git a/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s b/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s deleted file mode 100644 index 5db007a..0000000 --- a/hybrid/compartment_examples/inter_comp_call/secure/switch_compartment.s +++ /dev/null @@ -1,125 +0,0 @@ -/* The compartment switch function. Expects compartment information to be - * stored in memory (defined by the capability stored in register `c29`). - * Performs a compartment switch based on the id saved in `x0` (currently just - * an integer index into the `comps` array). - */ -.global switch_compartment -.type switch_compartment, "function" -switch_compartment: - // Store entering compartment's DDC, and move to memory containing - // compartment info - mrs c2, DDC - mov x10, x0 - - // Expect switcher DDC in c29 - msr DDC, c29 - - // Get compartment to switch to data - mov x11, #COMP_SIZE - mul x10, x10, x11 - - // Load PCC, including function we are jumping to within compartment - add x11, x10, #COMP_OFFSET_PCC - ldr c0, [x29, x11] - - // Load DDC - add x11, x10, #COMP_OFFSET_DDC - ldr c1, [x29, x11] - - // Setup SP - mov x12, sp - add x11, x10, #COMP_OFFSET_STK_ADDR - ldr x11, [x29, x11] - mov sp, x11 - - // Install compartment DDC - msr DDC, c1 - - // Save old DDC (c2), old SP (x12), old CLR (clr) on stack - stp c2, clr, [sp, #-48]! - str x12, [sp, #32] - - // Stack layout at this point: - // - // `stack + size` -> ________________________ - // sp + 40 -> [ ] ^ - // sp + 32 -> [ old SP ] | - // sp + 24 -> [ old CLR (hi64) ] | - // sp + 16 -> [ old CLR (lo64) ] | - // sp + 8 -> [ old DDC (high 64) ] | DDC bounds - // sp + 0 -> [ old DDC (low 64) ] | - // : : - // `stack` -> ________________________v - - // Clean all registers, except register used to call function within - // compartment we are transitioning to - bl clean+4 - - // Jump to the function within the compartment we are switching to (this - // also sets PCC) - blr c0 - - // Clean capabilities left in the return value. - mov w0, w0 - bl clean - - // Restore the caller's context and compartment. - ldp c10, clr, [sp] - ldr x12, [sp, #32] - msr DDC, c10 - mov x10, #0 - mov sp, x12 - - ret clr - - // Inner helper for cleaning capabilities from registers, either side of an - // AAPCS64 function call where some level of distrust exists between caller - // and callee. - // - // Depending on the trust model, this might not be required, but the process - // is included here for demonstration purposes. Note that if data needs to - // be scrubbed as well as capabilities, then NEON registers also need to be - // cleaned. - // - // Callers should enter at an appropriate offset so that live registers - // holding arguments and return values (c0-c7) are preserved. -clean: - mov x0, #0 - mov x1, #0 - mov x2, #0 - mov x3, #0 - mov x4, #0 - mov x5, #0 - mov x6, #0 - mov x7, #0 - mov x8, #0 - mov x9, #0 - mov x10, #0 - mov x11, #0 - mov x12, #0 - mov x13, #0 - mov x14, #0 - mov x15, #0 - mov x16, #0 - mov x17, #0 - // x18 is the "platform register" (for some platforms). If so, it needs to - // be preserved, but here we assume that only the lower 64 bits are - // required. - mov x18, x18 - // x19-x29 are callee-saved, but only the lower 64 bits. - mov x19, x19 - mov x20, x20 - mov x21, x21 - mov x22, x22 - mov x23, x23 - mov x24, x24 - mov x25, x25 - mov x26, x26 - mov x27, x27 - mov x28, x28 - mov x29, x29 // FP - // We need LR (x30) to return. The call to this helper already cleaned it. - // Don't replace SP; this needs special handling by the caller anyway. - ret -switch_compartment_end: - diff --git a/tests/run_tests.sh b/tests/run_tests.sh index eceaa62..a86c176 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -75,14 +75,14 @@ elif [ "$1" = "morello-hybrid" ]; then # Tests that should fail run to_fail hybrid/ddc_compartment_switching ddc_compartment_switching_nok run to_fail hybrid ddc_invalid ddc_null - run to_fail hybrid/compartment_examples/inter_comp_call/secure-try_deref main - run to_fail hybrid/compartment_examples/inter_comp_call/secure-redirect_clr main - run to_fail hybrid/compartment_examples/inter_comp_call/secure-update_ddc main + run to_fail hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure-try_deref + run to_fail hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure-redirect_clr + run to_fail hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure-update_ddc # Tests that should pass run OK hybrid/ddc_compartment_switching ddc_compartment_switching run OK hybrid basic_ddc run OK hybrid/compartment_examples/inter_comp_call/base main - run OK hybrid/compartment_examples/inter_comp_call/secure main + run OK hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure run OK syscall-restrict syscall-restrict else echo "$1 not recognised." From 289fbd8632d4057d2db8b48a3562b7dd0f778bbf Mon Sep 17 00:00:00 2001 From: Gabi Moldovan Date: Fri, 11 Nov 2022 16:45:48 +0000 Subject: [PATCH 90/98] Replace unreachable `ret` instructions with `udf`. This replaces several unreachable `ret` instructions with `udf` (the `permanently undefined` instruction). Using `udf` instead of `ret` makes it more obvious that `switch_compartment` doesn't return to `executive_switch` (it returns to the caller of `executive_switch`). --- .../malicious_compartments/secure-redirect_clr/shared.S | 3 ++- .../malicious_compartments/secure-try_deref/shared.S | 3 ++- .../secure-update_ddc/shared-update_ddc.S | 3 ++- .../inter_comp_call/malicious_compartments/secure/shared.S | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S index 7bdb6dd..87cf1d5 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/shared.S @@ -11,4 +11,5 @@ executive_switch: mov x0, #0 cvtp clr, lr b switch_compartment - ret clr + // Unreachable (switch_compartment returns to main) + udf #0xdead diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S index 7bdb6dd..87cf1d5 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/shared.S @@ -11,4 +11,5 @@ executive_switch: mov x0, #0 cvtp clr, lr b switch_compartment - ret clr + // Unreachable (switch_compartment returns to main) + udf #0xdead diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S index 4148c9d..ffb7e31 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/shared-update_ddc.S @@ -12,4 +12,5 @@ executive_switch: mov x0, #0 cvtp clr, lr b switch_compartment - ret clr + // Unreachable (switch_compartment returns to main) + udf #0xdead diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S index e440f25..d87664e 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/shared.S @@ -20,4 +20,5 @@ executive_switch: mov x0, #0 cvtp clr, lr b switch_compartment - ret clr + // Unreachable (switch_compartment returns to main) + udf #0xdead From 5d03aee95fcd1d4490492433d93f1a88a580c41a Mon Sep 17 00:00:00 2001 From: H Oliver Date: Tue, 31 Jan 2023 13:26:41 +0000 Subject: [PATCH 91/98] Fixes two tests in tests/run_tests.sh which were causing the CI to fail. The two tests are capability_sharing/cap-to-file and capability_sharing/leak-capability. Previously, we expected these tests to pass. Now, it seems that the issues highlighted by these two examples may have been fixed, which, if true, means the tests should now be failing. Both tests have been changed from 'run OK' to 'run to_fail' to allow the CI to complete. --- tests/run_tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index a86c176..c87617a 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -60,15 +60,15 @@ function run { if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then # Tests that should pass run OK shared_objects shared_objects-pcc_bounds_check_main - run OK capability_sharing/cap-to-file cap_to_file run OK capability_sharing/read-cap-from-file read-cap-from-file - run OK capability_sharing/leak-capability leak-capability run OK capability_sharing/mmap-shared-vs-private private_anon_main run OK capability_sharing/mmap-shared-vs-private private_file_main run OK capability_sharing/mmap-shared-vs-private shared_anon_main run OK capability_sharing/read-cap-from-pipe main run OK capability_sharing/read-cap-from-pipe-with-sh read-cap-from-pipe-with-sh # Tests that should fail + run to_fail capability_sharing/cap-to-file cap_to_file + run to_fail capability_sharing/leak-capability leak-capability run to_fail capability_sharing/mmap-shared-vs-private shared_file_main elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS From c749ef8d082284db096718cfbebda05833b3939b Mon Sep 17 00:00:00 2001 From: H Oliver Date: Thu, 26 Jan 2023 11:17:44 +0000 Subject: [PATCH 92/98] Added test of the compare_platforms/compare_platforms_read_only example to the test script tests/run_tests.sh, to make sure the example executes as expected (it should fail). The example has a struct, 'review', with three members: - 'realname', a char* representing the reviewer's name - 'publicreview', a char* representing a review of a submitted paper - 'permissions', a char* that is initialized to just 'r' There are two sizes passed to malloc(): 0x20 and 0x1001. - 'realname' and 'permissions' are allocated the smaller size before being initialized. - 'publicreview' is allocated the larger size, and then initialized to a few sentences rejecting an imagined paper submission. The relative positions of the three pointers are checked, by a series of asserts that their respective allocations should not overlap. Then, another char* called 'newpublicreview' is created, containing a much more positive review and accepting the paper. Next, in a CHERI environment only, capabilities are used to set the members of the 'review' struct to read-only. At this point there is one execution path, exclusive to CHERI, that cannot be tested by the test script. If the command-line flag '-overwrite' is passed in, the program will attempt to change 'publicreview' to 'newpublicreview', which will SIGPROT, because all the struct's members have read-only permissions enforced by capabilities. The test script cannot currently handle arguments passed to the tests themselves, which is why this execution path cannot currently be tested. The other execution path, which can be tested by the current script, is followed both in a CHERI environment where the '-overwrite' flag is not passed in, and in a non-CHERI environment. In this second execution path, the program will try to overflow the bounds of 'realname' in order to add the character 'w' to 'permissions', the presence of which character is interpreted as conferring write permissions on all of the struct's members. The program first attempts to overflow 'realname' by one, then by multiple characters. - In a CHERI environment, this will cause a SIGPROT, though different implementations may SIGPROT at different points in the process. - In a non-CHERI environment, the overflow is expected to succeed, acquire write permissions, and rewrite the review such that the rejected paper appears to have been accepted by the reviewer. Finally, the pointer allocations are freed. This code will only be reached in a non-CHERI environment, and, depending on the environment, may crash. --- compare_platforms/Makefile.morello-purecap | 6 + compare_platforms/README.md | 11 + compare_platforms/build/Makefile.review | 11 + .../compare_platforms_read_only.c | 192 ++++++++++++++++++ tests/run_tests.sh | 1 + 5 files changed, 221 insertions(+) create mode 100644 compare_platforms/Makefile.morello-purecap create mode 100644 compare_platforms/README.md create mode 100644 compare_platforms/build/Makefile.review create mode 100644 compare_platforms/compare_platforms_read_only.c diff --git a/compare_platforms/Makefile.morello-purecap b/compare_platforms/Makefile.morello-purecap new file mode 100644 index 0000000..0436e4d --- /dev/null +++ b/compare_platforms/Makefile.morello-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.morello-purecap +include ../build/Makefile.vars.common +include build/Makefile.review \ No newline at end of file diff --git a/compare_platforms/README.md b/compare_platforms/README.md new file mode 100644 index 0000000..5642dda --- /dev/null +++ b/compare_platforms/README.md @@ -0,0 +1,11 @@ +# "compare_platform" examples + +These examples are not automatically built by the top-level makefiles, but +are +built in a similar manner: + +``` +$ make -f Makefile. +``` + +Refer to the top-level [README](../README.md) for usage details. diff --git a/compare_platforms/build/Makefile.review b/compare_platforms/build/Makefile.review new file mode 100644 index 0000000..d8bd778 --- /dev/null +++ b/compare_platforms/build/Makefile.review @@ -0,0 +1,11 @@ +# Copyright (c) 2023 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +# Common Makefile for the compare_platforms example. +# This should not be invoked directly. + +RUNDIR := $(RUNDIR)/review +HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) +export + +include ../build/Makefile.simple \ No newline at end of file diff --git a/compare_platforms/compare_platforms_read_only.c b/compare_platforms/compare_platforms_read_only.c new file mode 100644 index 0000000..8186ccc --- /dev/null +++ b/compare_platforms/compare_platforms_read_only.c @@ -0,0 +1,192 @@ +/* + * This version can be run in both a CHERI and a non-CHERI environment to compare the two. + * + * In a CHERI environment, it will: + * Step 1: Use capabilities to enforce read-only permissions + * (see https://github.com/capablevms/cheri-examples/blob/master/employee/include/employee.h). + * Step 2: Either: + * a) if command-line flag "-overwrite" was passed in, try to write to the read-only struct members, + * and SIGPROT; or b) try to exceed the bounds of an object and add write permissions (which will + * also SIGPROT). + * + * In a non-CHERI environment, it will: + * Step 1: Use can_write to check if the struct's "permissions" member includes write permissions. + * Step 2: Try to exceed the bounds of an object and add write permissions + * (see https://ctsrd-cheri.github.io/cheri-exercises/exercises/buffer-overflow-heap/index.html). + * Step 3: Rewrite the review. + */ + +#ifdef __CHERI_PURE_CAPABILITY__ +#include "../include/common.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +struct review +{ + char *realname; + char *publicreview; + char *permissions; +}; + +bool can_write(struct review *rv) +{ + return (strchr(rv->permissions, 'w') != NULL); +} + +bool change_publicreview(struct review *rv, char *newpublicreview, bool bWeak) +{ + if (can_write(rv) || (bWeak == false)) + { + printf("\nAttempting to change the public review.\n"); + rv->publicreview = newpublicreview; + return true; + } + else + { + printf("\nYou don't have permission to change the public review.\n"); + return false; + } +} + +void print_details(struct review *rv) +{ + printf("Reviewer's Real Name: %s \n", rv->realname); + printf("Public Review: %s\n", rv->publicreview); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); +} + +struct review *set_read_only(struct review *rv) +{ +#ifdef __CHERI_PURE_CAPABILITY__ + rv->realname = (char *) cheri_perms_and(rv->realname, CHERI_PERM_LOAD); + rv->publicreview = (char *) cheri_perms_and(rv->publicreview, CHERI_PERM_LOAD); + rv->permissions = (char *) cheri_perms_and(rv->permissions, CHERI_PERM_LOAD); + return (struct review *) cheri_perms_and(rv, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); +#else + assert(false); +#endif +} + +void overflow_reviewer_realname(size_t offset, size_t reps, struct review *rv) +{ + printf("\nOverflowing reviewer realname by %zx\n", reps); + memset(rv->realname + offset, 'w', reps); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); +} + +char *min(char *a, char *b) +{ + return (a > b ? b : a); +} + +char *max(char *a, char *b) +{ + return (a > b ? a : b); +} + +void assert_positions(char *realname, char *publicreview, char *permissions, size_t smsz, + size_t bigsz) +{ + if (min(min(realname, publicreview), permissions) == publicreview) + { + assert((publicreview + bigsz) <= (min(realname, permissions))); + assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); + } + else if (max(max(realname, publicreview), permissions) == publicreview) + { + assert((max(realname, permissions) + smsz) <= publicreview); + assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); + } + else + { + assert((min(realname, permissions) + smsz) <= publicreview); + assert((publicreview + bigsz) <= (max(realname, permissions))); + } +} + +void free_pointers(struct review *rv) +{ + free(rv->realname); + free(rv->publicreview); + free(rv->permissions); +} + +int main(int argc, char *argv[]) +{ + // initialization values for struct member sizes + const size_t smallsz = 0x20; + const size_t biggersz = 0x1001; + + struct review review; + + char *realname = malloc(smallsz); + strcpy(realname, "Baba Yaga"); + review.realname = realname; + + char *publicreview = malloc(biggersz); + strcpy(publicreview, + "I am a little unclear as to the contribution. I think the authors could strengthen " + "their case considerably if they conducted an RCT. Weak reject."); + review.publicreview = publicreview; + + char *permissions = malloc(smallsz); + strcpy(permissions, "r"); + review.permissions = permissions; + + print_details(&review); + + // In a non-CHERI environment, write protection is weakly enforced + bool bWeak = true; + + assert_positions(realname, publicreview, permissions, smallsz, biggersz); + + bool b_improved = false; + + char *newpublicreview = malloc(biggersz); + strcpy(newpublicreview, + "5 ***** strong accept. This author deserves two Nobels and an ice cream.\n"); + +#ifdef __CHERI_PURE_CAPABILITY__ + struct review *ro_review = set_read_only(&review); + assert((cheri_perms_get(ro_review) & CHERI_PERM_STORE) == 0); + + if ((argc > 1) && (strcmp(argv[1], "-overwrite") == 0)) + { + printf("\nThe struct is read-only so trying to change the review will make the program " + "crash.\n"); + bWeak = false; + b_improved = change_publicreview(ro_review, newpublicreview, bWeak); + assert(false); + } + fflush(stdout); + b_improved = false; +#else + b_improved = change_publicreview(&review, newpublicreview, true); +#endif + + // Contrast the behaviour of this code in a CHERI vs. non-CHERI environment. + if (b_improved == false) + { + overflow_reviewer_realname(smallsz + 1, 1, &review); + + const size_t oversz = review.permissions - review.realname + 2 - smallsz; + overflow_reviewer_realname(smallsz + 2, oversz, &review); + + // If we reached this line, we should have acquired write privileges on the review. + b_improved = change_publicreview(&review, newpublicreview, bWeak); + print_details(&review); + } + + free_pointers(&review); + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index c87617a..ad266e6 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -70,6 +70,7 @@ if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run to_fail capability_sharing/cap-to-file cap_to_file run to_fail capability_sharing/leak-capability leak-capability run to_fail capability_sharing/mmap-shared-vs-private shared_file_main + run to_fail compare_platforms compare_platforms_read_only elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS # Tests that should fail From f8183bbc1a8d0c824054964e52f2eea38a65d971 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Fri, 3 Feb 2023 14:19:38 +0000 Subject: [PATCH 93/98] Update `clang-format` * Prefer to never use tabs * Update all source files with new formatting option --- .clang-format | 2 +- allocate.c | 18 +- bounds-cxx.cpp | 12 +- bounds.c | 18 +- capability_sharing/cap-to-file/cap_to_file.c | 130 +- capability_sharing/leak-capability/read.c | 50 +- capability_sharing/leak-capability/write.c | 42 +- .../mmap-shared-vs-private/include/util.h | 24 +- .../private_anon_main.c | 4 +- .../private_file_main.c | 28 +- .../mmap-shared-vs-private/shared_anon_main.c | 4 +- .../mmap-shared-vs-private/shared_file_main.c | 30 +- .../mmap-shared-vs-private/util.c | 44 +- capability_sharing/read-cap-from-file/main.c | 52 +- .../read-cap-from-pipe-with-sh/read.c | 28 +- .../read-cap-from-pipe-with-sh/write.c | 22 +- capability_sharing/read-cap-from-pipe/main.c | 292 +-- check_length.c | 8 +- check_mask.c | 20 +- .../compare_platforms_read_only.c | 196 +- employee/full_privileges.c | 14 +- employee/include/employee.h | 28 +- employee/read_only.c | 26 +- employee/sealed.c | 54 +- .../bitmap_allocator/bitmap_alloc.c | 272 +-- .../bitmap_allocator/bitmap_alloc.h | 2 +- .../bitmap_allocator/good_client.c | 76 +- .../bump_allocator/bad_client.c | 36 +- .../bump_allocator/bump_alloc.c | 74 +- .../bump_allocator/bump_alloc.h | 2 +- .../bump_allocator/good_client.c | 48 +- .../freelist_allocator/binary_trees.c | 122 +- .../freelist_allocator/freelist_allocator.c | 264 +-- function.c | 68 +- general_bounds.c | 80 +- hybrid/basic_ddc.c | 26 +- .../compartment_examples/call_sentry/main.c | 80 +- hybrid/compartment_examples/comp_setup/main.c | 76 +- .../inter_comp_call/base/main.c | 90 +- .../secure-redirect_clr/main.c | 124 +- .../secure-try_deref/main.c | 124 +- .../secure-update_ddc/main.c | 124 +- .../malicious_compartments/secure/main.c | 124 +- .../compartment_examples/restrict_pcc/main.c | 22 +- .../ddc_compartment_switching.c | 18 +- .../ddc_compartment_switching_nok.c | 20 +- .../ddc_compartment_switching_sentry.c | 32 +- hybrid/ddc_invalid.c | 10 +- hybrid/ddc_null.c | 10 +- hybrid/include/utils.h | 8 +- include/common.h | 30 +- include/instructions.h | 1768 ++++++++--------- mmap.c | 142 +- seal.c | 34 +- sentry.c | 194 +- set_bounds.c | 46 +- setjmp.c | 42 +- shared_objects/compartment_per_object.c | 56 +- .../include/compartment_per_object.h | 10 +- shared_objects/include/find_sentries.h | 64 +- shared_objects/main.c | 78 +- shared_objects/pcc_bounds_check_main.c | 6 +- shared_objects/static_function.c | 30 +- shared_objects/static_variable.c | 22 +- shared_objects/unexported_function.c | 26 +- stackscan.c | 176 +- syscall-restrict/syscall-restrict.c | 40 +- timsort/include/timsort_lib.h | 210 +- timsort/timsort.c | 228 +-- xor_pointers.c | 106 +- 70 files changed, 3193 insertions(+), 3193 deletions(-) diff --git a/.clang-format b/.clang-format index 4f2b52b..b7e6af8 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,7 @@ BasedOnStyle: LLVM IndentWidth: 4 TabWidth: 4 -UseTab: Always +UseTab: Never AllowShortIfStatementsOnASingleLine: Never AllowShortFunctionsOnASingleLine: Empty AllowAllArgumentsOnNextLine: true diff --git a/allocate.c b/allocate.c index b556836..9782872 100644 --- a/allocate.c +++ b/allocate.c @@ -8,15 +8,15 @@ int main() { - size_t size = 31; + size_t size = 31; - printf("Size: "); - if (0 == scanf("%lu", &size)) - { - error("Extraneous input"); - return -1; - } + printf("Size: "); + if (0 == scanf("%lu", &size)) + { + error("Extraneous input"); + return -1; + } - void *memory = malloc(size); - pp_cap(memory); + void *memory = malloc(size); + pp_cap(memory); } diff --git a/bounds-cxx.cpp b/bounds-cxx.cpp index fd21aaf..dfeeb2d 100644 --- a/bounds-cxx.cpp +++ b/bounds-cxx.cpp @@ -5,10 +5,10 @@ int main() { - std::vector list = {1, 2, 3, 4}; - for (unsigned int i = 0; i <= 16; i++) - { - pp_cap(&list[i]); - std::cout << "Value: " << list[i] << std::endl; - } + std::vector list = {1, 2, 3, 4}; + for (unsigned int i = 0; i <= 16; i++) + { + pp_cap(&list[i]); + std::cout << "Value: " << list[i] << std::endl; + } } diff --git a/bounds.c b/bounds.c index a019605..1b9a517 100644 --- a/bounds.c +++ b/bounds.c @@ -12,14 +12,14 @@ int main() { - int32_t array[16] = {0}; - int32_t *typed_array = &array; + int32_t array[16] = {0}; + int32_t *typed_array = &array; - uint64_t length = cheri_length_get(typed_array); - for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 15; counter++) - { - pp_cap(typed_array + counter); - // Read value to crash - printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); - } + uint64_t length = cheri_length_get(typed_array); + for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 15; counter++) + { + pp_cap(typed_array + counter); + // Read value to crash + printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); + } } diff --git a/capability_sharing/cap-to-file/cap_to_file.c b/capability_sharing/cap-to-file/cap_to_file.c index bcdbe14..9abf7e7 100644 --- a/capability_sharing/cap-to-file/cap_to_file.c +++ b/capability_sharing/cap-to-file/cap_to_file.c @@ -16,77 +16,77 @@ int write_cap(FILE *f, uintptr_t *cap) { - size_t res = fwrite(cap, sizeof(uintptr_t), 1, f); - if (res != 1) - { - fprintf(stderr, "write failed: %zu\n", res); - return 1; - } - - return 0; + size_t res = fwrite(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "write failed: %zu\n", res); + return 1; + } + + return 0; } int read_cap(FILE *f, uintptr_t *cap) { - size_t res = fread(cap, sizeof(uintptr_t), 1, f); - if (res != 1) - { - fprintf(stderr, "read failed: %zu\n", res); - return 1; - } - - return 0; + size_t res = fread(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "read failed: %zu\n", res); + return 1; + } + + return 0; } int main(int argc, char **argv) { - int x = 1; - uintptr_t cap = (uintptr_t) &x; - FILE *f = NULL; - - if (!(f = tmpfile())) - { - perror("failed to create temporary file"); - return 1; - } - - printf("Writing capability to file: %#lp\n", (void *) cap); - assert(cheri_is_valid((void *) cap)); - - if (write_cap(f, &cap)) - { - return 1; - } - - if (fflush(f)) - { - perror("failed to flush"); - return 1; - } - - if (fseek(f, 0, SEEK_SET)) - { - perror("failed to seek"); - return 1; - } - - uintptr_t stored_cap = 0; - // 0 is not a valid capability - assert(!cheri_is_valid((void *) stored_cap)); - - if (read_cap(f, &stored_cap)) - { - return 1; - } - - printf("Read capability from file: %#lp\n", (void *) stored_cap); - assert(cap == stored_cap); - - // XXX the capability should not be valid but due to a bug in CheriBSD - // this assertion succeeds! - assert(cheri_is_valid((void *) stored_cap)); - - fclose(f); - - return 0; + int x = 1; + uintptr_t cap = (uintptr_t) &x; + FILE *f = NULL; + + if (!(f = tmpfile())) + { + perror("failed to create temporary file"); + return 1; + } + + printf("Writing capability to file: %#lp\n", (void *) cap); + assert(cheri_is_valid((void *) cap)); + + if (write_cap(f, &cap)) + { + return 1; + } + + if (fflush(f)) + { + perror("failed to flush"); + return 1; + } + + if (fseek(f, 0, SEEK_SET)) + { + perror("failed to seek"); + return 1; + } + + uintptr_t stored_cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) stored_cap)); + + if (read_cap(f, &stored_cap)) + { + return 1; + } + + printf("Read capability from file: %#lp\n", (void *) stored_cap); + assert(cap == stored_cap); + + // XXX the capability should not be valid but due to a bug in CheriBSD + // this assertion succeeds! + assert(cheri_is_valid((void *) stored_cap)); + + fclose(f); + + return 0; } diff --git a/capability_sharing/leak-capability/read.c b/capability_sharing/leak-capability/read.c index cd7675e..28cb304 100644 --- a/capability_sharing/leak-capability/read.c +++ b/capability_sharing/leak-capability/read.c @@ -30,37 +30,37 @@ */ int main(int argc, char **argv) { - uintptr_t cap = 0; - // 0 is not a valid capability - assert(!cheri_is_valid((void *) cap)); + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); - FILE *f = fopen(CAP_FILE, "r"); + FILE *f = fopen(CAP_FILE, "r"); - if (f == NULL) - { - perror("failed to open file"); - return 1; - } + if (f == NULL) + { + perror("failed to open file"); + return 1; + } - size_t n = fread(&cap, sizeof(uintptr_t), 1, f); + size_t n = fread(&cap, sizeof(uintptr_t), 1, f); - if (n != 1) - { - fprintf(stderr, "read failed: %zu\n", n); - return 1; - } + if (n != 1) + { + fprintf(stderr, "read failed: %zu\n", n); + return 1; + } - if (fclose(f)) - { - perror("failed to close file"); - return 1; - } + if (fclose(f)) + { + perror("failed to close file"); + return 1; + } - printf("Read capability from %s: %#lp\n", CAP_FILE, (void *) cap); + printf("Read capability from %s: %#lp\n", CAP_FILE, (void *) cap); - // XXX the capability should not be valid but due to a bug in CheriBSD - // this assertion succeeds! - assert(cheri_is_valid((void *) cap)); + // XXX the capability should not be valid but due to a bug in CheriBSD + // this assertion succeeds! + assert(cheri_is_valid((void *) cap)); - return 0; + return 0; } diff --git a/capability_sharing/leak-capability/write.c b/capability_sharing/leak-capability/write.c index 9d20c30..abaa9c2 100644 --- a/capability_sharing/leak-capability/write.c +++ b/capability_sharing/leak-capability/write.c @@ -30,32 +30,32 @@ */ int main(int argc, char **argv) { - FILE *f = fopen(CAP_FILE, "w"); + FILE *f = fopen(CAP_FILE, "w"); - if (f == NULL) - { - perror("failed to open file"); - return 1; - } + if (f == NULL) + { + perror("failed to open file"); + return 1; + } - int x = 1; - uintptr_t cap = (uintptr_t) &x; + int x = 1; + uintptr_t cap = (uintptr_t) &x; - size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); - fprintf(stderr, "Wrote capability to %s: %#lp\n", CAP_FILE, (void *) cap); + fprintf(stderr, "Wrote capability to %s: %#lp\n", CAP_FILE, (void *) cap); - if (n != 1) - { - fprintf(stderr, "write failed: %zu\n", n); - return 1; - } + if (n != 1) + { + fprintf(stderr, "write failed: %zu\n", n); + return 1; + } - if (fclose(f)) - { - perror("failed to close file"); - return 1; - } + if (fclose(f)) + { + perror("failed to close file"); + return 1; + } - return 0; + return 0; } diff --git a/capability_sharing/mmap-shared-vs-private/include/util.h b/capability_sharing/mmap-shared-vs-private/include/util.h index 007a5e4..7fb12b2 100644 --- a/capability_sharing/mmap-shared-vs-private/include/util.h +++ b/capability_sharing/mmap-shared-vs-private/include/util.h @@ -6,21 +6,21 @@ #define MMAP_SIZE 4096 #define INIT_TEST_CASE(prot_in, flags_in, fd_in) \ - { \ - .prot = prot_in, .flags = flags_in, .fd = fd_in, \ - .details = "flags = " #flags_in ", prot = " #prot_in \ - } + { \ + .prot = prot_in, .flags = flags_in, .fd = fd_in, \ + .details = "flags = " #flags_in ", prot = " #prot_in \ + } typedef struct mmap_test_case { - /* The memory protection flags */ - int prot; - /* The visibility flags */ - int flags; - /* The file descriptor, if a file mapping was requested. */ - int fd; - /* A string describing the mapping (used for debugging). */ - char *details; + /* The memory protection flags */ + int prot; + /* The visibility flags */ + int flags; + /* The file descriptor, if a file mapping was requested. */ + int fd; + /* A string describing the mapping (used for debugging). */ + char *details; } mmap_test_case_t; int run_mmap_test(mmap_test_case_t); diff --git a/capability_sharing/mmap-shared-vs-private/private_anon_main.c b/capability_sharing/mmap-shared-vs-private/private_anon_main.c index 8994584..06c0b38 100644 --- a/capability_sharing/mmap-shared-vs-private/private_anon_main.c +++ b/capability_sharing/mmap-shared-vs-private/private_anon_main.c @@ -5,7 +5,7 @@ int main(int argc, char **argv) { - mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1); + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1); - return run_mmap_test(test); + return run_mmap_test(test); } diff --git a/capability_sharing/mmap-shared-vs-private/private_file_main.c b/capability_sharing/mmap-shared-vs-private/private_file_main.c index f791356..2369c72 100644 --- a/capability_sharing/mmap-shared-vs-private/private_file_main.c +++ b/capability_sharing/mmap-shared-vs-private/private_file_main.c @@ -6,23 +6,23 @@ int main(int argc, char **argv) { - FILE *f = tmpfile(); + FILE *f = tmpfile(); - if (f == NULL) - { - perror("failed to create file"); - return 1; - } + if (f == NULL) + { + perror("failed to create file"); + return 1; + } - int errno; + int errno; - // Make sure the file is large enough - if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) - { - fprintf(stderr, "failed to extend file (error code: %d)\n", errno); - } + // Make sure the file is large enough + if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) + { + fprintf(stderr, "failed to extend file (error code: %d)\n", errno); + } - mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(f)); + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(f)); - return run_mmap_test(test); + return run_mmap_test(test); } diff --git a/capability_sharing/mmap-shared-vs-private/shared_anon_main.c b/capability_sharing/mmap-shared-vs-private/shared_anon_main.c index 999d43a..e75dc23 100644 --- a/capability_sharing/mmap-shared-vs-private/shared_anon_main.c +++ b/capability_sharing/mmap-shared-vs-private/shared_anon_main.c @@ -5,7 +5,7 @@ int main(int argc, char **argv) { - mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1); + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1); - return run_mmap_test(test); + return run_mmap_test(test); } diff --git a/capability_sharing/mmap-shared-vs-private/shared_file_main.c b/capability_sharing/mmap-shared-vs-private/shared_file_main.c index 6acb880..76c1197 100644 --- a/capability_sharing/mmap-shared-vs-private/shared_file_main.c +++ b/capability_sharing/mmap-shared-vs-private/shared_file_main.c @@ -6,24 +6,24 @@ int main(int argc, char **argv) { - FILE *f = tmpfile(); + FILE *f = tmpfile(); - if (f == NULL) - { - perror("failed to create file"); - return 1; - } + if (f == NULL) + { + perror("failed to create file"); + return 1; + } - int errno; + int errno; - // Make sure the file is large enough - if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) - { - fprintf(stderr, "failed to extend file (error code: %d)\n", errno); - } + // Make sure the file is large enough + if ((errno = posix_fallocate(fileno(f), 0, MMAP_SIZE))) + { + fprintf(stderr, "failed to extend file (error code: %d)\n", errno); + } - mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED, fileno(f)); + mmap_test_case_t test = INIT_TEST_CASE(PROT_READ | PROT_WRITE, MAP_SHARED, fileno(f)); - // Writing a capability to a file-backed mapping will trigger a segfault: - return run_mmap_test(test); + // Writing a capability to a file-backed mapping will trigger a segfault: + return run_mmap_test(test); } diff --git a/capability_sharing/mmap-shared-vs-private/util.c b/capability_sharing/mmap-shared-vs-private/util.c index 4fa4e7a..4a378a9 100644 --- a/capability_sharing/mmap-shared-vs-private/util.c +++ b/capability_sharing/mmap-shared-vs-private/util.c @@ -9,38 +9,38 @@ static void *create_mmap(mmap_test_case_t test) { - printf("creating mapping (%s, fd = %d)\n", test.details, test.fd); + printf("creating mapping (%s, fd = %d)\n", test.details, test.fd); - void *addr = mmap(NULL, MMAP_SIZE, test.prot, test.flags, test.fd, 0); + void *addr = mmap(NULL, MMAP_SIZE, test.prot, test.flags, test.fd, 0); - if (addr == MAP_FAILED) - { - perror("failed to mmap"); - return NULL; - } + if (addr == MAP_FAILED) + { + perror("failed to mmap"); + return NULL; + } - printf("mmap succeeded (requested length = %d, actual length = %zu): %#lp\n", MMAP_SIZE, - cheri_getlength(addr), addr); + printf("mmap succeeded (requested length = %d, actual length = %zu): %#lp\n", MMAP_SIZE, + cheri_getlength(addr), addr); - return addr; + return addr; } int run_mmap_test(mmap_test_case_t test) { - uintptr_t *addr = (uintptr_t *) create_mmap(test); + uintptr_t *addr = (uintptr_t *) create_mmap(test); - int x = 0; - uintptr_t cap = (uintptr_t) &x; - *addr = cap; + int x = 0; + uintptr_t cap = (uintptr_t) &x; + *addr = cap; - printf("original capability: %#lp\n", (void *) cap); - printf("stored capability: %#lp\n", (void *) *addr); + printf("original capability: %#lp\n", (void *) cap); + printf("stored capability: %#lp\n", (void *) *addr); - if (munmap(addr, MMAP_SIZE)) - { - perror("failed to unmap"); - return 1; - } + if (munmap(addr, MMAP_SIZE)) + { + perror("failed to unmap"); + return 1; + } - return 0; + return 0; } diff --git a/capability_sharing/read-cap-from-file/main.c b/capability_sharing/read-cap-from-file/main.c index b09626c..173dd48 100644 --- a/capability_sharing/read-cap-from-file/main.c +++ b/capability_sharing/read-cap-from-file/main.c @@ -18,41 +18,41 @@ int read_cap(FILE *f, uintptr_t *cap) { - size_t res = fread(cap, sizeof(uintptr_t), 1, f); - if (res != 1) - { - fprintf(stderr, "read failed: %zu\n", res); - return 1; - } - - return 0; + size_t res = fread(cap, sizeof(uintptr_t), 1, f); + if (res != 1) + { + fprintf(stderr, "read failed: %zu\n", res); + return 1; + } + + return 0; } int main(int argc, char **argv) { - int x = 1; - FILE *f = NULL; + int x = 1; + FILE *f = NULL; - if (!(f = fopen(CAP_FILE, "r"))) - { - perror("failed to open capability file"); - return 1; - } + if (!(f = fopen(CAP_FILE, "r"))) + { + perror("failed to open capability file"); + return 1; + } - uintptr_t stored_cap = 0; - // 0 is not a valid capability - assert(!cheri_is_valid((void *) stored_cap)); + uintptr_t stored_cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) stored_cap)); - if (read_cap(f, &stored_cap)) - { - return 1; - } + if (read_cap(f, &stored_cap)) + { + return 1; + } - printf("Read capability from file: %#lp\n", (void *) stored_cap); + printf("Read capability from file: %#lp\n", (void *) stored_cap); - assert(!cheri_is_valid((void *) stored_cap)); + assert(!cheri_is_valid((void *) stored_cap)); - fclose(f); + fclose(f); - return 0; + return 0; } diff --git a/capability_sharing/read-cap-from-pipe-with-sh/read.c b/capability_sharing/read-cap-from-pipe-with-sh/read.c index 67cd868..e33cf34 100644 --- a/capability_sharing/read-cap-from-pipe-with-sh/read.c +++ b/capability_sharing/read-cap-from-pipe-with-sh/read.c @@ -26,24 +26,24 @@ */ int main(int argc, char **argv) { - uintptr_t cap = 0; - // 0 is not a valid capability - assert(!cheri_is_valid((void *) cap)); + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); - freopen(NULL, "rb", stdin); + freopen(NULL, "rb", stdin); - size_t n = fread(&cap, sizeof(uintptr_t), 1, stdin); + size_t n = fread(&cap, sizeof(uintptr_t), 1, stdin); - if (n != 1) - { - fprintf(stderr, "read failed: %zu\n", n); - return 1; - } + if (n != 1) + { + fprintf(stderr, "read failed: %zu\n", n); + return 1; + } - printf("Read capability from stdin: %#lp\n", (void *) cap); + printf("Read capability from stdin: %#lp\n", (void *) cap); - // As expected, the capability read from stdin is invalid. - assert(!cheri_is_valid((void *) cap)); + // As expected, the capability read from stdin is invalid. + assert(!cheri_is_valid((void *) cap)); - return 0; + return 0; } diff --git a/capability_sharing/read-cap-from-pipe-with-sh/write.c b/capability_sharing/read-cap-from-pipe-with-sh/write.c index 8ff42b0..eb50206 100644 --- a/capability_sharing/read-cap-from-pipe-with-sh/write.c +++ b/capability_sharing/read-cap-from-pipe-with-sh/write.c @@ -28,20 +28,20 @@ */ int main(int argc, char **argv) { - freopen(NULL, "wb", stdout); + freopen(NULL, "wb", stdout); - int x = 1; - uintptr_t cap = (uintptr_t) &x; + int x = 1; + uintptr_t cap = (uintptr_t) &x; - size_t n = fwrite(&cap, sizeof(uintptr_t), 1, stdout); + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, stdout); - fprintf(stderr, "Wrote capability to stdout: %#lp\n", (void *) cap); + fprintf(stderr, "Wrote capability to stdout: %#lp\n", (void *) cap); - if (n != 1) - { - fprintf(stderr, "write failed: %zu\n", n); - return 1; - } + if (n != 1) + { + fprintf(stderr, "write failed: %zu\n", n); + return 1; + } - return 0; + return 0; } diff --git a/capability_sharing/read-cap-from-pipe/main.c b/capability_sharing/read-cap-from-pipe/main.c index 1d09704..3d22bf4 100644 --- a/capability_sharing/read-cap-from-pipe/main.c +++ b/capability_sharing/read-cap-from-pipe/main.c @@ -28,108 +28,108 @@ int read_from_pipe(int fds[2]) { - if (close(fds[PIPE_WRITE])) - { - perror("failed to close the write end of the pipe"); - return -1; - } - - uintptr_t cap = 0; - // 0 is not a valid capability - assert(!cheri_is_valid((void *) cap)); - - FILE *f = fdopen(fds[PIPE_READ], "r"); - if (f == NULL) - { - perror("fdopen fail"); - return -1; - } - - size_t n = fread(&cap, sizeof(uintptr_t), 1, f); - - if (n != 1) - { - fprintf(stderr, "read failed or EOF: %zu\n", n); - return -1; - } - - if (fclose(f)) - { - perror("failed to close stream"); - return -1; - } - - printf("Read capability from pipe: %#lp\n", (void *) cap); - - // As expected, the capability read from the pipe is invalid. - assert(!cheri_is_valid((void *) cap)); - - return 0; + if (close(fds[PIPE_WRITE])) + { + perror("failed to close the write end of the pipe"); + return -1; + } + + uintptr_t cap = 0; + // 0 is not a valid capability + assert(!cheri_is_valid((void *) cap)); + + FILE *f = fdopen(fds[PIPE_READ], "r"); + if (f == NULL) + { + perror("fdopen fail"); + return -1; + } + + size_t n = fread(&cap, sizeof(uintptr_t), 1, f); + + if (n != 1) + { + fprintf(stderr, "read failed or EOF: %zu\n", n); + return -1; + } + + if (fclose(f)) + { + perror("failed to close stream"); + return -1; + } + + printf("Read capability from pipe: %#lp\n", (void *) cap); + + // As expected, the capability read from the pipe is invalid. + assert(!cheri_is_valid((void *) cap)); + + return 0; } int write_to_pipe(int fds[2]) { - if (close(fds[PIPE_READ])) - { - perror("failed to close the read end of the pipe"); - return -1; - } - - int x = 1; - uintptr_t cap = (uintptr_t) &x; - - fprintf(stderr, "Writing capability to pipe: %#lp\n", (void *) cap); - - FILE *f = fdopen(fds[PIPE_WRITE], "w"); - if (f == NULL) - { - perror("fdopen fail"); - return -1; - } - - size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); - - if (n != 1) - { - fprintf(stderr, "write failed or EOF: %zu\n", n); - return 1; - } - - if (fclose(f)) - { - perror("failed to close stream"); - return 1; - } - - return 0; + if (close(fds[PIPE_READ])) + { + perror("failed to close the read end of the pipe"); + return -1; + } + + int x = 1; + uintptr_t cap = (uintptr_t) &x; + + fprintf(stderr, "Writing capability to pipe: %#lp\n", (void *) cap); + + FILE *f = fdopen(fds[PIPE_WRITE], "w"); + if (f == NULL) + { + perror("fdopen fail"); + return -1; + } + + size_t n = fwrite(&cap, sizeof(uintptr_t), 1, f); + + if (n != 1) + { + fprintf(stderr, "write failed or EOF: %zu\n", n); + return 1; + } + + if (fclose(f)) + { + perror("failed to close stream"); + return 1; + } + + return 0; } int spawn_child(int (*child_fn)(int[2]), int fds[2]) { - pid_t pid = fork(); - switch (pid) - { - case -1: - { - perror("failed to spawn child process"); - return -1; - } - case 0: - { - if (child_fn(fds)) - { - return -1; - } - return 0; - } - default: - { - break; - } - } - - // Parent - return pid; + pid_t pid = fork(); + switch (pid) + { + case -1: + { + perror("failed to spawn child process"); + return -1; + } + case 0: + { + if (child_fn(fds)) + { + return -1; + } + return 0; + } + default: + { + break; + } + } + + // Parent + return pid; } /* @@ -138,33 +138,33 @@ int spawn_child(int (*child_fn)(int[2]), int fds[2]) */ int run_pipe_test() { - int fds[2]; + int fds[2]; - if (pipe(fds)) - { - perror("failed to create pipe"); - return -1; - } + if (pipe(fds)) + { + perror("failed to create pipe"); + return -1; + } - // The writer process: - int res = spawn_child(write_to_pipe, fds); + // The writer process: + int res = spawn_child(write_to_pipe, fds); - // Only continue if we're the parent and no error occurred. - if (res == EXIT_CHILD || res == -1) - { - return res; - } + // Only continue if we're the parent and no error occurred. + if (res == EXIT_CHILD || res == -1) + { + return res; + } - // The reader process: - res = spawn_child(read_from_pipe, fds); + // The reader process: + res = spawn_child(read_from_pipe, fds); - // Only continue if we're the parent and no error occurred. - if (res == EXIT_CHILD || res == -1) - { - return res; - } + // Only continue if we're the parent and no error occurred. + if (res == EXIT_CHILD || res == -1) + { + return res; + } - return EXIT_PARENT; + return EXIT_PARENT; } /* @@ -172,35 +172,35 @@ int run_pipe_test() */ int main(int argc, char **argv) { - for (size_t i = 0; i < NUM_ITERATIONS; ++i) - { - switch (run_pipe_test()) - { - case -1: - { - fprintf(stderr, "failed after %zu iterations\n", i); - return -1; - } - case EXIT_CHILD: - { - return 0; - } - default: - { - // We're the parent process (continue to the next iteration). - continue; - } - } - } - - for (size_t i = 0; i < NUM_CHILDREN * NUM_ITERATIONS; ++i) - { - if (wait(NULL) == -1 && errno != ECHILD) - { - perror("failed to wait for child"); - return 1; - } - } - - return 0; + for (size_t i = 0; i < NUM_ITERATIONS; ++i) + { + switch (run_pipe_test()) + { + case -1: + { + fprintf(stderr, "failed after %zu iterations\n", i); + return -1; + } + case EXIT_CHILD: + { + return 0; + } + default: + { + // We're the parent process (continue to the next iteration). + continue; + } + } + } + + for (size_t i = 0; i < NUM_CHILDREN * NUM_ITERATIONS; ++i) + { + if (wait(NULL) == -1 && errno != ECHILD) + { + perror("failed to wait for child"); + return 1; + } + } + + return 0; } diff --git a/check_length.c b/check_length.c index 066bd55..d9c948a 100644 --- a/check_length.c +++ b/check_length.c @@ -10,8 +10,8 @@ int main() { - for (uint64_t length = 0; length <= (4096 * 2); length += 1) - { - printf("%lu\n", cheri_representable_length(length)); - } + for (uint64_t length = 0; length <= (4096 * 2); length += 1) + { + printf("%lu\n", cheri_representable_length(length)); + } } diff --git a/check_mask.c b/check_mask.c index 09884ce..4da7896 100644 --- a/check_mask.c +++ b/check_mask.c @@ -11,15 +11,15 @@ int main() { - void *base = - mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + void *base = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - uint64_t base_address = cheri_address_get(base); - for (uint64_t alignment = 0; alignment < (4096 * 10); alignment++) - { - uint64_t mask = cheri_representable_alignment_mask(base_address + alignment); - uint64_t mask1 = cheri_representable_alignment_mask(alignment); - printf("%lx %lx %lx | %lx %lx %lx\n", mask, base_address + alignment, - (base_address + alignment) & mask, mask1, alignment, alignment & mask1); - } + uint64_t base_address = cheri_address_get(base); + for (uint64_t alignment = 0; alignment < (4096 * 10); alignment++) + { + uint64_t mask = cheri_representable_alignment_mask(base_address + alignment); + uint64_t mask1 = cheri_representable_alignment_mask(alignment); + printf("%lx %lx %lx | %lx %lx %lx\n", mask, base_address + alignment, + (base_address + alignment) & mask, mask1, alignment, alignment & mask1); + } } diff --git a/compare_platforms/compare_platforms_read_only.c b/compare_platforms/compare_platforms_read_only.c index 8186ccc..9b2c25e 100644 --- a/compare_platforms/compare_platforms_read_only.c +++ b/compare_platforms/compare_platforms_read_only.c @@ -30,163 +30,163 @@ struct review { - char *realname; - char *publicreview; - char *permissions; + char *realname; + char *publicreview; + char *permissions; }; bool can_write(struct review *rv) { - return (strchr(rv->permissions, 'w') != NULL); + return (strchr(rv->permissions, 'w') != NULL); } bool change_publicreview(struct review *rv, char *newpublicreview, bool bWeak) { - if (can_write(rv) || (bWeak == false)) - { - printf("\nAttempting to change the public review.\n"); - rv->publicreview = newpublicreview; - return true; - } - else - { - printf("\nYou don't have permission to change the public review.\n"); - return false; - } + if (can_write(rv) || (bWeak == false)) + { + printf("\nAttempting to change the public review.\n"); + rv->publicreview = newpublicreview; + return true; + } + else + { + printf("\nYou don't have permission to change the public review.\n"); + return false; + } } void print_details(struct review *rv) { - printf("Reviewer's Real Name: %s \n", rv->realname); - printf("Public Review: %s\n", rv->publicreview); - printf("Permissions: %s\n", rv->permissions); - fflush(stdout); + printf("Reviewer's Real Name: %s \n", rv->realname); + printf("Public Review: %s\n", rv->publicreview); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); } struct review *set_read_only(struct review *rv) { #ifdef __CHERI_PURE_CAPABILITY__ - rv->realname = (char *) cheri_perms_and(rv->realname, CHERI_PERM_LOAD); - rv->publicreview = (char *) cheri_perms_and(rv->publicreview, CHERI_PERM_LOAD); - rv->permissions = (char *) cheri_perms_and(rv->permissions, CHERI_PERM_LOAD); - return (struct review *) cheri_perms_and(rv, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); + rv->realname = (char *) cheri_perms_and(rv->realname, CHERI_PERM_LOAD); + rv->publicreview = (char *) cheri_perms_and(rv->publicreview, CHERI_PERM_LOAD); + rv->permissions = (char *) cheri_perms_and(rv->permissions, CHERI_PERM_LOAD); + return (struct review *) cheri_perms_and(rv, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); #else - assert(false); + assert(false); #endif } void overflow_reviewer_realname(size_t offset, size_t reps, struct review *rv) { - printf("\nOverflowing reviewer realname by %zx\n", reps); - memset(rv->realname + offset, 'w', reps); - printf("Permissions: %s\n", rv->permissions); - fflush(stdout); + printf("\nOverflowing reviewer realname by %zx\n", reps); + memset(rv->realname + offset, 'w', reps); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); } char *min(char *a, char *b) { - return (a > b ? b : a); + return (a > b ? b : a); } char *max(char *a, char *b) { - return (a > b ? a : b); + return (a > b ? a : b); } void assert_positions(char *realname, char *publicreview, char *permissions, size_t smsz, - size_t bigsz) + size_t bigsz) { - if (min(min(realname, publicreview), permissions) == publicreview) - { - assert((publicreview + bigsz) <= (min(realname, permissions))); - assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); - } - else if (max(max(realname, publicreview), permissions) == publicreview) - { - assert((max(realname, permissions) + smsz) <= publicreview); - assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); - } - else - { - assert((min(realname, permissions) + smsz) <= publicreview); - assert((publicreview + bigsz) <= (max(realname, permissions))); - } + if (min(min(realname, publicreview), permissions) == publicreview) + { + assert((publicreview + bigsz) <= (min(realname, permissions))); + assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); + } + else if (max(max(realname, publicreview), permissions) == publicreview) + { + assert((max(realname, permissions) + smsz) <= publicreview); + assert((min(realname, permissions) + smsz) <= (max(realname, permissions))); + } + else + { + assert((min(realname, permissions) + smsz) <= publicreview); + assert((publicreview + bigsz) <= (max(realname, permissions))); + } } void free_pointers(struct review *rv) { - free(rv->realname); - free(rv->publicreview); - free(rv->permissions); + free(rv->realname); + free(rv->publicreview); + free(rv->permissions); } int main(int argc, char *argv[]) { - // initialization values for struct member sizes - const size_t smallsz = 0x20; - const size_t biggersz = 0x1001; + // initialization values for struct member sizes + const size_t smallsz = 0x20; + const size_t biggersz = 0x1001; - struct review review; + struct review review; - char *realname = malloc(smallsz); - strcpy(realname, "Baba Yaga"); - review.realname = realname; + char *realname = malloc(smallsz); + strcpy(realname, "Baba Yaga"); + review.realname = realname; - char *publicreview = malloc(biggersz); - strcpy(publicreview, - "I am a little unclear as to the contribution. I think the authors could strengthen " - "their case considerably if they conducted an RCT. Weak reject."); - review.publicreview = publicreview; + char *publicreview = malloc(biggersz); + strcpy(publicreview, + "I am a little unclear as to the contribution. I think the authors could strengthen " + "their case considerably if they conducted an RCT. Weak reject."); + review.publicreview = publicreview; - char *permissions = malloc(smallsz); - strcpy(permissions, "r"); - review.permissions = permissions; + char *permissions = malloc(smallsz); + strcpy(permissions, "r"); + review.permissions = permissions; - print_details(&review); + print_details(&review); - // In a non-CHERI environment, write protection is weakly enforced - bool bWeak = true; + // In a non-CHERI environment, write protection is weakly enforced + bool bWeak = true; - assert_positions(realname, publicreview, permissions, smallsz, biggersz); + assert_positions(realname, publicreview, permissions, smallsz, biggersz); - bool b_improved = false; + bool b_improved = false; - char *newpublicreview = malloc(biggersz); - strcpy(newpublicreview, - "5 ***** strong accept. This author deserves two Nobels and an ice cream.\n"); + char *newpublicreview = malloc(biggersz); + strcpy(newpublicreview, + "5 ***** strong accept. This author deserves two Nobels and an ice cream.\n"); #ifdef __CHERI_PURE_CAPABILITY__ - struct review *ro_review = set_read_only(&review); - assert((cheri_perms_get(ro_review) & CHERI_PERM_STORE) == 0); - - if ((argc > 1) && (strcmp(argv[1], "-overwrite") == 0)) - { - printf("\nThe struct is read-only so trying to change the review will make the program " - "crash.\n"); - bWeak = false; - b_improved = change_publicreview(ro_review, newpublicreview, bWeak); - assert(false); - } - fflush(stdout); - b_improved = false; + struct review *ro_review = set_read_only(&review); + assert((cheri_perms_get(ro_review) & CHERI_PERM_STORE) == 0); + + if ((argc > 1) && (strcmp(argv[1], "-overwrite") == 0)) + { + printf("\nThe struct is read-only so trying to change the review will make the program " + "crash.\n"); + bWeak = false; + b_improved = change_publicreview(ro_review, newpublicreview, bWeak); + assert(false); + } + fflush(stdout); + b_improved = false; #else - b_improved = change_publicreview(&review, newpublicreview, true); + b_improved = change_publicreview(&review, newpublicreview, true); #endif - // Contrast the behaviour of this code in a CHERI vs. non-CHERI environment. - if (b_improved == false) - { - overflow_reviewer_realname(smallsz + 1, 1, &review); + // Contrast the behaviour of this code in a CHERI vs. non-CHERI environment. + if (b_improved == false) + { + overflow_reviewer_realname(smallsz + 1, 1, &review); - const size_t oversz = review.permissions - review.realname + 2 - smallsz; - overflow_reviewer_realname(smallsz + 2, oversz, &review); + const size_t oversz = review.permissions - review.realname + 2 - smallsz; + overflow_reviewer_realname(smallsz + 2, oversz, &review); - // If we reached this line, we should have acquired write privileges on the review. - b_improved = change_publicreview(&review, newpublicreview, bWeak); - print_details(&review); - } + // If we reached this line, we should have acquired write privileges on the review. + b_improved = change_publicreview(&review, newpublicreview, bWeak); + print_details(&review); + } - free_pointers(&review); + free_pointers(&review); - return 0; + return 0; } diff --git a/employee/full_privileges.c b/employee/full_privileges.c index 821750f..6499ca7 100644 --- a/employee/full_privileges.c +++ b/employee/full_privileges.c @@ -10,11 +10,11 @@ int main() { - struct employee employee = {0, "Good", "Employee", 55000}; - double salary = 0.0; - print_details(&employee); - printf("\nInsert the new salary for this employee: \n"); - scanf("%lf", &salary); - change_salary(&employee, salary); - print_details(&employee); + struct employee employee = {0, "Good", "Employee", 55000}; + double salary = 0.0; + print_details(&employee); + printf("\nInsert the new salary for this employee: \n"); + scanf("%lf", &salary); + change_salary(&employee, salary); + print_details(&employee); } diff --git a/employee/include/employee.h b/employee/include/employee.h index aeaa920..2d20a84 100644 --- a/employee/include/employee.h +++ b/employee/include/employee.h @@ -10,25 +10,25 @@ struct employee { - int id; - char *name; - char *surname; - double salary; + int id; + char *name; + char *surname; + double salary; }; void print_details(struct employee *e) { - printf("--- EMPLOYEE ---\n"); - printf("Employee Id: %d\n", e->id); - printf("Employee Name: %s \n", e->name); - printf("Employee Surname: %s\n", e->surname); - printf("Employee Salary: %.2lf\n", e->salary); - fflush(stdout); + printf("--- EMPLOYEE ---\n"); + printf("Employee Id: %d\n", e->id); + printf("Employee Name: %s \n", e->name); + printf("Employee Surname: %s\n", e->surname); + printf("Employee Salary: %.2lf\n", e->salary); + fflush(stdout); } void change_salary(struct employee *e, double salary) { - e->salary = salary; + e->salary = salary; } /* @@ -36,7 +36,7 @@ void change_salary(struct employee *e, double salary) */ struct employee *set_read_only(struct employee *e) { - e->name = (char *) cheri_perms_and(e->name, CHERI_PERM_LOAD); - e->surname = (char *) cheri_perms_and(e->surname, CHERI_PERM_LOAD); - return (struct employee *) cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); + e->name = (char *) cheri_perms_and(e->name, CHERI_PERM_LOAD); + e->surname = (char *) cheri_perms_and(e->surname, CHERI_PERM_LOAD); + return (struct employee *) cheri_perms_and(e, CHERI_PERM_LOAD | CHERI_PERM_LOAD_CAP); } \ No newline at end of file diff --git a/employee/read_only.c b/employee/read_only.c index 141e07f..e89432a 100644 --- a/employee/read_only.c +++ b/employee/read_only.c @@ -12,17 +12,17 @@ int main() { - struct employee employee = {1000, "Good", "Employee", 55000.0}; - // set the permissions to read-only - struct employee *ro_employee = set_read_only(&employee); - assert((cheri_perms_get(ro_employee) & CHERI_PERM_STORE) == 0); - print_details(ro_employee); - double new_salary = 65000.0; - printf( - "\nThe struct is read-only so trying to change the salary to %.2lf will make the program " - "crash...\n", - new_salary); - fflush(stdout); - change_salary(ro_employee, new_salary); - print_details(ro_employee); + struct employee employee = {1000, "Good", "Employee", 55000.0}; + // set the permissions to read-only + struct employee *ro_employee = set_read_only(&employee); + assert((cheri_perms_get(ro_employee) & CHERI_PERM_STORE) == 0); + print_details(ro_employee); + double new_salary = 65000.0; + printf( + "\nThe struct is read-only so trying to change the salary to %.2lf will make the program " + "crash...\n", + new_salary); + fflush(stdout); + change_salary(ro_employee, new_salary); + print_details(ro_employee); } \ No newline at end of file diff --git a/employee/sealed.c b/employee/sealed.c index 3b18c6f..ce7d84d 100644 --- a/employee/sealed.c +++ b/employee/sealed.c @@ -13,31 +13,31 @@ int main() { - // Request special capability `sealcap` from the operating system - // in order to use it as key to seal `new_small_salary` - void *__capability sealcap; - size_t sealcap_size = sizeof(sealcap); - if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) - { - error("Fatal error. Cannot get `security.cheri.sealcap`."); - exit(1); - } - assert(cheri_perms_get(sealcap) & CHERI_PERM_SEAL); - uint8_t *new_small_salary; - // Seal `new_small_salary` using previously requested `sealcap` - new_small_salary = (uint8_t *) malloc(sizeof(uint8_t)); - new_small_salary = (uint8_t *) cheri_seal(new_small_salary, sealcap); - assert(cheri_is_sealed(new_small_salary)); - uint8_t *small_salary; - small_salary = (uint8_t *) malloc(sizeof(uint8_t)); - assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); - pp_cap(small_salary); - // Make `small_salary` read-only - small_salary = (uint8_t *) cheri_perms_and(&small_salary, CHERI_PERM_LOAD); - assert(cheri_perms_get(small_salary) & CHERI_PERM_LOAD); - pp_cap(small_salary); - // Restore it to read-write using previously sealed capability `new_small_salary` - small_salary = (uint8_t *) cheri_unseal(new_small_salary, sealcap); - assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); - pp_cap(small_salary); + // Request special capability `sealcap` from the operating system + // in order to use it as key to seal `new_small_salary` + void *__capability sealcap; + size_t sealcap_size = sizeof(sealcap); + if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) + { + error("Fatal error. Cannot get `security.cheri.sealcap`."); + exit(1); + } + assert(cheri_perms_get(sealcap) & CHERI_PERM_SEAL); + uint8_t *new_small_salary; + // Seal `new_small_salary` using previously requested `sealcap` + new_small_salary = (uint8_t *) malloc(sizeof(uint8_t)); + new_small_salary = (uint8_t *) cheri_seal(new_small_salary, sealcap); + assert(cheri_is_sealed(new_small_salary)); + uint8_t *small_salary; + small_salary = (uint8_t *) malloc(sizeof(uint8_t)); + assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); + pp_cap(small_salary); + // Make `small_salary` read-only + small_salary = (uint8_t *) cheri_perms_and(&small_salary, CHERI_PERM_LOAD); + assert(cheri_perms_get(small_salary) & CHERI_PERM_LOAD); + pp_cap(small_salary); + // Restore it to read-write using previously sealed capability `new_small_salary` + small_salary = (uint8_t *) cheri_unseal(new_small_salary, sealcap); + assert(cheri_perms_get(small_salary) & (CHERI_PERM_LOAD | CHERI_PERM_STORE)); + pp_cap(small_salary); } diff --git a/example_allocators/bitmap_allocator/bitmap_alloc.c b/example_allocators/bitmap_allocator/bitmap_alloc.c index 0be9772..08d81f2 100644 --- a/example_allocators/bitmap_allocator/bitmap_alloc.c +++ b/example_allocators/bitmap_allocator/bitmap_alloc.c @@ -43,72 +43,72 @@ #define BITS_PER_BYTE 8 -char *buffer = NULL; /* allocation buffer */ +char *buffer = NULL; /* allocation buffer */ unsigned char *bitmap = NULL; /* bitmap for the buffer */ -int buffer_size = 0; /* size of buffer (in bytes) */ -int bitmap_size = 0; /* size of bitmap (in bytes) */ +int buffer_size = 0; /* size of buffer (in bytes) */ +int bitmap_size = 0; /* size of bitmap (in bytes) */ int bytes_per_chunk = 0; /* size of single chunk (in bytes) */ void init_alloc(int num_chunks, int chunk_size) { - int i = 0; - - /* we need to increase the num_chunks - * so every bit in bitmap will be used - */ - int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0) - ? num_chunks - : (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE))); - - /* we need to increase the chunk_size - * so chunks will be CHERI aligned - * (i.e. 16 bytes for RISC-V 64-bit arch) - */ - int adjusted_chunk_size = - (chunk_size % (sizeof(void *)) == 0) - ? chunk_size - : (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *)))); - - /* check this chunk size is small enough so we can represent - * bounds precisely with CHERI compressed representation - */ - adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size); - - /* request memory for our allocation buffer */ - char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); - /* request memory for our bitmap */ - bitmap = (unsigned char *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, - PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - - if (res == MAP_FAILED || bitmap == MAP_FAILED) - { - perror("error in initial mem allocation"); - exit(-1); - } - - /* NB mmap min bounds for capability is 1 page (4K) */ - buffer = res; - /* check buffer is aligned */ - assert((uintptr_t) buffer % sizeof(void *) == 0); - /* check bitmap is aligned */ - assert((uintptr_t) bitmap % sizeof(void *) == 0); - - bytes_per_chunk = adjusted_chunk_size; - buffer_size = adjusted_num_chunks * adjusted_chunk_size; - bitmap_size = adjusted_num_chunks / BITS_PER_BYTE; - - /* zero bitmap, since all chunks are free initially */ - for (i = 0; i < bitmap_size; i++) - { - bitmap[i] = 0; - } - - // set exact bounds for buffer and bitmap? - buffer = cheri_setbounds(buffer, buffer_size); - bitmap = cheri_setbounds(bitmap, bitmap_size); - return; + int i = 0; + + /* we need to increase the num_chunks + * so every bit in bitmap will be used + */ + int adjusted_num_chunks = (num_chunks % BITS_PER_BYTE == 0) + ? num_chunks + : (num_chunks + (BITS_PER_BYTE - (num_chunks % BITS_PER_BYTE))); + + /* we need to increase the chunk_size + * so chunks will be CHERI aligned + * (i.e. 16 bytes for RISC-V 64-bit arch) + */ + int adjusted_chunk_size = + (chunk_size % (sizeof(void *)) == 0) + ? chunk_size + : (chunk_size + (sizeof(void *)) - (chunk_size % (sizeof(void *)))); + + /* check this chunk size is small enough so we can represent + * bounds precisely with CHERI compressed representation + */ + adjusted_chunk_size = cheri_representable_length(adjusted_chunk_size); + + /* request memory for our allocation buffer */ + char *res = mmap(NULL, adjusted_num_chunks * adjusted_chunk_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + /* request memory for our bitmap */ + bitmap = (unsigned char *) mmap(NULL, adjusted_num_chunks / BITS_PER_BYTE, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (res == MAP_FAILED || bitmap == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + /* NB mmap min bounds for capability is 1 page (4K) */ + buffer = res; + /* check buffer is aligned */ + assert((uintptr_t) buffer % sizeof(void *) == 0); + /* check bitmap is aligned */ + assert((uintptr_t) bitmap % sizeof(void *) == 0); + + bytes_per_chunk = adjusted_chunk_size; + buffer_size = adjusted_num_chunks * adjusted_chunk_size; + bitmap_size = adjusted_num_chunks / BITS_PER_BYTE; + + /* zero bitmap, since all chunks are free initially */ + for (i = 0; i < bitmap_size; i++) + { + bitmap[i] = 0; + } + + // set exact bounds for buffer and bitmap? + buffer = cheri_setbounds(buffer, buffer_size); + bitmap = cheri_setbounds(bitmap, bitmap_size); + return; } /* @@ -117,88 +117,88 @@ void init_alloc(int num_chunks, int chunk_size) */ char *alloc_chunk() { - unsigned char updated_byte = 0; - int chunk_index = 0; - char *chunk = NULL; - // iterate over all bits in bitmap, looking for a 0 - // when we find a 0, set it to 1 and - // return the corresponding chunk - // (setting its capability bounds) - int i = 0; - while (bitmap[i] == (unsigned char) 0xff) - { - i++; - if (i >= bitmap_size) - break; - } - // do we have a 0? - if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff) - { - // find the lowest 0 ... - int j = 0; - // right shift until bottom bit is 0 - for (j = 0; j < BITS_PER_BYTE; j++) - { - int bit = (bitmap[i] >> j) & 1; - if (bit == 0) - { - break; - } - } - // now i is the word index, j is the bit index - // set this bit to 1 ... - // and work out the chunk to allocate - updated_byte = bitmap[i] + (unsigned char) (1 << j); - bitmap[i] = updated_byte; - - chunk_index = i * BITS_PER_BYTE + j; - chunk = buffer + (chunk_index * bytes_per_chunk); - - /* restrict capability range before returning ptr */ - chunk = cheri_setbounds(chunk, bytes_per_chunk); - } - - return chunk; + unsigned char updated_byte = 0; + int chunk_index = 0; + char *chunk = NULL; + // iterate over all bits in bitmap, looking for a 0 + // when we find a 0, set it to 1 and + // return the corresponding chunk + // (setting its capability bounds) + int i = 0; + while (bitmap[i] == (unsigned char) 0xff) + { + i++; + if (i >= bitmap_size) + break; + } + // do we have a 0? + if (i < bitmap_size && bitmap[i] != (unsigned char) 0xff) + { + // find the lowest 0 ... + int j = 0; + // right shift until bottom bit is 0 + for (j = 0; j < BITS_PER_BYTE; j++) + { + int bit = (bitmap[i] >> j) & 1; + if (bit == 0) + { + break; + } + } + // now i is the word index, j is the bit index + // set this bit to 1 ... + // and work out the chunk to allocate + updated_byte = bitmap[i] + (unsigned char) (1 << j); + bitmap[i] = updated_byte; + + chunk_index = i * BITS_PER_BYTE + j; + chunk = buffer + (chunk_index * bytes_per_chunk); + + /* restrict capability range before returning ptr */ + chunk = cheri_setbounds(chunk, bytes_per_chunk); + } + + return chunk; } void free_chunk(void *chunk) { - vaddr_t base = cheri_getbase(chunk); - vaddr_t buff_base = cheri_getbase(buffer); - /* calculate chunk index in buffer */ - int chunk_index = (base - buff_base) / bytes_per_chunk; - assert(chunk_index >= 0); - /* calculate corresponding bitmap index */ - int bitmap_index = chunk_index / BITS_PER_BYTE; - assert(bitmap_index < bitmap_size); - int bitmap_offset = chunk_index % BITS_PER_BYTE; - /* set this bitmap entry to 0 */ - unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset)); - bitmap[bitmap_index] = updated_byte; - return; + vaddr_t base = cheri_getbase(chunk); + vaddr_t buff_base = cheri_getbase(buffer); + /* calculate chunk index in buffer */ + int chunk_index = (base - buff_base) / bytes_per_chunk; + assert(chunk_index >= 0); + /* calculate corresponding bitmap index */ + int bitmap_index = chunk_index / BITS_PER_BYTE; + assert(bitmap_index < bitmap_size); + int bitmap_offset = chunk_index % BITS_PER_BYTE; + /* set this bitmap entry to 0 */ + unsigned char updated_byte = bitmap[bitmap_index] & (unsigned char) (~(1 << bitmap_offset)); + bitmap[bitmap_index] = updated_byte; + return; } int num_used_chunks() { - int i = 0; - int used_chunks = 0; - - while (i < bitmap_size) - { - unsigned char x = bitmap[i]; - if (x != 0) - { - /* some used chunks here */ - unsigned char j; - for (j = 1; j <= x; j = j << 1) - { - if (x & j) - { - used_chunks++; - } - } - } - i++; - } - return used_chunks; + int i = 0; + int used_chunks = 0; + + while (i < bitmap_size) + { + unsigned char x = bitmap[i]; + if (x != 0) + { + /* some used chunks here */ + unsigned char j; + for (j = 1; j <= x; j = j << 1) + { + if (x & j) + { + used_chunks++; + } + } + } + i++; + } + return used_chunks; } diff --git a/example_allocators/bitmap_allocator/bitmap_alloc.h b/example_allocators/bitmap_allocator/bitmap_alloc.h index 642a185..b8adebe 100644 --- a/example_allocators/bitmap_allocator/bitmap_alloc.h +++ b/example_allocators/bitmap_allocator/bitmap_alloc.h @@ -12,7 +12,7 @@ void init_alloc(int num_chunks, int chunk_size); -char *alloc_chunk(); /* simplest malloc */ +char *alloc_chunk(); /* simplest malloc */ void free_chunk(void *chunk); /* simplest free */ int num_used_chunks(); /* count of used memory */ diff --git a/example_allocators/bitmap_allocator/good_client.c b/example_allocators/bitmap_allocator/good_client.c index 8afd266..548f740 100644 --- a/example_allocators/bitmap_allocator/good_client.c +++ b/example_allocators/bitmap_allocator/good_client.c @@ -23,50 +23,50 @@ /* trivial linked list data structure */ typedef struct _list { - int payload; - struct _list *next; - ; + int payload; + struct _list *next; + ; } list; int main() { - unsigned int i = 0; - unsigned int n = 1; - list *l = NULL; + unsigned int i = 0; + unsigned int n = 1; + list *l = NULL; - init_alloc(NUM_CELLS, sizeof(list)); - while (n <= NUM_CELLS) - { - /* now try to do some allocations */ - for (i = 0; i < n; i++) - { - list *tmp = (list *) alloc_chunk(); - if (tmp) - { - tmp->payload = i; - tmp->next = l; - printf("alloc cell %d\n", i); - l = tmp; - } - else - { - fprintf(stderr, "chunk allocation failed but there should be enough memory!"); - exit(-1); - } - } + init_alloc(NUM_CELLS, sizeof(list)); + while (n <= NUM_CELLS) + { + /* now try to do some allocations */ + for (i = 0; i < n; i++) + { + list *tmp = (list *) alloc_chunk(); + if (tmp) + { + tmp->payload = i; + tmp->next = l; + printf("alloc cell %d\n", i); + l = tmp; + } + else + { + fprintf(stderr, "chunk allocation failed but there should be enough memory!"); + exit(-1); + } + } - // now free this list - while (l != NULL) - { - list *tmp = l->next; - printf("free cell %d\n", l->payload); - free_chunk(l); - l = tmp; - } + // now free this list + while (l != NULL) + { + list *tmp = l->next; + printf("free cell %d\n", l->payload); + free_chunk(l); + l = tmp; + } - /* double the limit, n */ - n <<= 1; - } + /* double the limit, n */ + n <<= 1; + } - return 0; + return 0; } diff --git a/example_allocators/bump_allocator/bad_client.c b/example_allocators/bump_allocator/bad_client.c index e3c3166..b409ea9 100644 --- a/example_allocators/bump_allocator/bad_client.c +++ b/example_allocators/bump_allocator/bad_client.c @@ -24,22 +24,22 @@ int main() { - int i; - int *p; - - init_alloc(NUM_WORDS * sizeof(int)); - - p = (int *) bump_alloc(1 * sizeof(int)); - if (DEBUG_PRINTF) - pp_cap(p); - if (p) - { - *p = 42; - /* first bad op: write outside the pointer bounds */ - *(p + 1) = 0xbaadf00d; - /* second bad op: read outside the pointer bounds */ - printf("out-of-bounds read: %d\n", *(p + 1)); - } - - return 0; + int i; + int *p; + + init_alloc(NUM_WORDS * sizeof(int)); + + p = (int *) bump_alloc(1 * sizeof(int)); + if (DEBUG_PRINTF) + pp_cap(p); + if (p) + { + *p = 42; + /* first bad op: write outside the pointer bounds */ + *(p + 1) = 0xbaadf00d; + /* second bad op: read outside the pointer bounds */ + printf("out-of-bounds read: %d\n", *(p + 1)); + } + + return 0; } diff --git a/example_allocators/bump_allocator/bump_alloc.c b/example_allocators/bump_allocator/bump_alloc.c index 0a2d29a..e8317c2 100644 --- a/example_allocators/bump_allocator/bump_alloc.c +++ b/example_allocators/bump_allocator/bump_alloc.c @@ -20,26 +20,26 @@ #include "bump_alloc.h" -int count = 0; /* number of bytes allocated so far*/ -int max = 0; /* upper limit for count */ +int count = 0; /* number of bytes allocated so far*/ +int max = 0; /* upper limit for count */ char *buffer = NULL; /* the allocation buffer */ void init_alloc(int size_in_bytes) { - /* request memory for our allocation buffer - * NB mmap min bounds for capability is 1 page (4K) - */ - char *res = mmap(NULL, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + /* request memory for our allocation buffer + * NB mmap min bounds for capability is 1 page (4K) + */ + char *res = mmap(NULL, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - if (res == MAP_FAILED) - { - perror("error in initial mem allocation"); - exit(-1); - } + if (res == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } - buffer = res; - max = size_in_bytes; - return; + buffer = res; + max = size_in_bytes; + return; } /* @@ -48,31 +48,31 @@ void init_alloc(int size_in_bytes) */ char *bump_alloc(int len) { - char *chunk = buffer + count; - size_t rounded_len; /* for CHERI alignment */ - size_t new_count; /* for buffer overflow check */ + char *chunk = buffer + count; + size_t rounded_len; /* for CHERI alignment */ + size_t new_count; /* for buffer overflow check */ - /* ensure we can represent the capability accurately, - * see p30 of CHERI C/C++ Prog Guide (June 2020) - * www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947 - */ - chunk = __builtin_align_up(chunk, ~cheri_representable_alignment_mask(len) + 1); - rounded_len = cheri_representable_length(len); + /* ensure we can represent the capability accurately, + * see p30 of CHERI C/C++ Prog Guide (June 2020) + * www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947 + */ + chunk = __builtin_align_up(chunk, ~cheri_representable_alignment_mask(len) + 1); + rounded_len = cheri_representable_length(len); - new_count = (chunk + rounded_len) - buffer; - if (new_count > max) - { - /* out of bounds - don't allocate anything */ - chunk = 0; - } - else - { - /* restrict capability range before returning ptr */ - chunk = cheri_bounds_set_exact(chunk, rounded_len); + new_count = (chunk + rounded_len) - buffer; + if (new_count > max) + { + /* out of bounds - don't allocate anything */ + chunk = 0; + } + else + { + /* restrict capability range before returning ptr */ + chunk = cheri_bounds_set_exact(chunk, rounded_len); - /* update bytes allocated count */ - count = new_count; - } + /* update bytes allocated count */ + count = new_count; + } - return chunk; + return chunk; } diff --git a/example_allocators/bump_allocator/bump_alloc.h b/example_allocators/bump_allocator/bump_alloc.h index e14e0d7..c577682 100644 --- a/example_allocators/bump_allocator/bump_alloc.h +++ b/example_allocators/bump_allocator/bump_alloc.h @@ -15,4 +15,4 @@ void init_alloc(int size_in_bytes); char *bump_alloc(int bytes); /* the simplest malloc */ - /* oh, and there's no free() ! */ + /* oh, and there's no free() ! */ diff --git a/example_allocators/bump_allocator/good_client.c b/example_allocators/bump_allocator/good_client.c index 1a3d733..81b7702 100644 --- a/example_allocators/bump_allocator/good_client.c +++ b/example_allocators/bump_allocator/good_client.c @@ -20,28 +20,28 @@ int main() { - int i; - - init_alloc(NUM_WORDS * sizeof(int)); - - /* now try to do some bump pointer allocations */ - for (i = 0; i < NUM_WORDS; i++) - { - int *x = (int *) bump_alloc(1 * sizeof(int)); - if (DEBUG_PRINTF) - pp_cap(x); - if (x) - { - *x = 42; - if (DEBUG_PRINTF) - printf("stored value %d\n", *x); - } - else - { - if (DEBUG_PRINTF) - printf("bump_alloc returned null\n"); - } - } - - return 0; + int i; + + init_alloc(NUM_WORDS * sizeof(int)); + + /* now try to do some bump pointer allocations */ + for (i = 0; i < NUM_WORDS; i++) + { + int *x = (int *) bump_alloc(1 * sizeof(int)); + if (DEBUG_PRINTF) + pp_cap(x); + if (x) + { + *x = 42; + if (DEBUG_PRINTF) + printf("stored value %d\n", *x); + } + else + { + if (DEBUG_PRINTF) + printf("bump_alloc returned null\n"); + } + } + + return 0; } diff --git a/example_allocators/freelist_allocator/binary_trees.c b/example_allocators/freelist_allocator/binary_trees.c index 3d39d8f..d3313de 100644 --- a/example_allocators/freelist_allocator/binary_trees.c +++ b/example_allocators/freelist_allocator/binary_trees.c @@ -3,8 +3,8 @@ contributed by Kevin Carson compilation: - gcc -O3 -fomit-frame-pointer -funroll-loops -static binary-trees.c -lm - icc -O3 -ip -unroll -static binary-trees.c -lm + gcc -O3 -fomit-frame-pointer -funroll-loops -static binary-trees.c -lm + icc -O3 -ip -unroll -static binary-trees.c -lm *reset* */ @@ -17,103 +17,103 @@ typedef struct tn { - struct tn *left; - struct tn *right; + struct tn *left; + struct tn *right; } treeNode; treeNode *NewTreeNode(treeNode *left, treeNode *right) { - treeNode *new; + treeNode *new; - new = (treeNode *) alloc(sizeof(treeNode)); + new = (treeNode *) alloc(sizeof(treeNode)); - new->left = left; - new->right = right; + new->left = left; + new->right = right; - return new; + return new; } /* NewTreeNode() */ long ItemCheck(treeNode *tree) { - if (tree->left == NULL) - return 1; - else - return 1 + ItemCheck(tree->left) + ItemCheck(tree->right); + if (tree->left == NULL) + return 1; + else + return 1 + ItemCheck(tree->left) + ItemCheck(tree->right); } /* ItemCheck() */ treeNode *BottomUpTree(unsigned depth) { - if (depth > 0) - return NewTreeNode(BottomUpTree(depth - 1), BottomUpTree(depth - 1)); - else - return NewTreeNode(NULL, NULL); + if (depth > 0) + return NewTreeNode(BottomUpTree(depth - 1), BottomUpTree(depth - 1)); + else + return NewTreeNode(NULL, NULL); } /* BottomUpTree() */ void DeleteTree(treeNode *tree) { - if (tree->left != NULL) - { - DeleteTree(tree->left); - DeleteTree(tree->right); - } + if (tree->left != NULL) + { + DeleteTree(tree->left); + DeleteTree(tree->right); + } - dealloc(tree); + dealloc(tree); } /* DeleteTree() */ int main(int argc, char *argv[]) { - unsigned N, depth, minDepth, maxDepth, stretchDepth; - treeNode *stretchTree, *longLivedTree, *tempTree; - unsigned pages; /* mem required */ + unsigned N, depth, minDepth, maxDepth, stretchDepth; + treeNode *stretchTree, *longLivedTree, *tempTree; + unsigned pages; /* mem required */ - N = (argc > 1) ? atol(argv[1]) : 0; + N = (argc > 1) ? atol(argv[1]) : 0; - minDepth = 4; + minDepth = 4; - if ((minDepth + 2) > N) - maxDepth = minDepth + 2; - else - maxDepth = N; + if ((minDepth + 2) > N) + maxDepth = minDepth + 2; + else + maxDepth = N; - stretchDepth = maxDepth + 1; + stretchDepth = maxDepth + 1; - /* calculate mem requirements, with allocator-specific - * size-class assumptions - */ - pages = ((2 << (stretchDepth + 3)) * sizeof(treeNode)) / BYTES_IN_PAGE; - printf("treenode size is %u bytes\n", (unsigned int) sizeof(treeNode)); - printf("we need %u pages\n", pages); + /* calculate mem requirements, with allocator-specific + * size-class assumptions + */ + pages = ((2 << (stretchDepth + 3)) * sizeof(treeNode)) / BYTES_IN_PAGE; + printf("treenode size is %u bytes\n", (unsigned int) sizeof(treeNode)); + printf("we need %u pages\n", pages); - /* allocate memory pool */ - initialize(pages); + /* allocate memory pool */ + initialize(pages); - /* start creating tree data structures */ - stretchTree = BottomUpTree(stretchDepth); - printf("stretch tree of depth %u\t check: %li\n", stretchDepth, ItemCheck(stretchTree)); + /* start creating tree data structures */ + stretchTree = BottomUpTree(stretchDepth); + printf("stretch tree of depth %u\t check: %li\n", stretchDepth, ItemCheck(stretchTree)); - DeleteTree(stretchTree); + DeleteTree(stretchTree); - longLivedTree = BottomUpTree(maxDepth); + longLivedTree = BottomUpTree(maxDepth); - for (depth = minDepth; depth <= maxDepth; depth += 2) - { - long i, iterations, check; + for (depth = minDepth; depth <= maxDepth; depth += 2) + { + long i, iterations, check; - iterations = pow(2, maxDepth - depth + minDepth); + iterations = pow(2, maxDepth - depth + minDepth); - check = 0; + check = 0; - for (i = 1; i <= iterations; i++) - { - tempTree = BottomUpTree(depth); - check += ItemCheck(tempTree); - DeleteTree(tempTree); - } /* for(i = 1...) */ + for (i = 1; i <= iterations; i++) + { + tempTree = BottomUpTree(depth); + check += ItemCheck(tempTree); + DeleteTree(tempTree); + } /* for(i = 1...) */ - printf("%li\t trees of depth %u\t check: %li\n", iterations, depth, check); - } /* for(depth = minDepth...) */ + printf("%li\t trees of depth %u\t check: %li\n", iterations, depth, check); + } /* for(depth = minDepth...) */ - printf("long lived tree of depth %u\t check: %li\n", maxDepth, ItemCheck(longLivedTree)); + printf("long lived tree of depth %u\t check: %li\n", maxDepth, ItemCheck(longLivedTree)); - return 0; + return 0; } /* main() */ diff --git a/example_allocators/freelist_allocator/freelist_allocator.c b/example_allocators/freelist_allocator/freelist_allocator.c index b40a59b..a6a24f3 100644 --- a/example_allocators/freelist_allocator/freelist_allocator.c +++ b/example_allocators/freelist_allocator/freelist_allocator.c @@ -13,154 +13,154 @@ char *large_freelist = NULL; void initialize(unsigned int size_in_pages) { - /* request memory for our allocation buffer - * NB mmap min bounds for capability is 1 page (4K) - */ - size_t bytes_to_allocate = size_in_pages * BYTES_IN_PAGE; - char *res = - mmap(NULL, bytes_to_allocate, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); - - if (res == MAP_FAILED) - { - perror("error in initial mem allocation"); - exit(-1); - } - - // put in linked list pointers and - // stick into the large freelist - // give this space to the large freelist ... - large_freelist = insert_linked_list_pointers(LARGE, bytes_to_allocate, res, large_freelist); - return; + /* request memory for our allocation buffer + * NB mmap min bounds for capability is 1 page (4K) + */ + size_t bytes_to_allocate = size_in_pages * BYTES_IN_PAGE; + char *res = + mmap(NULL, bytes_to_allocate, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + + if (res == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + // put in linked list pointers and + // stick into the large freelist + // give this space to the large freelist ... + large_freelist = insert_linked_list_pointers(LARGE, bytes_to_allocate, res, large_freelist); + return; } char *insert_linked_list_pointers(size_t cell_size, size_t limit, char *start, char *freelist) { - char *curr = start; - char *next = curr + cell_size; - char *max = start + limit; - // ensure next ptr will fit into cell - assert(sizeof(void *) <= cell_size); - - while (next < max) - { - ((char **) curr)[0] = next; - curr = next; - next = curr + cell_size; - } - // at the end, concatenate this newly formed - // list with existing freelist - ((char **) curr)[0] = freelist; - - return start; + char *curr = start; + char *next = curr + cell_size; + char *max = start + limit; + // ensure next ptr will fit into cell + assert(sizeof(void *) <= cell_size); + + while (next < max) + { + ((char **) curr)[0] = next; + curr = next; + next = curr + cell_size; + } + // at the end, concatenate this newly formed + // list with existing freelist + ((char **) curr)[0] = freelist; + + return start; } char *alloc(size_t bytes) { - size_t size; - char *freelist_to_use = NULL; - char *ret = NULL; // ptr to return - - // work out which freelist to use - if (bytes <= SMALL) - { - size = SMALL; - freelist_to_use = small_freelist; - } - else if (bytes <= MEDIUM) - { - size = MEDIUM; - freelist_to_use = medium_freelist; - } - else - { - size = LARGE; - freelist_to_use = large_freelist; - } - - if (freelist_to_use == NULL) - { - // fixup freelist (if no available mem there) - char *new_space = NULL; - switch (size) - { - case SMALL: - new_space = alloc(MEDIUM); - if (new_space != NULL) - { - small_freelist = - insert_linked_list_pointers(SMALL, MEDIUM, new_space, small_freelist); - freelist_to_use = small_freelist; - // now we have replenished space... - } - break; - case MEDIUM: - new_space = alloc(LARGE); - if (new_space != NULL) - { - medium_freelist = - insert_linked_list_pointers(MEDIUM, LARGE, new_space, medium_freelist); - freelist_to_use = medium_freelist; - } - break; - default: - // stuck! no more mem! - // we will return NULL - break; - } - } - - // pop from head of freelist (if there's anything there) - if (freelist_to_use != NULL) - { - char *head = freelist_to_use; - char *tail = ((char **) head)[0]; - switch (size) - { - case SMALL: - small_freelist = tail; - break; - case MEDIUM: - medium_freelist = tail; - break; - default: - large_freelist = tail; - break; - } - ret = head; - SET_SIZE(ret, size); - } - return ret; + size_t size; + char *freelist_to_use = NULL; + char *ret = NULL; // ptr to return + + // work out which freelist to use + if (bytes <= SMALL) + { + size = SMALL; + freelist_to_use = small_freelist; + } + else if (bytes <= MEDIUM) + { + size = MEDIUM; + freelist_to_use = medium_freelist; + } + else + { + size = LARGE; + freelist_to_use = large_freelist; + } + + if (freelist_to_use == NULL) + { + // fixup freelist (if no available mem there) + char *new_space = NULL; + switch (size) + { + case SMALL: + new_space = alloc(MEDIUM); + if (new_space != NULL) + { + small_freelist = + insert_linked_list_pointers(SMALL, MEDIUM, new_space, small_freelist); + freelist_to_use = small_freelist; + // now we have replenished space... + } + break; + case MEDIUM: + new_space = alloc(LARGE); + if (new_space != NULL) + { + medium_freelist = + insert_linked_list_pointers(MEDIUM, LARGE, new_space, medium_freelist); + freelist_to_use = medium_freelist; + } + break; + default: + // stuck! no more mem! + // we will return NULL + break; + } + } + + // pop from head of freelist (if there's anything there) + if (freelist_to_use != NULL) + { + char *head = freelist_to_use; + char *tail = ((char **) head)[0]; + switch (size) + { + case SMALL: + small_freelist = tail; + break; + case MEDIUM: + medium_freelist = tail; + break; + default: + large_freelist = tail; + break; + } + ret = head; + SET_SIZE(ret, size); + } + return ret; } void dealloc(void *buffer) { - // work out the size of the buffer - size_t size; - char *freelist; - - size = GET_SIZE(buffer); - - // then prepend it to the appropriate freelist - switch (size) - { - case SMALL: - small_freelist = cons_onto_freelist(buffer, small_freelist); - break; - case MEDIUM: - medium_freelist = cons_onto_freelist(buffer, medium_freelist); - break; - default: - large_freelist = cons_onto_freelist(buffer, large_freelist); - break; - } - - return; + // work out the size of the buffer + size_t size; + char *freelist; + + size = GET_SIZE(buffer); + + // then prepend it to the appropriate freelist + switch (size) + { + case SMALL: + small_freelist = cons_onto_freelist(buffer, small_freelist); + break; + case MEDIUM: + medium_freelist = cons_onto_freelist(buffer, medium_freelist); + break; + default: + large_freelist = cons_onto_freelist(buffer, large_freelist); + break; + } + + return; } char *cons_onto_freelist(char *cell, char *freelist) { - ((char **) cell)[0] = freelist; - return cell; + ((char **) cell)[0] = freelist; + return cell; } diff --git a/function.c b/function.c index 7e58b0b..407a375 100644 --- a/function.c +++ b/function.c @@ -10,47 +10,47 @@ int gcd(int a, int b) { - if (0 == a) - { - return b; - } - else if (0 == b) - { - return a; - } - else if (a == b) - { - return a; - } - else if (a > b) - { - return gcd(a - b, b); - } - return gcd(a, b - a); + if (0 == a) + { + return b; + } + else if (0 == b) + { + return a; + } + else if (a == b) + { + return a; + } + else if (a > b) + { + return gcd(a - b, b); + } + return gcd(a, b - a); } int main() { - int a, b = 0; + int a, b = 0; - printf("Enter first number:"); - if (0 == scanf("%d", &a)) - { - error("Extraneous input"); - return -1; - } + printf("Enter first number:"); + if (0 == scanf("%d", &a)) + { + error("Extraneous input"); + return -1; + } - printf("Enter second number:"); - if (0 == scanf("%d", &b)) - { - error("Extraneous input"); - return -1; - } + printf("Enter second number:"); + if (0 == scanf("%d", &b)) + { + error("Extraneous input"); + return -1; + } - int c_gcd = gcd(a, b); - printf("The gcd of these numbers is: %d\n", c_gcd); - pp_cap(*gcd); + int c_gcd = gcd(a, b); + printf("The gcd of these numbers is: %d\n", c_gcd); + pp_cap(*gcd); - return 0; + return 0; } diff --git a/general_bounds.c b/general_bounds.c index 8c823e5..8c51e4d 100644 --- a/general_bounds.c +++ b/general_bounds.c @@ -16,50 +16,50 @@ int main(int argc, char *argv[]) { - int32_t array[12] = {0}; - int32_t *typed_array = &array; - u_int32_t bounds = 48; + int32_t array[12] = {0}; + int32_t *typed_array = &array; + u_int32_t bounds = 48; #if defined(__aarch64__) && __ARM_ARCH == 8 - // Do a dereference - uint64_t length = cheri_getlength(typed_array); - for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 11; counter++) - { - pp_cap(typed_array + counter); - // Read value to crash - if (counter == 12) - { - printf("--> On Morello (ARMv8) dereferencing outside the range" - " causes the following exception:\n"); - fflush(stdout); - } - printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); - } + // Do a dereference + uint64_t length = cheri_getlength(typed_array); + for (uint32_t counter = 0; counter <= (length / sizeof(int32_t)) + 11; counter++) + { + pp_cap(typed_array + counter); + // Read value to crash + if (counter == 12) + { + printf("--> On Morello (ARMv8) dereferencing outside the range" + " causes the following exception:\n"); + fflush(stdout); + } + printf("Count: %d, Value: %d\n", counter, *(typed_array + counter)); + } #elif defined(__riscv) - if (argc < 2) - { - // Simply increase the bounds - printf("Bounds [Choose a value greater than 48]:\n"); - if (0 == scanf("%u", &bounds)) - { - error("Extraneous input"); - } - } - // Command line argument to simplify testing - else if (atoi(argv[1]) > 48) - { - bounds = atoi(argv[1]); - } - else - { - printf("Please choose a value greater than 48."); - // This will cause the test to fail if a value lower than 64 has been chosen - exit(0); - } + if (argc < 2) + { + // Simply increase the bounds + printf("Bounds [Choose a value greater than 48]:\n"); + if (0 == scanf("%u", &bounds)) + { + error("Extraneous input"); + } + } + // Command line argument to simplify testing + else if (atoi(argv[1]) > 48) + { + bounds = atoi(argv[1]); + } + else + { + printf("Please choose a value greater than 48."); + // This will cause the test to fail if a value lower than 64 has been chosen + exit(0); + } - printf("Explicitly setting the bounds outside the range causes the following exception: "); - fflush(stdout); - int32_t custom_bounds_array = cheri_setbounds(array, bounds); + printf("Explicitly setting the bounds outside the range causes the following exception: "); + fflush(stdout); + int32_t custom_bounds_array = cheri_setbounds(array, bounds); #else #error Platform not currently supported. #endif diff --git a/hybrid/basic_ddc.c b/hybrid/basic_ddc.c index 921e246..25ead04 100644 --- a/hybrid/basic_ddc.c +++ b/hybrid/basic_ddc.c @@ -15,17 +15,17 @@ int main() { - uint16_t *some_int_ptr = (uint16_t *) malloc(sizeof(uint16_t)); - *some_int_ptr = 200; - assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); - uint32_t *some_other_int_ptr = (uint32_t *) malloc(sizeof(uint32_t)); - write_ddc((void *__capability) some_other_int_ptr); - assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); - assert(cheri_address_get(cheri_ddc_get()) == some_other_int_ptr); - // Note: this program is very simple and writing to the DDC in this fashion - // would cause a crash if the program were to execute much further. - write_ddc((void *__capability) some_int_ptr); - assert(cheri_address_get(cheri_ddc_get()) != some_other_int_ptr); - assert(cheri_address_get(cheri_ddc_get()) == some_int_ptr); - return 0; + uint16_t *some_int_ptr = (uint16_t *) malloc(sizeof(uint16_t)); + *some_int_ptr = 200; + assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); + uint32_t *some_other_int_ptr = (uint32_t *) malloc(sizeof(uint32_t)); + write_ddc((void *__capability) some_other_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) != some_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) == some_other_int_ptr); + // Note: this program is very simple and writing to the DDC in this fashion + // would cause a crash if the program were to execute much further. + write_ddc((void *__capability) some_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) != some_other_int_ptr); + assert(cheri_address_get(cheri_ddc_get()) == some_int_ptr); + return 0; } \ No newline at end of file diff --git a/hybrid/compartment_examples/call_sentry/main.c b/hybrid/compartment_examples/call_sentry/main.c index 0bac847..03db15a 100644 --- a/hybrid/compartment_examples/call_sentry/main.c +++ b/hybrid/compartment_examples/call_sentry/main.c @@ -35,58 +35,58 @@ extern int comp_fun_tr(); // The function connected to the assembly trampoline extern int switch_compartment(void *stack, size_t size, void *__capability fn_call_start, - void *__capability pcc); + void *__capability pcc); // Function to be called via a sentry from within a restricted compartment. As // the `stk` variable is within DDC, it can write to it. void comp_fun_c(uint8_t *stk) { - stk[1800] = 80; + stk[1800] = 80; - // The `add` instruction is required to correctly reset the `sp`, which is - // modified by the assembled version of this function; this might be - // compiler-specific. In this example, `sp` is used to store information to - // reset the environment, therefore we must ensure it is reset correctly - // upon return. - // - // The `ret` instruction produced by - // default only uses `lr`, instead of `clr`; using the `lr` means we would - // return with a PCC bound to this function, and not be able to execute - // within the caller. Using `clr` ensures the PCC is reset correctly. - // - // Overall, this example shows it is not a good idea to call C functions - // via sentries in hybrid mode. - // - // NOTE: As the `clr` remains in scope during the execution of this - // function, it essentially leaks the executable capability. The current - // design, and the interfacing between `C` and assembly requires this to be - // available in this function - asm("add sp, sp, #0x10; ret clr"); + // The `add` instruction is required to correctly reset the `sp`, which is + // modified by the assembled version of this function; this might be + // compiler-specific. In this example, `sp` is used to store information to + // reset the environment, therefore we must ensure it is reset correctly + // upon return. + // + // The `ret` instruction produced by + // default only uses `lr`, instead of `clr`; using the `lr` means we would + // return with a PCC bound to this function, and not be able to execute + // within the caller. Using `clr` ensures the PCC is reset correctly. + // + // Overall, this example shows it is not a good idea to call C functions + // via sentries in hybrid mode. + // + // NOTE: As the `clr` remains in scope during the execution of this + // function, it essentially leaks the executable capability. The current + // design, and the interfacing between `C` and assembly requires this to be + // available in this function + asm("add sp, sp, #0x10; ret clr"); } int main() { - uint8_t *comp_mem = malloc(5000); - size_t comp_size = 2000; + uint8_t *comp_mem = malloc(5000); + size_t comp_size = 2000; - // Create a capability which we will use to tightly bound the PCC for the - // compartment - void *__capability call_cap = (void *__capability) comp_fun_tr; - call_cap = cheri_bounds_set(call_cap, comp_size); + // Create a capability which we will use to tightly bound the PCC for the + // compartment + void *__capability call_cap = (void *__capability) comp_fun_tr; + call_cap = cheri_bounds_set(call_cap, comp_size); - // Derive a sentry from the PCC to allow calling a function outside PCC - // bounds - void *__capability comp_fun_c_sentry = cheri_pcc_get(); - comp_fun_c_sentry = cheri_address_set(comp_fun_c_sentry, (unsigned long) &comp_fun_c); + // Derive a sentry from the PCC to allow calling a function outside PCC + // bounds + void *__capability comp_fun_c_sentry = cheri_pcc_get(); + comp_fun_c_sentry = cheri_address_set(comp_fun_c_sentry, (unsigned long) &comp_fun_c); - // Set the bounds on the sentry, which restricts the PCC when executing the - // function. The value `40` here was determined by disassembling - // `comp_fun_c`. - comp_fun_c_sentry = cheri_bounds_set(comp_fun_c_sentry, 40); + // Set the bounds on the sentry, which restricts the PCC when executing the + // function. The value `40` here was determined by disassembling + // `comp_fun_c`. + comp_fun_c_sentry = cheri_bounds_set(comp_fun_c_sentry, 40); - switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c_sentry); - // - // Check the compartment function has been executed. - assert(comp_mem[1800] == 80); - return 0; + switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c_sentry); + // + // Check the compartment function has been executed. + assert(comp_mem[1800] == 80); + return 0; } diff --git a/hybrid/compartment_examples/comp_setup/main.c b/hybrid/compartment_examples/comp_setup/main.c index 194e072..dce352d 100644 --- a/hybrid/compartment_examples/comp_setup/main.c +++ b/hybrid/compartment_examples/comp_setup/main.c @@ -54,13 +54,13 @@ size_t id = 0; */ struct comp { - size_t id; - void *stack_addr; - size_t stack_len; - void *heap_addr; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *stack_addr; + size_t stack_len; + void *heap_addr; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -68,12 +68,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -86,38 +86,38 @@ struct comp comps[COMP_COUNT]; */ void executive_switch(struct comp c) { - void *comps_addr = &comps; - asm("mov x19, %0\n\t" - "mov x0, #0\n\t" - "msr CID_EL0, c0" - : - : "r"(comps_addr)); - - switch_compartment(); + void *comps_addr = &comps; + asm("mov x19, %0\n\t" + "mov x0, #0\n\t" + "msr CID_EL0, c0" + : + : "r"(comps_addr)); + + switch_compartment(); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) { - assert(id < COMP_COUNT); + assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; + struct comp new_comp; + new_comp.id = id; - new_comp.stack_addr = (void *) _start_addr; - new_comp.stack_len = comp_stack_size; - new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); - new_comp.heap_len = total_comp_size - comp_stack_size; + new_comp.stack_addr = (void *) _start_addr; + new_comp.stack_len = comp_stack_size; + new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); + new_comp.heap_len = total_comp_size - comp_stack_size; - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; - void *__capability comp_fn = (void *__capability) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, 40); - new_comp.comp_fn = comp_fn; + void *__capability comp_fn = (void *__capability) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, 40); + new_comp.comp_fn = comp_fn; - comps[id] = new_comp; - ++id; + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -126,11 +126,11 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) int main() { - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn); - executive_switch(comps[0]); + executive_switch(comps[0]); - // Check compartment did indeed execute - assert(comp_f[4000] == 42); + // Check compartment did indeed execute + assert(comp_f[4000] == 42); } diff --git a/hybrid/compartment_examples/inter_comp_call/base/main.c b/hybrid/compartment_examples/inter_comp_call/base/main.c index 41e9e4f..4f75905 100644 --- a/hybrid/compartment_examples/inter_comp_call/base/main.c +++ b/hybrid/compartment_examples/inter_comp_call/base/main.c @@ -60,13 +60,13 @@ size_t id = 0; */ struct comp { - size_t id; - void *stack_addr; - size_t stack_len; - void *heap_addr; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *stack_addr; + size_t stack_len; + void *heap_addr; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -74,12 +74,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -92,46 +92,46 @@ struct comp comps[COMP_COUNT]; */ void executive_switch(struct comp c) { - void *__capability switch_cap = (void *__capability) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, 80 * 4); + void *__capability switch_cap = (void *__capability) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, 80 * 4); - void *__capability comps_addr = (void *__capability) &comps; - comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); - asm("mov c19, %w0\n\t" - "mov c20, %w1\n\t" - "mov x0, #0\n\t" - "msr CID_EL0, c0" - : - : "r"(comps_addr), "r"(switch_cap)); + asm("mov c19, %w0\n\t" + "mov c20, %w1\n\t" + "mov x0, #0\n\t" + "msr CID_EL0, c0" + : + : "r"(comps_addr), "r"(switch_cap)); - switch_compartment(); + switch_compartment(); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) { - assert(id < COMP_COUNT); + assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; + struct comp new_comp; + new_comp.id = id; - new_comp.stack_addr = (void *) _start_addr; - new_comp.stack_len = comp_stack_size; - new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); - new_comp.heap_len = total_comp_size - comp_stack_size; + new_comp.stack_addr = (void *) _start_addr; + new_comp.stack_len = comp_stack_size; + new_comp.heap_addr = (void *) (_start_addr + comp_stack_size); + new_comp.heap_len = total_comp_size - comp_stack_size; - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; - // Set up a capability pointing to the function we want to call within the - // compartment. This will be loaded as the PCC when the function is called. - void *__capability comp_fn = (void *__capability) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, 40); - new_comp.comp_fn = comp_fn; + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, 40); + new_comp.comp_fn = comp_fn; - comps[id] = new_comp; - ++id; + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -140,13 +140,13 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)()) int main() { - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn); - uint8_t *comp_g = malloc(total_comp_size); - add_comp(comp_g, comp_g_fn); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn); - executive_switch(comps[0]); + executive_switch(comps[0]); - // Check compartment did indeed execute - assert(comp_g[4000] == 42); + // Check compartment did indeed execute + assert(comp_g[4000] == 42); } diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/main.c index ba064fe..4ce6017 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-redirect_clr/main.c @@ -70,13 +70,13 @@ void *__capability switcher_call; */ struct comp { - size_t id; - void *compartment_start; - void *stack_addr; - size_t stack_len; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -84,12 +84,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -104,54 +104,54 @@ struct comp comps[COMP_COUNT]; */ void init_comps() { - void *__capability switch_cap = (void *__capability) switch_compartment; - size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, switcher_size); - switcher_caps[1] = switch_cap; - - void *__capability comps_addr = (void *__capability) &comps; - comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); - switcher_caps[0] = comps_addr; - - switcher_call = (void *__capability) switcher_caps; - // Seal this capability to be only used via a `lpb` type call - asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) { - assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; - - new_comp.compartment_start = (void *) _start_addr; - new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); - new_comp.stack_len = comp_stack_size; - new_comp.heap_len = total_comp_size - comp_stack_size; - - // Ensure 16-byte alignment throughout the compartment bounds - assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); - assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); - assert(total_comp_size % 16 == 0); - - // When creating a compartment, store a local copy of the capability which - // will allow us to call `switch_compartment` in the heap of the compartment. - void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); - memcpy(heap_top, &switcher_call, sizeof(void *__capability)); - - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; - - // Set up a capability pointing to the function we want to call within the - // compartment. This will be loaded as the PCC when the function is called. - void *__capability comp_fn = (void *__capability) _comp_fn; - size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); - new_comp.comp_fn = comp_fn; - - comps[id] = new_comp; - ++id; + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throughout the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -160,15 +160,15 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) int main() { - init_comps(); + init_comps(); - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn, &comp_f_fn_end); - uint8_t *comp_g = malloc(total_comp_size); - add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); - executive_switch(switcher_caps[0]); + executive_switch(switcher_caps[0]); - // Check compartment did indeed execute - assert(comp_g[4000] == 42); + // Check compartment did indeed execute + assert(comp_g[4000] == 42); } diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c index 6337bf4..9eeb62e 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-try_deref/main.c @@ -99,13 +99,13 @@ void *__capability switcher_call; */ struct comp { - size_t id; - void *compartment_start; - void *stack_addr; - size_t stack_len; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -113,12 +113,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -133,54 +133,54 @@ struct comp comps[COMP_COUNT]; */ void init_comps() { - void *__capability switch_cap = (void *__capability) switch_compartment; - size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, switcher_size); - switcher_caps[1] = switch_cap; - - void *__capability comps_addr = (void *__capability) &comps; - comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); - switcher_caps[0] = comps_addr; - - switcher_call = (void *__capability) switcher_caps; - // Seal this capability to be only used via a `lpb` type call - asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) { - assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; - - new_comp.compartment_start = (void *) _start_addr; - new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); - new_comp.stack_len = comp_stack_size; - new_comp.heap_len = total_comp_size - comp_stack_size; - - // Ensure 16-byte alignment throught the compartment bounds - assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); - assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); - assert(total_comp_size % 16 == 0); - - // When creating a compartment, store a local copy of the capability which - // will allow us to call `switch_compartment` in the heap of the compartment. - void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); - memcpy(heap_top, &switcher_call, sizeof(void *__capability)); - - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; - - // Set up a capability pointing to the function we want to call within the - // compartment. This will be loaded as the PCC when the function is called. - void *__capability comp_fn = (void *__capability) _comp_fn; - size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); - new_comp.comp_fn = comp_fn; - - comps[id] = new_comp; - ++id; + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throught the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -189,15 +189,15 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) int main() { - init_comps(); + init_comps(); - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn, &comp_f_fn_end); - uint8_t *comp_g = malloc(total_comp_size); - add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); - executive_switch(switcher_caps[0]); + executive_switch(switcher_caps[0]); - // Check compartment did indeed execute - assert(comp_g[4000] == 42); + // Check compartment did indeed execute + assert(comp_g[4000] == 42); } diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c index 923fd18..49c81ff 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure-update_ddc/main.c @@ -65,13 +65,13 @@ void *__capability switcher_call; */ struct comp { - size_t id; - void *compartment_start; - void *stack_addr; - size_t stack_len; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -79,12 +79,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -99,54 +99,54 @@ struct comp comps[COMP_COUNT]; */ void init_comps() { - void *__capability switch_cap = (void *__capability) switch_compartment; - size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, switcher_size); - switcher_caps[1] = switch_cap; - - void *__capability comps_addr = (void *__capability) &comps; - comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); - switcher_caps[0] = comps_addr; - - switcher_call = (void *__capability) switcher_caps; - // Seal this capability to be only used via a `lpb` type call - asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) { - assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; - - new_comp.compartment_start = (void *) _start_addr; - new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); - new_comp.stack_len = comp_stack_size; - new_comp.heap_len = total_comp_size - comp_stack_size; - - // Ensure 16-byte alignment throughout the compartment bounds - assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); - assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); - assert(total_comp_size % 16 == 0); - - // When creating a compartment, store a local copy of the capability which - // will allow us to call `switch_compartment` in the heap of the compartment. - void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); - memcpy(heap_top, &switcher_call, sizeof(void *__capability)); - - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; - - // Set up a capability pointing to the function we want to call within the - // compartment. This will be loaded as the PCC when the function is called. - void *__capability comp_fn = (void *__capability) _comp_fn; - size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); - new_comp.comp_fn = comp_fn; - - comps[id] = new_comp; - ++id; + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throughout the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -155,15 +155,15 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) int main() { - init_comps(); + init_comps(); - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn, &comp_f_fn_end); - uint8_t *comp_g = malloc(total_comp_size); - add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); - executive_switch(switcher_caps[0]); + executive_switch(switcher_caps[0]); - // Check compartment did indeed execute - assert(comp_g[4000] == 42); + // Check compartment did indeed execute + assert(comp_g[4000] == 42); } diff --git a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c index 6337bf4..9eeb62e 100644 --- a/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c +++ b/hybrid/compartment_examples/inter_comp_call/malicious_compartments/secure/main.c @@ -99,13 +99,13 @@ void *__capability switcher_call; */ struct comp { - size_t id; - void *compartment_start; - void *stack_addr; - size_t stack_len; - size_t heap_len; - void *__capability ddc; - void *__capability comp_fn; + size_t id; + void *compartment_start; + void *stack_addr; + size_t stack_len; + size_t heap_len; + void *__capability ddc; + void *__capability comp_fn; }; // ASM offsets, included here for validation @@ -113,12 +113,12 @@ struct comp static_assert(COMP_SIZE == sizeof(struct comp), "Invalid `COMP_SIZE` provided"); static_assert(COMP_OFFSET_STK_ADDR == offsetof(struct comp, stack_addr), - "Invalid `COMP_OFFSET_STK_ADDR` provided."); + "Invalid `COMP_OFFSET_STK_ADDR` provided."); static_assert(COMP_OFFSET_STK_LEN == offsetof(struct comp, stack_len), - "Invalid `COMP_OFFSET_STK_LEN` provided."); + "Invalid `COMP_OFFSET_STK_LEN` provided."); static_assert(COMP_OFFSET_DDC == offsetof(struct comp, ddc), "Invalid `COMP_OFFSET_DDC` provided."); static_assert(COMP_OFFSET_PCC == offsetof(struct comp, comp_fn), - "Invalid `COMP_OFFSET_PCC` provided."); + "Invalid `COMP_OFFSET_PCC` provided."); struct comp comps[COMP_COUNT]; @@ -133,54 +133,54 @@ struct comp comps[COMP_COUNT]; */ void init_comps() { - void *__capability switch_cap = (void *__capability) switch_compartment; - size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; - switch_cap = cheri_bounds_set(switch_cap, switcher_size); - switcher_caps[1] = switch_cap; - - void *__capability comps_addr = (void *__capability) &comps; - comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); - switcher_caps[0] = comps_addr; - - switcher_call = (void *__capability) switcher_caps; - // Seal this capability to be only used via a `lpb` type call - asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); + void *__capability switch_cap = (void *__capability) switch_compartment; + size_t switcher_size = (uintptr_t) switch_compartment_end - (uintptr_t) switch_compartment; + switch_cap = cheri_bounds_set(switch_cap, switcher_size); + switcher_caps[1] = switch_cap; + + void *__capability comps_addr = (void *__capability) &comps; + comps_addr = cheri_bounds_set(comps_addr, COMP_COUNT * COMP_SIZE); + switcher_caps[0] = comps_addr; + + switcher_call = (void *__capability) switcher_caps; + // Seal this capability to be only used via a `lpb` type call + asm("seal %w0, %w0, lpb" : "+r"(switcher_call) :); } void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) { - assert(id < COMP_COUNT); - struct comp new_comp; - new_comp.id = id; - - new_comp.compartment_start = (void *) _start_addr; - new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); - new_comp.stack_len = comp_stack_size; - new_comp.heap_len = total_comp_size - comp_stack_size; - - // Ensure 16-byte alignment throught the compartment bounds - assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); - assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); - assert(total_comp_size % 16 == 0); - - // When creating a compartment, store a local copy of the capability which - // will allow us to call `switch_compartment` in the heap of the compartment. - void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); - memcpy(heap_top, &switcher_call, sizeof(void *__capability)); - - void *__capability comp_ddc = (void *__capability) _start_addr; - comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); - new_comp.ddc = comp_ddc; - - // Set up a capability pointing to the function we want to call within the - // compartment. This will be loaded as the PCC when the function is called. - void *__capability comp_fn = (void *__capability) _comp_fn; - size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; - comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); - new_comp.comp_fn = comp_fn; - - comps[id] = new_comp; - ++id; + assert(id < COMP_COUNT); + struct comp new_comp; + new_comp.id = id; + + new_comp.compartment_start = (void *) _start_addr; + new_comp.stack_addr = (void *) (_start_addr + comp_stack_size); + new_comp.stack_len = comp_stack_size; + new_comp.heap_len = total_comp_size - comp_stack_size; + + // Ensure 16-byte alignment throught the compartment bounds + assert(((uintptr_t) new_comp.compartment_start) % 16 == 0); + assert(((uintptr_t) new_comp.stack_addr) % 16 == 0); + assert(total_comp_size % 16 == 0); + + // When creating a compartment, store a local copy of the capability which + // will allow us to call `switch_compartment` in the heap of the compartment. + void *heap_top = (void *) (_start_addr + total_comp_size - sizeof(void *__capability)); + memcpy(heap_top, &switcher_call, sizeof(void *__capability)); + + void *__capability comp_ddc = (void *__capability) _start_addr; + comp_ddc = cheri_bounds_set(comp_ddc, total_comp_size); + new_comp.ddc = comp_ddc; + + // Set up a capability pointing to the function we want to call within the + // compartment. This will be loaded as the PCC when the function is called. + void *__capability comp_fn = (void *__capability) _comp_fn; + size_t comp_fn_size = (uintptr_t) _comp_fn_end - (uintptr_t) _comp_fn; + comp_fn = cheri_bounds_set(comp_fn, comp_fn_size); + new_comp.comp_fn = comp_fn; + + comps[id] = new_comp; + ++id; } /******************************************************************************* @@ -189,15 +189,15 @@ void add_comp(uint8_t *_start_addr, void (*_comp_fn)(), void *_comp_fn_end) int main() { - init_comps(); + init_comps(); - uint8_t *comp_f = malloc(total_comp_size); - add_comp(comp_f, comp_f_fn, &comp_f_fn_end); - uint8_t *comp_g = malloc(total_comp_size); - add_comp(comp_g, comp_g_fn, &comp_g_fn_end); + uint8_t *comp_f = malloc(total_comp_size); + add_comp(comp_f, comp_f_fn, &comp_f_fn_end); + uint8_t *comp_g = malloc(total_comp_size); + add_comp(comp_g, comp_g_fn, &comp_g_fn_end); - executive_switch(switcher_caps[0]); + executive_switch(switcher_caps[0]); - // Check compartment did indeed execute - assert(comp_g[4000] == 42); + // Check compartment did indeed execute + assert(comp_g[4000] == 42); } diff --git a/hybrid/compartment_examples/restrict_pcc/main.c b/hybrid/compartment_examples/restrict_pcc/main.c index f54292a..9165486 100644 --- a/hybrid/compartment_examples/restrict_pcc/main.c +++ b/hybrid/compartment_examples/restrict_pcc/main.c @@ -21,25 +21,25 @@ extern int comp_fun(); // The function connected to the assembly trampoline. extern int switch_compartment(void *stack, size_t size, void *__capability fn_call_start, - void *__capability pcc); + void *__capability pcc); // Function outside of PCC bounds to be called from within the compartment. void comp_fun_c(uint8_t *stk) { - // unreachable + // unreachable } int main() { - uint8_t *comp_mem = malloc(5000); - size_t comp_size = 2000; + uint8_t *comp_mem = malloc(5000); + size_t comp_size = 2000; - // Create a capability which we will use to tightly bound the PCC for the - // compartment. - void *__capability call_cap = (void *__capability) comp_fun; - call_cap = cheri_bounds_set(call_cap, comp_size); + // Create a capability which we will use to tightly bound the PCC for the + // compartment. + void *__capability call_cap = (void *__capability) comp_fun; + call_cap = cheri_bounds_set(call_cap, comp_size); - switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c); - assert(false && "Should not get here"); - return 0; + switch_compartment(comp_mem, comp_size, call_cap, comp_fun_c); + assert(false && "Should not get here"); + return 0; } diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c index 0c380d2..29a889b 100644 --- a/hybrid/ddc_compartment_switching/ddc_compartment_switching.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching.c @@ -25,17 +25,17 @@ extern int switch_compartment(void *stack, size_t size); int main() { - uint8_t *simple_block = malloc(5000); - size_t compartment_size = 2000; - simple_block[1900] = 80; - switch_compartment(simple_block, compartment_size); - return 0; + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[1900] = 80; + switch_compartment(simple_block, compartment_size); + return 0; } int compartment_simple_fun() { - uint8_t *__capability ddc_cap = cheri_ddc_get(); - assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); - assert(ddc_cap[1900] == 80); - return 0; + uint8_t *__capability ddc_cap = cheri_ddc_get(); + assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); + assert(ddc_cap[1900] == 80); + return 0; } \ No newline at end of file diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c index 5043d92..3cc0b2d 100644 --- a/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching_nok.c @@ -23,18 +23,18 @@ extern int switch_compartment(void *stack, size_t size); int main() { - uint8_t *simple_block = malloc(5000); - size_t compartment_size = 2000; - simple_block[2500] = 8; - switch_compartment(simple_block, compartment_size); - return 0; + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[2500] = 8; + switch_compartment(simple_block, compartment_size); + return 0; } int compartment_simple_fun() { - uint8_t *__capability ddc_cap = cheri_ddc_get(); - // This function can access only 2000 bytes, i.e. `compartment_size` - // So the following will go over its compartment bounds - ddc_cap[2500] = 12; - return 0; + uint8_t *__capability ddc_cap = cheri_ddc_get(); + // This function can access only 2000 bytes, i.e. `compartment_size` + // So the following will go over its compartment bounds + ddc_cap[2500] = 12; + return 0; } \ No newline at end of file diff --git a/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c index e96d9b0..be7b9c4 100644 --- a/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c +++ b/hybrid/ddc_compartment_switching/ddc_compartment_switching_sentry.c @@ -24,24 +24,24 @@ extern int switch_compartment(void *stack, size_t size); int main() { - uint8_t *simple_block = malloc(5000); - size_t compartment_size = 2000; - simple_block[1900] = 80; - - // Wrap our function in a sentry - int (*__capability wrap_fn)(void *, size_t) = - cheri_sentry_create((void *__capability) switch_compartment); - - assert(cheri_is_valid(wrap_fn)); - assert(cheri_is_sentry(wrap_fn)); - wrap_fn(simple_block, compartment_size); - return 0; + uint8_t *simple_block = malloc(5000); + size_t compartment_size = 2000; + simple_block[1900] = 80; + + // Wrap our function in a sentry + int (*__capability wrap_fn)(void *, size_t) = + cheri_sentry_create((void *__capability) switch_compartment); + + assert(cheri_is_valid(wrap_fn)); + assert(cheri_is_sentry(wrap_fn)); + wrap_fn(simple_block, compartment_size); + return 0; } int compartment_simple_fun() { - uint8_t *__capability ddc_cap = cheri_ddc_get(); - assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); - assert(ddc_cap[1900] == 80); - return 0; + uint8_t *__capability ddc_cap = cheri_ddc_get(); + assert(cheri_tag_get(ddc_cap) && cheri_length_get(ddc_cap) == 2000); + assert(ddc_cap[1900] == 80); + return 0; } diff --git a/hybrid/ddc_invalid.c b/hybrid/ddc_invalid.c index 8dc6d13..4732659 100644 --- a/hybrid/ddc_invalid.c +++ b/hybrid/ddc_invalid.c @@ -15,9 +15,9 @@ int main() { - // Before clearing the tag we ensure we have a valid one - assert(cheri_tag_get(cheri_ddc_get())); - // Clearing the tag will cause the exception - write_ddc(cheri_tag_clear(cheri_ddc_get())); - return 0; + // Before clearing the tag we ensure we have a valid one + assert(cheri_tag_get(cheri_ddc_get())); + // Clearing the tag will cause the exception + write_ddc(cheri_tag_clear(cheri_ddc_get())); + return 0; } \ No newline at end of file diff --git a/hybrid/ddc_null.c b/hybrid/ddc_null.c index db0a156..4a6c285 100644 --- a/hybrid/ddc_null.c +++ b/hybrid/ddc_null.c @@ -15,9 +15,9 @@ int main() { - // Ensure the DDC contains a valid capability - assert(cheri_tag_get(cheri_ddc_get())); - // Putting a NULL will cause the exception - write_ddc(NULL); - return 0; + // Ensure the DDC contains a valid capability + assert(cheri_tag_get(cheri_ddc_get())); + // Putting a NULL will cause the exception + write_ddc(NULL); + return 0; } \ No newline at end of file diff --git a/hybrid/include/utils.h b/hybrid/include/utils.h index 0387400..04fe358 100644 --- a/hybrid/include/utils.h +++ b/hybrid/include/utils.h @@ -13,13 +13,13 @@ void *__capability read_ddc(); // Write a capability to the DDC inline void write_ddc(void *__capability cap) { - asm("MSR DDC, %[cap]\n\t" : : [cap] "C"(cap) : "memory"); + asm("MSR DDC, %[cap]\n\t" : : [cap] "C"(cap) : "memory"); } // Read a capability from the DDC inline void *__capability read_ddc() { - void *__capability ddc_cap; - asm("MRS %[ddc_cap], DDC\n\t" : [ddc_cap] "=C"(ddc_cap) : :); - return ddc_cap; + void *__capability ddc_cap; + asm("MRS %[ddc_cap], DDC\n\t" : [ddc_cap] "=C"(ddc_cap) : :); + return ddc_cap; } \ No newline at end of file diff --git a/include/common.h b/include/common.h index e11f488..4dfb588 100644 --- a/include/common.h +++ b/include/common.h @@ -9,26 +9,26 @@ void pp_cap(void *__capability ptr) { - uint64_t length = cheri_length_get(ptr); - uint64_t address = cheri_address_get(ptr); - uint64_t base = cheri_base_get(ptr); - uint64_t flags = cheri_flags_get(ptr); - uint64_t perms = cheri_perms_get(ptr); - uint64_t type = cheri_type_get(ptr); - bool tag = cheri_tag_get(ptr); + uint64_t length = cheri_length_get(ptr); + uint64_t address = cheri_address_get(ptr); + uint64_t base = cheri_base_get(ptr); + uint64_t flags = cheri_flags_get(ptr); + uint64_t perms = cheri_perms_get(ptr); + uint64_t type = cheri_type_get(ptr); + bool tag = cheri_tag_get(ptr); - uint64_t offset = cheri_offset_get(ptr); + uint64_t offset = cheri_offset_get(ptr); - printf("Capability: %#lp\n", ptr); - printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, End: %04lx, Flags: %lx, " - "Length: %04lx, Offset: %04lx\n", - tag, perms, type, address, base, base + length, flags, length, offset); + printf("Capability: %#lp\n", ptr); + printf("Tag: %d, Perms: %04lx, Type: %lx, Address: %04lx, Base: %04lx, End: %04lx, Flags: %lx, " + "Length: %04lx, Offset: %04lx\n", + tag, perms, type, address, base, base + length, flags, length, offset); } void error(char *string) { - fputs(string, stderr); - fputc('\n', stderr); + fputs(string, stderr); + fputc('\n', stderr); } // This function returns the current stack pointer. @@ -38,5 +38,5 @@ void error(char *string) // This will be the stack top of the calling function. __attribute__((noinline)) void *cheri_getcsp() { - return __builtin_frame_address(0); + return __builtin_frame_address(0); } diff --git a/include/instructions.h b/include/instructions.h index 9f2ba1c..7d42b23 100644 --- a/include/instructions.h +++ b/include/instructions.h @@ -2,1649 +2,1649 @@ uint32_t add(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00000033; // 33 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00000033; // 33 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t addi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000013; // 13 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00000013; // 13 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t addiw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000001B; // 1B 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x0000001B; // 1B 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t addw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000003B; // 3B 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0000003B; // 3B 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t and (uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00007033; // 33 70 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00007033; // 33 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t andi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00007013; // 13 70 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00007013; // 13 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t auipc(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000017; // 17 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((imm >> 0) & 0b11111111111111111111) << 12); - return i; + uint32_t i = 0x00000017; // 17 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); + return i; } uint32_t auipcc(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000017; // 17 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((imm >> 0) & 0b11111111111111111111) << 12); - return i; + uint32_t i = 0x00000017; // 17 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); + return i; } uint32_t beq(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000063; // 63 00 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00000063; // 63 00 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t bge(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00005063; // 63 50 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00005063; // 63 50 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t bgeu(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00007063; // 63 70 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00007063; // 63 70 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t blt(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004063; // 63 40 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00004063; // 63 40 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t bltu(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00006063; // 63 60 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00006063; // 63 60 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t bne(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001063; // 63 10 00 00 - i |= (((imm >> 10) & 0b1) << 7); - i |= (((imm >> 0) & 0b1111) << 8); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 4) & 0b111111) << 25); - i |= (((imm >> 11) & 0b1) << 31); - return i; + uint32_t i = 0x00001063; // 63 10 00 00 + i |= (((imm >> 10) & 0b1) << 7); + i |= (((imm >> 0) & 0b1111) << 8); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 4) & 0b111111) << 25); + i |= (((imm >> 11) & 0b1) << 31); + return i; } uint32_t candperm(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1A00005B; // 5B 00 00 1A - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1A00005B; // 5B 00 00 1A + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cbuildcap(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3A00005B; // 5B 00 00 3A - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x3A00005B; // 5B 00 00 3A + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ccseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3E00005B; // 5B 00 00 3E - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x3E00005B; // 5B 00 00 3E + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ccall(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xFC0000DB; // DB 00 00 FC - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xFC0000DB; // DB 00 00 FC + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ccleartag(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEB0005B; // 5B 00 B0 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFEB0005B; // 5B 00 B0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t ccopytype(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x3C00005B; // 5B 00 00 3C - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x3C00005B; // 5B 00 00 3C + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cfld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003007; // 07 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003007; // 07 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t cflw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002007; // 07 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00002007; // 07 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t cfsd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003027; // 27 30 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00003027; // 27 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t cfsw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002027; // 27 20 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00002027; // 27 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t cfromptr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2600005B; // 5B 00 00 26 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x2600005B; // 5B 00 00 26 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cgetaddr(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEF0005B; // 5B 00 F0 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFEF0005B; // 5B 00 F0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetbase(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE20005B; // 5B 00 20 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE20005B; // 5B 00 20 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetflags(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE70005B; // 5B 00 70 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE70005B; // 5B 00 70 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetlen(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE30005B; // 5B 00 30 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE30005B; // 5B 00 30 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetoffset(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE60005B; // 5B 00 60 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE60005B; // 5B 00 60 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetperm(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE00005B; // 5B 00 00 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE00005B; // 5B 00 00 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgetsealed(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE50005B; // 5B 00 50 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE50005B; // 5B 00 50 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgettag(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE40005B; // 5B 00 40 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE40005B; // 5B 00 40 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cgettype(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE10005B; // 5B 00 10 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE10005B; // 5B 00 10 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cincoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2200005B; // 5B 00 00 22 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x2200005B; // 5B 00 00 22 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cincoffsetimm(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000105B; // 5B 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x0000105B; // 5B 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t cjalr(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEC0005B; // 5B 00 C0 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFEC0005B; // 5B 00 C0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t clb(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000003; // 03 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00000003; // 03 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clbu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00004003; // 03 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00004003; // 03 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clc_128(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000200F; // 0F 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x0000200F; // 0F 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clc_64(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t cld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clh(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001003; // 03 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00001003; // 03 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clhu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005003; // 03 50 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00005003; // 03 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002003; // 03 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00002003; // 03 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t clwu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006003; // 03 60 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00006003; // 03 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t cmove(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFEA0005B; // 5B 00 A0 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFEA0005B; // 5B 00 A0 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t cram(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE90005B; // 5B 00 90 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE90005B; // 5B 00 90 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t crrl(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFE80005B; // 5B 00 80 FE - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFE80005B; // 5B 00 80 FE + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t csb(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000023; // 23 00 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00000023; // 23 00 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t csc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004023; // 23 40 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00004023; // 23 40 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t csc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t csd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t cseqx(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4200005B; // 5B 00 00 42 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x4200005B; // 5B 00 00 42 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csh(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001023; // 23 10 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00001023; // 23 10 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t csrrc(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003073; // 73 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003073; // 73 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csrrci(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00007073; // 73 70 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00007073; // 73 70 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csrrs(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002073; // 73 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00002073; // 73 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csrrsi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006073; // 73 60 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00006073; // 73 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csrrw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001073; // 73 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00001073; // 73 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csrrwi(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005073; // 73 50 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00005073; // 73 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002023; // 23 20 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00002023; // 23 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t cseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1600005B; // 5B 00 00 16 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1600005B; // 5B 00 00 16 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csealentry(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFF10005B; // 5B 00 10 FF - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFF10005B; // 5B 00 10 FF + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t csetaddr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2000005B; // 5B 00 00 20 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x2000005B; // 5B 00 00 20 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csetbounds(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1000005B; // 5B 00 00 10 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1000005B; // 5B 00 00 10 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csetboundsexact(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1200005B; // 5B 00 00 12 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1200005B; // 5B 00 00 12 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csetboundsimm(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000205B; // 5B 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x0000205B; // 5B 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t csetflags(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1C00005B; // 5B 00 00 1C - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1C00005B; // 5B 00 00 1C + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t csetoffset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1E00005B; // 5B 00 00 1E - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1E00005B; // 5B 00 00 1E + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cspecialrw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0200005B; // 5B 00 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200005B; // 5B 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b11111) << 20); + return i; } uint32_t csub(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2800005B; // 5B 00 00 28 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x2800005B; // 5B 00 00 28 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ctestsubset(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000005B; // 5B 00 00 40 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x4000005B; // 5B 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ctoptr(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x2400005B; // 5B 00 00 24 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x2400005B; // 5B 00 00 24 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t cunseal(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x1800005B; // 5B 00 00 18 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x1800005B; // 5B 00 00 18 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t clear(uint32_t quarter, uint32_t mask) { - uint32_t i = 0xFED0005B; // 5B 00 D0 FE - i |= (((mask >> 0) & 0b11111) << 7); - i |= (((mask >> 5) & 0b111) << 15); - i |= (((quarter >> 0) & 0b11) << 18); - return i; + uint32_t i = 0xFED0005B; // 5B 00 D0 FE + i |= (((mask >> 0) & 0b11111) << 7); + i |= (((mask >> 5) & 0b111) << 15); + i |= (((quarter >> 0) & 0b11) << 18); + return i; } uint32_t asm_div(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02004033; // 33 40 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02004033; // 33 40 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t divu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02005033; // 33 50 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02005033; // 33 50 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t divuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200503B; // 3B 50 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200503B; // 3B 50 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t divw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200403B; // 3B 40 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200403B; // 3B 40 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t dret() { - uint32_t i = 0x7B200073; // 73 00 20 7B + uint32_t i = 0x7B200073; // 73 00 20 7B - return i; + return i; } uint32_t ebreak() { - uint32_t i = 0x00100073; // 73 00 10 00 + uint32_t i = 0x00100073; // 73 00 10 00 - return i; + return i; } uint32_t ecall() { - uint32_t i = 0x00000073; // 73 00 00 00 + uint32_t i = 0x00000073; // 73 00 00 00 - return i; + return i; } uint32_t fence(uint32_t succ, uint32_t pred) { - uint32_t i = 0x0000000F; // 0F 00 00 00 - i |= (((succ >> 0) & 0b1111) << 20); - i |= (((pred >> 0) & 0b1111) << 24); - return i; + uint32_t i = 0x0000000F; // 0F 00 00 00 + i |= (((succ >> 0) & 0b1111) << 20); + i |= (((pred >> 0) & 0b1111) << 24); + return i; } uint32_t fence_i() { - uint32_t i = 0x0000100F; // 0F 10 00 00 + uint32_t i = 0x0000100F; // 0F 10 00 00 - return i; + return i; } uint32_t fence_tso() { - uint32_t i = 0x8330000F; // 0F 00 30 83 + uint32_t i = 0x8330000F; // 0F 00 30 83 - return i; + return i; } uint32_t fpclear(uint32_t quarter, uint32_t mask) { - uint32_t i = 0xFF00005B; // 5B 00 00 FF - i |= (((mask >> 0) & 0b11111) << 7); - i |= (((mask >> 5) & 0b111) << 15); - i |= (((quarter >> 0) & 0b11) << 18); - return i; + uint32_t i = 0xFF00005B; // 5B 00 00 FF + i |= (((mask >> 0) & 0b11111) << 7); + i |= (((mask >> 5) & 0b111) << 15); + i |= (((quarter >> 0) & 0b11) << 18); + return i; } uint32_t jal(uint32_t rd, uint32_t imm) { - uint32_t i = 0x0000006F; // 6F 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((imm >> 11) & 0b11111111) << 12); - i |= (((imm >> 10) & 0b1) << 20); - i |= (((imm >> 0) & 0b1111111111) << 21); - i |= (((imm >> 19) & 0b1) << 31); - return i; + uint32_t i = 0x0000006F; // 6F 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 11) & 0b11111111) << 12); + i |= (((imm >> 10) & 0b1) << 20); + i |= (((imm >> 0) & 0b1111111111) << 21); + i |= (((imm >> 19) & 0b1) << 31); + return i; } uint32_t jalr(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000067; // 67 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00000067; // 67 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lb(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00000003; // 03 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00000003; // 03 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lbu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00004003; // 03 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00004003; // 03 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lbu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAC0005B; // 5B 00 C0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAC0005B; // 5B 00 C0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lbu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA40005B; // 5B 00 40 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA40005B; // 5B 00 40 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lb_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA80005B; // 5B 00 80 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA80005B; // 5B 00 80 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lb_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA00005B; // 5B 00 00 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA00005B; // 5B 00 00 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lc_128(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x0000200F; // 0F 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x0000200F; // 0F 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lc_64(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lc_cap_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBF0005B; // 5B 00 F0 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFBF0005B; // 5B 00 F0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lc_cap_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAB0005B; // 5B 00 B0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAB0005B; // 5B 00 B0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lc_ddc_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB70005B; // 5B 00 70 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB70005B; // 5B 00 70 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lc_ddc_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA30005B; // 5B 00 30 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA30005B; // 5B 00 30 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t ld(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003003; // 03 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003003; // 03 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t ld_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAB0005B; // 5B 00 B0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAB0005B; // 5B 00 B0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t ld_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA30005B; // 5B 00 30 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA30005B; // 5B 00 30 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lh(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00001003; // 03 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00001003; // 03 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lhu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00005003; // 03 50 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00005003; // 03 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lhu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAD0005B; // 5B 00 D0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAD0005B; // 5B 00 D0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lhu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA50005B; // 5B 00 50 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA50005B; // 5B 00 50 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lh_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA90005B; // 5B 00 90 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA90005B; // 5B 00 90 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lh_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA10005B; // 5B 00 10 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA10005B; // 5B 00 10 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_b_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB80005B; // 5B 00 80 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB80005B; // 5B 00 80 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_b_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB00005B; // 5B 00 00 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB00005B; // 5B 00 00 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_c_cap_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBC0005B; // 5B 00 C0 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFBC0005B; // 5B 00 C0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_c_cap_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBB0005B; // 5B 00 B0 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFBB0005B; // 5B 00 B0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_c_ddc_128(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB40005B; // 5B 00 40 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB40005B; // 5B 00 40 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_c_ddc_64(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB30005B; // 5B 00 30 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB30005B; // 5B 00 30 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_d_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBB0005B; // 5B 00 B0 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFBB0005B; // 5B 00 B0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_d_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB30005B; // 5B 00 30 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB30005B; // 5B 00 30 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_h_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB90005B; // 5B 00 90 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB90005B; // 5B 00 90 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_h_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB10005B; // 5B 00 10 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB10005B; // 5B 00 10 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_w_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFBA0005B; // 5B 00 A0 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFBA0005B; // 5B 00 A0 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lr_w_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFB20005B; // 5B 00 20 FB - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFB20005B; // 5B 00 20 FB + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lui(uint32_t rd, uint32_t imm) { - uint32_t i = 0x00000037; // 37 00 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((imm >> 0) & 0b11111111111111111111) << 12); - return i; + uint32_t i = 0x00000037; // 37 00 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((imm >> 0) & 0b11111111111111111111) << 12); + return i; } uint32_t lw(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002003; // 03 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00002003; // 03 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lwu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006003; // 03 60 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00006003; // 03 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t lwu_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAE0005B; // 5B 00 E0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAE0005B; // 5B 00 E0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lwu_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA60005B; // 5B 00 60 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA60005B; // 5B 00 60 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lw_cap(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFAA0005B; // 5B 00 A0 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFAA0005B; // 5B 00 A0 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t lw_ddc(uint32_t rd, uint32_t rs1) { - uint32_t i = 0xFA20005B; // 5B 00 20 FA - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - return i; + uint32_t i = 0xFA20005B; // 5B 00 20 FA + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + return i; } uint32_t mret() { - uint32_t i = 0x30200073; // 73 00 20 30 + uint32_t i = 0x30200073; // 73 00 20 30 - return i; + return i; } uint32_t mul(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02000033; // 33 00 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02000033; // 33 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t mulh(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02001033; // 33 10 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02001033; // 33 10 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t mulhsu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02002033; // 33 20 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02002033; // 33 20 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t mulhu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02003033; // 33 30 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02003033; // 33 30 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t mulw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200003B; // 3B 00 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200003B; // 3B 00 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t or (uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00006033; // 33 60 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00006033; // 33 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t ori(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00006013; // 13 60 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00006013; // 13 60 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t rem(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02006033; // 33 60 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02006033; // 33 60 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t remu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x02007033; // 33 70 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x02007033; // 33 70 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t remuw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200703B; // 3B 70 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200703B; // 3B 70 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t remw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0200603B; // 3B 60 00 02 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0200603B; // 3B 60 00 02 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sb(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00000023; // 23 00 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00000023; // 23 00 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sb_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800045B; // 5B 04 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800045B; // 5B 04 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sb_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800005B; // 5B 00 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800005B; // 5B 00 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_128(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00004023; // 23 40 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00004023; // 23 40 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sc_64(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sc_b_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000C5B; // 5B 0C 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000C5B; // 5B 0C 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_b_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800085B; // 5B 08 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800085B; // 5B 08 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_cap_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800065B; // 5B 06 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800065B; // 5B 06 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_cap_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80005DB; // DB 05 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80005DB; // DB 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_c_cap_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000E5B; // 5B 0E 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000E5B; // 5B 0E 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_c_cap_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000DDB; // DB 0D 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000DDB; // DB 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_c_ddc_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000A5B; // 5B 0A 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000A5B; // 5B 0A 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_c_ddc_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80009DB; // DB 09 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80009DB; // DB 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_ddc_128(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800025B; // 5B 02 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800025B; // 5B 02 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_ddc_64(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80001DB; // DB 01 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80001DB; // DB 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_d_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000DDB; // DB 0D 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000DDB; // DB 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_d_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80009DB; // DB 09 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80009DB; // DB 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_h_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000CDB; // DB 0C 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000CDB; // DB 0C 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_h_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80008DB; // DB 08 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80008DB; // DB 08 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_w_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF8000D5B; // 5B 0D 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF8000D5B; // 5B 0D 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sc_w_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800095B; // 5B 09 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800095B; // 5B 09 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sd(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00003023; // 23 30 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00003023; // 23 30 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sd_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80005DB; // DB 05 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80005DB; // DB 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sd_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80001DB; // DB 01 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80001DB; // DB 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sfence_vma(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x12000073; // 73 00 00 12 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x12000073; // 73 00 00 12 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sh(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00001023; // 23 10 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00001023; // 23 10 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sh_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80004DB; // DB 04 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80004DB; // DB 04 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sh_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF80000DB; // DB 00 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF80000DB; // DB 00 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sll(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00001033; // 33 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00001033; // 33 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sllw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000103B; // 3B 10 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0000103B; // 3B 10 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t slt(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00002033; // 33 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00002033; // 33 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t slti(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00002013; // 13 20 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00002013; // 13 20 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t sltiu(uint32_t rd, uint32_t rs1, uint32_t imm) { - uint32_t i = 0x00003013; // 13 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + uint32_t i = 0x00003013; // 13 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } uint32_t sltu(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00003033; // 33 30 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00003033; // 33 30 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sra(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x40005033; // 33 50 00 40 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x40005033; // 33 50 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sraw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000503B; // 3B 50 00 40 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x4000503B; // 3B 50 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sret() { - uint32_t i = 0x10200073; // 73 00 20 10 + uint32_t i = 0x10200073; // 73 00 20 10 - return i; + return i; } uint32_t srl(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00005033; // 33 50 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x00005033; // 33 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t srlw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x0000503B; // 3B 50 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x0000503B; // 3B 50 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sub(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x40000033; // 33 00 00 40 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x40000033; // 33 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t subw(uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x4000003B; // 3B 00 00 40 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0x4000003B; // 3B 00 00 40 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sw(uint32_t rs1, uint32_t rs2, uint32_t imm) { - uint32_t i = 0x00002023; // 23 20 00 00 - i |= (((imm >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - i |= (((imm >> 5) & 0b1111111) << 25); - return i; + uint32_t i = 0x00002023; // 23 20 00 00 + i |= (((imm >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + i |= (((imm >> 5) & 0b1111111) << 25); + return i; } uint32_t sw_cap(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800055B; // 5B 05 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800055B; // 5B 05 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t sw_ddc(uint32_t rs1, uint32_t rs2) { - uint32_t i = 0xF800015B; // 5B 01 00 F8 - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; + uint32_t i = 0xF800015B; // 5B 01 00 F8 + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; } uint32_t unimp() { - uint32_t i = 0xC0001073; // 73 10 00 C0 + uint32_t i = 0xC0001073; // 73 10 00 C0 - return i; + return i; } uint32_t uret() { - uint32_t i = 0x00200073; // 73 00 20 00 + uint32_t i = 0x00200073; // 73 00 20 00 - return i; + return i; } uint32_t wfi() { - uint32_t i = 0x10500073; // 73 00 50 10 + uint32_t i = 0x10500073; // 73 00 50 10 - return i; + return i; } uint32_t xor - (uint32_t rd, uint32_t rs1, uint32_t rs2) { - uint32_t i = 0x00004033; // 33 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((rs2 >> 0) & 0b11111) << 20); - return i; - } - - uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) -{ - uint32_t i = 0x00004013; // 13 40 00 00 - i |= (((rd >> 0) & 0b11111) << 7); - i |= (((rs1 >> 0) & 0b11111) << 15); - i |= (((imm >> 0) & 0b111111111111) << 20); - return i; + (uint32_t rd, uint32_t rs1, uint32_t rs2) { + uint32_t i = 0x00004033; // 33 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((rs2 >> 0) & 0b11111) << 20); + return i; + } + + uint32_t xori(uint32_t rd, uint32_t rs1, uint32_t imm) +{ + uint32_t i = 0x00004013; // 13 40 00 00 + i |= (((rd >> 0) & 0b11111) << 7); + i |= (((rs1 >> 0) & 0b11111) << 15); + i |= (((imm >> 0) & 0b111111111111) << 20); + return i; } diff --git a/mmap.c b/mmap.c index 98f187c..990a96e 100644 --- a/mmap.c +++ b/mmap.c @@ -12,50 +12,50 @@ uint32_t *get_executable_block() { - uint32_t *result = - mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + uint32_t *result = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - if (result == MAP_FAILED) - { - printf("ERRNO: %d, ERROR: %s \n\n", errno, strerror(errno)); - } - return result; + if (result == MAP_FAILED) + { + printf("ERRNO: %d, ERROR: %s \n\n", errno, strerror(errno)); + } + return result; } uint32_t *generate_purecap(uint32_t *code) { - uint32_t idx = 0; + uint32_t idx = 0; - if (cheri_getlength(code) <= (9 * sizeof(uint32_t))) - { - error("Insufficient size"); - exit(-1); - } + if (cheri_getlength(code) <= (9 * sizeof(uint32_t))) + { + error("Insufficient size"); + exit(-1); + } - code[idx++] = - cincoffsetimm(csp, csp, ((-32) + (2 << 20))); // 0xFE01115B; // cincoffset csp, csp, -32 - code[idx++] = csc_128(csp, cra, 16); // 0x00114823; // csc cra, 16(csp) - code[idx++] = csc_128(csp, cs0, 0); // 0x00814023; // csc cs0, 0(csp) - code[idx++] = cincoffset(cs0, csp, 32); // 0x0201145B; // cincoffset cs0, csp, 32 - code[idx++] = addi(a0, zero, 5); // 0x00500513; // addi a0, zero, 5 - code[idx++] = clc_128(cs0, csp, 0); // 0x0001240F; // clc cs0, 0(csp) - code[idx++] = clc_128(cra, csp, 16); // 0x0101208F; // clc cra, 16(csp) - code[idx++] = cincoffsetimm(csp, csp, 32); // 0x0201115B; // cincoffset csp, csp, 32 - code[idx++] = cjalr(zero, cra); // 0xFEC0805B; // cret + code[idx++] = + cincoffsetimm(csp, csp, ((-32) + (2 << 20))); // 0xFE01115B; // cincoffset csp, csp, -32 + code[idx++] = csc_128(csp, cra, 16); // 0x00114823; // csc cra, 16(csp) + code[idx++] = csc_128(csp, cs0, 0); // 0x00814023; // csc cs0, 0(csp) + code[idx++] = cincoffset(cs0, csp, 32); // 0x0201145B; // cincoffset cs0, csp, 32 + code[idx++] = addi(a0, zero, 5); // 0x00500513; // addi a0, zero, 5 + code[idx++] = clc_128(cs0, csp, 0); // 0x0001240F; // clc cs0, 0(csp) + code[idx++] = clc_128(cra, csp, 16); // 0x0101208F; // clc cra, 16(csp) + code[idx++] = cincoffsetimm(csp, csp, 32); // 0x0201115B; // cincoffset csp, csp, 32 + code[idx++] = cjalr(zero, cra); // 0xFEC0805B; // cret - /* - * Very important. - * The flags of the pointer that is used to call a function are then transferred to the pcc - * register. This is important when executing because: - * https://github.com/CTSRD-CHERI/sail-cheri-riscv/blob/8253bffe30abf2a8ae1c7eba515061b141aff727/src/cheri_addr_checks.sail#L109 - * The pcc register is used to determine the mode of operation. - * Is it in capability mode or hybrid mode. - * When in hybrid mode the capabilities of some registers are ignored. - * This means that the ddc (default data capability) is being used. - * When in capability mode the capability of the corresponding capability register is used. - * For example, if you try to access a0 that would use the capability inside of ca0 - */ - return cheri_setflags(code, 0x0001); + /* + * Very important. + * The flags of the pointer that is used to call a function are then transferred to the pcc + * register. This is important when executing because: + * https://github.com/CTSRD-CHERI/sail-cheri-riscv/blob/8253bffe30abf2a8ae1c7eba515061b141aff727/src/cheri_addr_checks.sail#L109 + * The pcc register is used to determine the mode of operation. + * Is it in capability mode or hybrid mode. + * When in hybrid mode the capabilities of some registers are ignored. + * This means that the ddc (default data capability) is being used. + * When in capability mode the capability of the corresponding capability register is used. + * For example, if you try to access a0 that would use the capability inside of ca0 + */ + return cheri_setflags(code, 0x0001); } /* @@ -67,52 +67,52 @@ uint32_t *generate_purecap(uint32_t *code) */ uint32_t *generate_hybrid(uint32_t *code) { - uint32_t idx = 0; + uint32_t idx = 0; - if (cheri_getlength(code) <= (10 * sizeof(uint32_t))) - { - error("Insufficient size"); - exit(-1); - } + if (cheri_getlength(code) <= (10 * sizeof(uint32_t))) + { + error("Insufficient size"); + exit(-1); + } - code[idx++] = cincoffsetimm(csp, csp, ((-32) + (2 << 20))); - code[idx++] = cspecialrw(cnull, csp, 1); // cspecialw csp, ddc - code[idx++] = csc_128(zero, cra, 16); - code[idx++] = csc_128(zero, cs0, 0); - code[idx++] = cincoffset(cs0, csp, 32); - code[idx++] = addi(a0, zero, 5); - code[idx++] = clc_128(cs0, zero, 0); - code[idx++] = clc_128(cra, zero, 16); - code[idx++] = cincoffsetimm(csp, csp, 32); - code[idx++] = cjalr(zero, cra); - return code; + code[idx++] = cincoffsetimm(csp, csp, ((-32) + (2 << 20))); + code[idx++] = cspecialrw(cnull, csp, 1); // cspecialw csp, ddc + code[idx++] = csc_128(zero, cra, 16); + code[idx++] = csc_128(zero, cs0, 0); + code[idx++] = cincoffset(cs0, csp, 32); + code[idx++] = addi(a0, zero, 5); + code[idx++] = clc_128(cs0, zero, 0); + code[idx++] = clc_128(cra, zero, 16); + code[idx++] = cincoffsetimm(csp, csp, 32); + code[idx++] = cjalr(zero, cra); + return code; } uint32_t *generate_micro(uint32_t *code) { - uint32_t idx = 0; + uint32_t idx = 0; - if (cheri_getlength(code) <= (2 * sizeof(uint32_t))) - { - error("Insufficient size"); - exit(-1); - } - code[idx++] = addi(a0, zero, 5); - code[idx++] = cjalr(zero, cra); - return code; + if (cheri_getlength(code) <= (2 * sizeof(uint32_t))) + { + error("Insufficient size"); + exit(-1); + } + code[idx++] = addi(a0, zero, 5); + code[idx++] = cjalr(zero, cra); + return code; } int main() { - uint32_t *code; - code = get_executable_block(); - code = generate_purecap(code); + uint32_t *code; + code = get_executable_block(); + code = generate_purecap(code); - pp_cap(cheri_getpcc()); - pp_cap(code); + pp_cap(cheri_getpcc()); + pp_cap(code); - int (*code_function)() = (int (*)()) code; - int result = code_function(); - printf("Result: %d\n\n", result); - return result; + int (*code_function)() = (int (*)()) code; + int result = code_function(); + printf("Result: %d\n\n", result); + return result; } diff --git a/seal.c b/seal.c index c23eae1..7b7ce2f 100644 --- a/seal.c +++ b/seal.c @@ -8,25 +8,25 @@ int main() { - void *sealcap; - size_t sealcap_size = sizeof(sealcap); - if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) - { - error("Fatal error. Cannot get `security.cheri.sealcap`."); - exit(1); - } + void *sealcap; + size_t sealcap_size = sizeof(sealcap); + if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) + { + error("Fatal error. Cannot get `security.cheri.sealcap`."); + exit(1); + } - printf("---- sealcap ----\n"); - pp_cap(sealcap); + printf("---- sealcap ----\n"); + pp_cap(sealcap); - void *buffer = malloc(64); - printf("---- buffer (before sealing) ----\n"); - pp_cap(buffer); + void *buffer = malloc(64); + printf("---- buffer (before sealing) ----\n"); + pp_cap(buffer); - void *sealed = cheri_seal(buffer, sealcap); - printf("---- sealed ----\n"); - pp_cap(sealed); + void *sealed = cheri_seal(buffer, sealcap); + printf("---- sealed ----\n"); + pp_cap(sealed); - free(buffer); - return 0; + free(buffer); + return 0; } diff --git a/sentry.c b/sentry.c index 71727dd..8c00687 100644 --- a/sentry.c +++ b/sentry.c @@ -18,9 +18,9 @@ struct data { - int a; - int b; - char c[256]; + int a; + int b; + char c[256]; }; void *setup_sentry(void *, void (*f)(uint32_t *, void *)); @@ -31,14 +31,14 @@ void simple_sentry(int); // the first argument is passed by the trampoline assembly. void oop_sentry(struct data *this, int arg) { - printf("OOP: %p %d \n", this, arg); + printf("OOP: %p %d \n", this, arg); } // This function will also be called through a sentry but no // arguments are added. void simple_sentry(int arg) { - printf("Simple: %d \n", arg); + printf("Simple: %d \n", arg); } // A function to generate the code that will put the @@ -46,122 +46,122 @@ void simple_sentry(int arg) // arguments to the next registers. void gen_oop(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *) code; - - // The additional data needs to be stored somewhere in memory - // where it can not be accessed by the user of the function. - // This is why it is stored in the same place where the code will be. - // As the code is not modifiable and not readable this data is also - // protected. - code_data[255] = (intptr_t) function; - code_data[254] = (intptr_t) malloc(sizeof(struct data)); - - uint32_t idx = 0; - code[idx++] = auipcc(cs2, 1); - code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); - // Move the arguments to the next register. - // This will break if we have more then 6 arguments but that should be fine. - code[idx++] = addi(a1, a0, 0); - code[idx++] = addi(a2, a1, 0); - code[idx++] = addi(a3, a2, 0); - code[idx++] = addi(a4, a3, 0); - code[idx++] = addi(a5, a4, 0); - code[idx++] = addi(a6, a5, 0); - code[idx++] = addi(a7, a6, 0); - code[idx++] = clc_128(ca0, cs2, ((-32) + (2 << 20))); - code[idx++] = cjalr(cnull, cs3); + intptr_t *code_data = (intptr_t *) code; + + // The additional data needs to be stored somewhere in memory + // where it can not be accessed by the user of the function. + // This is why it is stored in the same place where the code will be. + // As the code is not modifiable and not readable this data is also + // protected. + code_data[255] = (intptr_t) function; + code_data[254] = (intptr_t) malloc(sizeof(struct data)); + + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + // Move the arguments to the next register. + // This will break if we have more then 6 arguments but that should be fine. + code[idx++] = addi(a1, a0, 0); + code[idx++] = addi(a2, a1, 0); + code[idx++] = addi(a3, a2, 0); + code[idx++] = addi(a4, a3, 0); + code[idx++] = addi(a5, a4, 0); + code[idx++] = addi(a6, a5, 0); + code[idx++] = addi(a7, a6, 0); + code[idx++] = clc_128(ca0, cs2, ((-32) + (2 << 20))); + code[idx++] = cjalr(cnull, cs3); } void gen_simple(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *) code; - code_data[255] = (intptr_t) function; + intptr_t *code_data = (intptr_t *) code; + code_data[255] = (intptr_t) function; - uint32_t idx = 0; - code[idx++] = auipcc(cs2, 1); - code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); - code[idx++] = cjalr(cnull, cs3); + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + code[idx++] = cjalr(cnull, cs3); } void gen_override(uint32_t *code, void *function) { - intptr_t *code_data = (intptr_t *) code; - code_data[255] = (intptr_t) function; - - // Here we again generate code for calling the passed function, - // but the code modifies the first argument to be different then - // the one passed. I am not sure how this is useful. - uint32_t idx = 0; - code[idx++] = auipcc(cs2, 1); - code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); - code[idx++] = addi(a0, cnull, 42); - code[idx++] = cjalr(cnull, cs3); + intptr_t *code_data = (intptr_t *) code; + code_data[255] = (intptr_t) function; + + // Here we again generate code for calling the passed function, + // but the code modifies the first argument to be different then + // the one passed. I am not sure how this is useful. + uint32_t idx = 0; + code[idx++] = auipcc(cs2, 1); + code[idx++] = clc_128(cs3, cs2, ((-16) + (2 << 20))); + code[idx++] = addi(a0, cnull, 42); + code[idx++] = cjalr(cnull, cs3); } // Here we allocate the memory for the code that will be generated. void *setup_sentry(void *function, void (*generate_code)(uint32_t *, void *)) { - // As the function is returned as a sentry the pointer to it cannot be read or written to. - // This means that there is no need to remove write protection as only the function itself - // can change its own code. - uint32_t *code = - mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + // As the function is returned as a sentry the pointer to it cannot be read or written to. + // This means that there is no need to remove write protection as only the function itself + // can change its own code. + uint32_t *code = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - generate_code(code, function); + generate_code(code, function); - // We set the flag to run the code in CHERI mode and seal it so it can only be called. - return cheri_sealentry(cheri_setflags(code, 1)); + // We set the flag to run the code in CHERI mode and seal it so it can only be called. + return cheri_sealentry(cheri_setflags(code, 1)); } void *setup_normal(void *function, void (*generate_code)(uint32_t *, void *)) { - uint32_t *code = - mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + uint32_t *code = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - generate_code(code, function); + generate_code(code, function); - // Here the resulting capability isn't sealed to show that unsealed capabilities can be - // called from the same code. - return cheri_setflags(code, 1); + // Here the resulting capability isn't sealed to show that unsealed capabilities can be + // called from the same code. + return cheri_setflags(code, 1); } int main() { - puts("0: Simple sentry"); - puts("1: OOP sentry"); - puts("2: Override sentry"); - puts("3: Normal function"); - puts("4: Fail"); - printf("Mode: "); - uint32_t mode = 0; - if (scanf("%u", &mode) == 0) - { - error("Invalid input"); - } - - void (*fpointer)(int); - switch (mode) - { - case 0: - fpointer = setup_sentry(simple_sentry, gen_simple); - break; - case 1: - fpointer = setup_sentry(oop_sentry, gen_oop); - break; - case 2: - fpointer = setup_sentry(simple_sentry, gen_override); - break; - case 3: - fpointer = setup_normal(simple_sentry, gen_simple); - break; - case 4: // This case should fail - fpointer = setup_sentry(simple_sentry, gen_simple); - fpointer = cheri_incoffset(fpointer, 16); - break; - default: - err(1, "Invalid option selected\n"); - } - - fpointer(99); - printf("END\n"); + puts("0: Simple sentry"); + puts("1: OOP sentry"); + puts("2: Override sentry"); + puts("3: Normal function"); + puts("4: Fail"); + printf("Mode: "); + uint32_t mode = 0; + if (scanf("%u", &mode) == 0) + { + error("Invalid input"); + } + + void (*fpointer)(int); + switch (mode) + { + case 0: + fpointer = setup_sentry(simple_sentry, gen_simple); + break; + case 1: + fpointer = setup_sentry(oop_sentry, gen_oop); + break; + case 2: + fpointer = setup_sentry(simple_sentry, gen_override); + break; + case 3: + fpointer = setup_normal(simple_sentry, gen_simple); + break; + case 4: // This case should fail + fpointer = setup_sentry(simple_sentry, gen_simple); + fpointer = cheri_incoffset(fpointer, 16); + break; + default: + err(1, "Invalid option selected\n"); + } + + fpointer(99); + printf("END\n"); } diff --git a/set_bounds.c b/set_bounds.c index e326bfc..390c7dd 100644 --- a/set_bounds.c +++ b/set_bounds.c @@ -13,28 +13,28 @@ int main(int argc, char *argv[]) { - int32_t array[16] = {0}; - uint32_t bounds = 64; + int32_t array[16] = {0}; + uint32_t bounds = 64; - if (argc < 2) - { - puts("Bounds [Choose a value greater than 64]:"); - if (0 == scanf("%u", &bounds)) - { - error("Extraneous input"); - } - } - // Command line argument to simplify testing - else if (atoi(argv[1]) > 64) - { - bounds = atoi(argv[1]); - } - else - { - printf("Please choose a value greater than 64."); - // This will cause the test to fail for a value lower than 64 - exit(0); - } - // Trying to extend the bounds generates an exception. - int32_t *custom_bounds_array = cheri_setbounds(array, bounds); + if (argc < 2) + { + puts("Bounds [Choose a value greater than 64]:"); + if (0 == scanf("%u", &bounds)) + { + error("Extraneous input"); + } + } + // Command line argument to simplify testing + else if (atoi(argv[1]) > 64) + { + bounds = atoi(argv[1]); + } + else + { + printf("Please choose a value greater than 64."); + // This will cause the test to fail for a value lower than 64 + exit(0); + } + // Trying to extend the bounds generates an exception. + int32_t *custom_bounds_array = cheri_setbounds(array, bounds); } diff --git a/setjmp.c b/setjmp.c index 2abc3f7..4902a54 100644 --- a/setjmp.c +++ b/setjmp.c @@ -6,28 +6,28 @@ int main() { - jmp_buf buffer; - int res = setjmp(buffer); + jmp_buf buffer; + int res = setjmp(buffer); - uint32_t length = cheri_getlength(buffer); + uint32_t length = cheri_getlength(buffer); - // buffer[0] == _JB_MAGIC_SETJMP == 0xbe87fd8a2910af01 - // buffer[1] == $csp - // buffer[2] == $cfp - // buffer[3..13] == $cs1..11 - // buffer[14..31] = ??? - for (uint32_t idx; idx < (length / 16); idx++) - { - if (cheri_gettag(((void **) buffer)[idx])) - { - void *csp = cheri_getcsp(); - uint64_t address = cheri_getaddress(((void **) buffer)[idx]); + // buffer[0] == _JB_MAGIC_SETJMP == 0xbe87fd8a2910af01 + // buffer[1] == $csp + // buffer[2] == $cfp + // buffer[3..13] == $cs1..11 + // buffer[14..31] = ??? + for (uint32_t idx; idx < (length / 16); idx++) + { + if (cheri_gettag(((void **) buffer)[idx])) + { + void *csp = cheri_getcsp(); + uint64_t address = cheri_getaddress(((void **) buffer)[idx]); - if (cheri_is_address_inbounds(csp, address)) - { - printf("[STACK POINTER] "); - } - pp_cap(((void **) buffer)[idx]); - } - } + if (cheri_is_address_inbounds(csp, address)) + { + printf("[STACK POINTER] "); + } + pp_cap(((void **) buffer)[idx]); + } + } } diff --git a/shared_objects/compartment_per_object.c b/shared_objects/compartment_per_object.c index 578d965..026c8bd 100644 --- a/shared_objects/compartment_per_object.c +++ b/shared_objects/compartment_per_object.c @@ -18,48 +18,48 @@ void crash() { - printf("CRASH!\n"); + printf("CRASH!\n"); } void honk(struct Car_priv *priv, struct Car *car) { - printf("%p %p\n", priv, car); - printf("HONK!\n"); - if (car->speed > priv->maxSpeed) - { - priv->crash(); - } + printf("%p %p\n", priv, car); + printf("HONK!\n"); + if (car->speed > priv->maxSpeed) + { + priv->crash(); + } } struct Car *new_car() { - uint32_t *mem = - mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); + uint32_t *mem = + mmap(NULL, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0); - void **ptrs = mem; + void **ptrs = mem; - const uint32_t vtable_start_index = 2; - ptrs[vtable_start_index + 0] = honk; + const uint32_t vtable_start_index = 2; + ptrs[vtable_start_index + 0] = honk; - const ptrdiff_t vtable_offset = ((char *) (ptrs + vtable_start_index) - (char *) ptrs); - const size_t vtable_size = 1 * 16; + const ptrdiff_t vtable_offset = ((char *) (ptrs + vtable_start_index) - (char *) ptrs); + const size_t vtable_size = 1 * 16; - uint32_t idx = 0; - mem[idx++] = auipcc(cs2, 0); - mem[idx++] = clc_128(cs3, cs2, vtable_offset); - mem[idx++] = cincoffsetimm(ca0, cs2, vtable_offset + vtable_size); - mem[idx++] = cincoffsetimm(ca1, cs2, vtable_offset + vtable_size + sizeof(struct Car_priv)); - mem[idx++] = cjalr(cnull, cs3); + uint32_t idx = 0; + mem[idx++] = auipcc(cs2, 0); + mem[idx++] = clc_128(cs3, cs2, vtable_offset); + mem[idx++] = cincoffsetimm(ca0, cs2, vtable_offset + vtable_size); + mem[idx++] = cincoffsetimm(ca1, cs2, vtable_offset + vtable_size + sizeof(struct Car_priv)); + mem[idx++] = cjalr(cnull, cs3); - uint32_t functions_size = vtable_offset + vtable_size; - struct Car *public_car = - (struct Car *) (((char *) mem) + sizeof(struct Car_priv) + functions_size); - struct Car_priv *private_car = (struct Car_priv *) (((char *) mem) + functions_size); + uint32_t functions_size = vtable_offset + vtable_size; + struct Car *public_car = + (struct Car *) (((char *) mem) + sizeof(struct Car_priv) + functions_size); + struct Car_priv *private_car = (struct Car_priv *) (((char *) mem) + functions_size); - private_car->maxSpeed = 10; - private_car->crash = &crash; + private_car->maxSpeed = 10; + private_car->crash = &crash; - public_car->honk = cheri_sealentry(cheri_setflags(mem, 1)); + public_car->honk = cheri_sealentry(cheri_setflags(mem, 1)); - return public_car; + return public_car; } diff --git a/shared_objects/include/compartment_per_object.h b/shared_objects/include/compartment_per_object.h index 07c4c8f..ea684da 100644 --- a/shared_objects/include/compartment_per_object.h +++ b/shared_objects/include/compartment_per_object.h @@ -5,15 +5,15 @@ struct Car_priv { - int maxSpeed; - void (*crash)(); + int maxSpeed; + void (*crash)(); }; struct Car { - int speed; - void (*honk)(); - char name[]; + int speed; + void (*honk)(); + char name[]; }; void init(); diff --git a/shared_objects/include/find_sentries.h b/shared_objects/include/find_sentries.h index 5d90a10..bf43374 100644 --- a/shared_objects/include/find_sentries.h +++ b/shared_objects/include/find_sentries.h @@ -9,40 +9,40 @@ bool is_pointer(void *ptr) { - if (cheri_tag_get(ptr)) - { - return true; - } - return false; + if (cheri_tag_get(ptr)) + { + return true; + } + return false; } bool scan_range(void *ptr, void *exact) { - bool found = false; - for (void *iter = cheri_offset_set(ptr, 0); cheri_offset_get(iter) < cheri_length_get(iter); - iter = cheri_offset_set(iter, cheri_offset_get(iter) + 16)) - { - void *current = *(void **) iter; - if (is_pointer(current)) - { - if (cheri_is_sealed(current)) - { - if (cheri_address_get(current) == cheri_address_get(exact)) - { - found = true; - printf("[Exact match] "); - } - else if (cheri_length_get(current) == cheri_length_get(exact) && - cheri_base_get(current) == cheri_base_get(exact)) - { - printf("[Range match] "); - } + bool found = false; + for (void *iter = cheri_offset_set(ptr, 0); cheri_offset_get(iter) < cheri_length_get(iter); + iter = cheri_offset_set(iter, cheri_offset_get(iter) + 16)) + { + void *current = *(void **) iter; + if (is_pointer(current)) + { + if (cheri_is_sealed(current)) + { + if (cheri_address_get(current) == cheri_address_get(exact)) + { + found = true; + printf("[Exact match] "); + } + else if (cheri_length_get(current) == cheri_length_get(exact) && + cheri_base_get(current) == cheri_base_get(exact)) + { + printf("[Range match] "); + } - pp_cap(current); - } - } - } - return found; + pp_cap(current); + } + } + } + return found; } /** @@ -52,7 +52,7 @@ bool scan_range(void *ptr, void *exact) */ bool base_and_bounds_eq(void *__capability ptr1, void *__capability ptr2) { - void *__capability pcc = cheri_pcc_get(); - return cheri_base_get(ptr1) == cheri_base_get(ptr2) && - cheri_length_get(ptr1) == cheri_length_get(ptr2); + void *__capability pcc = cheri_pcc_get(); + return cheri_base_get(ptr1) == cheri_base_get(ptr2) && + cheri_length_get(ptr1) == cheri_length_get(ptr2); } diff --git a/shared_objects/main.c b/shared_objects/main.c index 75c72e0..9c7b0a8 100644 --- a/shared_objects/main.c +++ b/shared_objects/main.c @@ -16,60 +16,60 @@ void static_variables() { - increment(); - increment(); - increment(); + increment(); + increment(); + increment(); - printf("Count: %d\n", get_count()); + printf("Count: %d\n", get_count()); - increment(); - increment(); - increment(); + increment(); + increment(); + increment(); - printf("Count: %d\n", get_count()); + printf("Count: %d\n", get_count()); } void compartments_per_object() { - struct Car *car = new_car(); - car->speed = 1; - car->honk(); + struct Car *car = new_car(); + car->speed = 1; + car->honk(); - car->speed = 999; - car->honk(); + car->speed = 999; + car->honk(); } void unexported_functions() { - printf("Finding do_work using dlsym (main): "); - pp_cap(dlsym(NULL, "do_work")); - void *function_to_search_for = test(); - printf("test: %p\n", function_to_search_for); + printf("Finding do_work using dlsym (main): "); + pp_cap(dlsym(NULL, "do_work")); + void *function_to_search_for = test(); + printf("test: %p\n", function_to_search_for); - printf(" ------- scanning pcc for sealed pointers ------- \n"); - bool found = scan_range(cheri_pcc_get(), function_to_search_for); - assert(found == false); + printf(" ------- scanning pcc for sealed pointers ------- \n"); + bool found = scan_range(cheri_pcc_get(), function_to_search_for); + assert(found == false); } int main() { - printf("Choose an exapmle: \n1. Static Variables\n2. OOP Compartments\n3. Unexported " - "Functions\n> "); - uint32_t example = 0; - while (scanf("%u", &example) == 1) - { - if (example == 1) - { - static_variables(); - } - else if (example == 2) - { - compartments_per_object(); - } - else if (example == 3) - { - unexported_functions(); - } - printf("> "); - } + printf("Choose an exapmle: \n1. Static Variables\n2. OOP Compartments\n3. Unexported " + "Functions\n> "); + uint32_t example = 0; + while (scanf("%u", &example) == 1) + { + if (example == 1) + { + static_variables(); + } + else if (example == 2) + { + compartments_per_object(); + } + else if (example == 3) + { + unexported_functions(); + } + printf("> "); + } } diff --git a/shared_objects/pcc_bounds_check_main.c b/shared_objects/pcc_bounds_check_main.c index d0838ff..2c5bce0 100644 --- a/shared_objects/pcc_bounds_check_main.c +++ b/shared_objects/pcc_bounds_check_main.c @@ -8,7 +8,7 @@ int main() { - increment(); - check_static_and_non_static(); - return 0; + increment(); + check_static_and_non_static(); + return 0; } \ No newline at end of file diff --git a/shared_objects/static_function.c b/shared_objects/static_function.c index 2c7b03c..8057518 100644 --- a/shared_objects/static_function.c +++ b/shared_objects/static_function.c @@ -5,35 +5,35 @@ static int static_function() { - assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); - return 0; + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + return 0; } static double another_static_function() { - assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); - return 1.0; + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + return 1.0; } void non_static_fun() { - assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); - assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); - printf("Called static function with result: %d\n", static_function()); + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + printf("Called static function with result: %d\n", static_function()); } void non_static_fun2() { - assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); - assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); - printf("Called another function with result: %f\n", another_static_function()); + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + printf("Called another function with result: %f\n", another_static_function()); } void check_static_and_non_static() { - assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); - assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); - assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); - assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); - assert(base_and_bounds_eq(cheri_pcc_get(), &check_static_and_non_static)); + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun)); + assert(base_and_bounds_eq(cheri_pcc_get(), &static_function)); + assert(base_and_bounds_eq(cheri_pcc_get(), &non_static_fun2)); + assert(base_and_bounds_eq(cheri_pcc_get(), &another_static_function)); + assert(base_and_bounds_eq(cheri_pcc_get(), &check_static_and_non_static)); } diff --git a/shared_objects/static_variable.c b/shared_objects/static_variable.c index 73b8e38..b16ee57 100644 --- a/shared_objects/static_variable.c +++ b/shared_objects/static_variable.c @@ -13,20 +13,20 @@ static int32_t count = -5; void increment() { - count += 1; - assert(base_and_bounds_eq(cheri_pcc_get(), &increment)); + count += 1; + assert(base_and_bounds_eq(cheri_pcc_get(), &increment)); } int get_count() { - assert(base_and_bounds_eq(cheri_pcc_get(), &get_count)); + assert(base_and_bounds_eq(cheri_pcc_get(), &get_count)); - if (count > 0) - { - return count; - } - else - { - return 0; - } + if (count > 0) + { + return count; + } + else + { + return 0; + } } diff --git a/shared_objects/unexported_function.c b/shared_objects/unexported_function.c index 16be0cb..4c28529 100644 --- a/shared_objects/unexported_function.c +++ b/shared_objects/unexported_function.c @@ -15,22 +15,22 @@ __attribute__((visibility("hidden"))) void do_work() { - printf("Doing work\n"); + printf("Doing work\n"); } void *test() { - printf("Finding do_work using dlsym (lib4):\nptr: "); - pp_cap(dlsym(NULL, "do_work")); - printf("Obtaining a direct pointer to it: \nptr: "); - pp_cap(&do_work); - printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); - printf(" ------- scanning pcc for sealed pointers --------\n"); - bool found = scan_range(cheri_getpcc(), &do_work); - assert(found == true); - do_work(); + printf("Finding do_work using dlsym (lib4):\nptr: "); + pp_cap(dlsym(NULL, "do_work")); + printf("Obtaining a direct pointer to it: \nptr: "); + pp_cap(&do_work); + printf("test ptr just to keep it in the pcc:\n ptr: %p\n", &test); + printf(" ------- scanning pcc for sealed pointers --------\n"); + bool found = scan_range(cheri_getpcc(), &do_work); + assert(found == true); + do_work(); - // We return a non-tagged capability to show - // and automatically test if there is outside access. - return cheri_cleartag(&do_work); + // We return a non-tagged capability to show + // and automatically test if there is outside access. + return cheri_cleartag(&do_work); } diff --git a/stackscan.c b/stackscan.c index fd0f486..8c42e32 100644 --- a/stackscan.c +++ b/stackscan.c @@ -10,137 +10,137 @@ void *stack_top; void inspect_stack(void *stack) { - printf("offset: %lu\n", cheri_getlength(stack) - cheri_getoffset(stack)); + printf("offset: %lu\n", cheri_getlength(stack) - cheri_getoffset(stack)); } bool is_stack_pointer(void *ptr) { - if (cheri_gettag(ptr)) - { - uint64_t address = cheri_getaddress(ptr); - - if (cheri_is_address_inbounds(stack_top, address)) - { - return true; - } - } - return false; + if (cheri_gettag(ptr)) + { + uint64_t address = cheri_getaddress(ptr); + + if (cheri_is_address_inbounds(stack_top, address)) + { + return true; + } + } + return false; } bool is_pointer(void *ptr) { - if (cheri_gettag(ptr)) - { - return true; - } - return false; + if (cheri_gettag(ptr)) + { + return true; + } + return false; } bool is_exec(void *ptr) { - if (((cheri_getperm(ptr) & 0b10) >> 1) == 1) - { - return true; - } - return false; + if (((cheri_getperm(ptr) & 0b10) >> 1) == 1) + { + return true; + } + return false; } void scan_range(void *start, void *end) { - void **location = (void **) start; - puts("Scanning range: "); - pp_cap(start); - pp_cap(end); - puts("\n"); - - while (location > end) - { - if (is_pointer(*location)) - { - if (is_stack_pointer(*location)) - { - printf("[Stack Pointer] "); - } - if (is_exec(*location)) - { - printf("[Executable] "); - } - pp_cap(*location); - } - location -= 1; - } + void **location = (void **) start; + puts("Scanning range: "); + pp_cap(start); + pp_cap(end); + puts("\n"); + + while (location > end) + { + if (is_pointer(*location)) + { + if (is_stack_pointer(*location)) + { + printf("[Stack Pointer] "); + } + if (is_exec(*location)) + { + printf("[Executable] "); + } + pp_cap(*location); + } + location -= 1; + } } void scan_frames() { - // We dont's scan the part of the stack taken by the current function - // I promise the collect function doesn't allocate - void *stack_bot = __builtin_frame_address(0); - void *previous_frame = *(void **) stack_bot; - - inspect_stack(stack_bot); - inspect_stack(previous_frame); - while (previous_frame < stack_top) - { - void **next_frame = (void **) previous_frame; - if (!is_stack_pointer(*next_frame)) - { - printf("Couldn't find a stack pointer at: %p, looking up the stack", next_frame); - } - while (!is_stack_pointer(*next_frame)) - { - printf("doesn't contain a stack pointer: "); - inspect_stack(next_frame); - next_frame -= 1; - } - - scan_range(*next_frame, previous_frame); - previous_frame = *next_frame; - printf(" ======= "); - inspect_stack(previous_frame); - } + // We dont's scan the part of the stack taken by the current function + // I promise the collect function doesn't allocate + void *stack_bot = __builtin_frame_address(0); + void *previous_frame = *(void **) stack_bot; + + inspect_stack(stack_bot); + inspect_stack(previous_frame); + while (previous_frame < stack_top) + { + void **next_frame = (void **) previous_frame; + if (!is_stack_pointer(*next_frame)) + { + printf("Couldn't find a stack pointer at: %p, looking up the stack", next_frame); + } + while (!is_stack_pointer(*next_frame)) + { + printf("doesn't contain a stack pointer: "); + inspect_stack(next_frame); + next_frame -= 1; + } + + scan_range(*next_frame, previous_frame); + previous_frame = *next_frame; + printf(" ======= "); + inspect_stack(previous_frame); + } } void scan_all() { - void *stack_bot = __builtin_frame_address(0); - scan_range(stack_top, stack_bot); + void *stack_bot = __builtin_frame_address(0); + scan_range(stack_top, stack_bot); } int test_2() { - scan_all(); - return 99; + scan_all(); + return 99; } int test_3() { - return test_2(); + return test_2(); } uint32_t *test() { - uint32_t *values = (uint32_t *) malloc((size_t) test_3()); - values[0] = 42; - values[1] = 42; - return values; + uint32_t *values = (uint32_t *) malloc((size_t) test_3()); + values[0] = 42; + values[1] = 42; + return values; } int main() { - stack_top = __builtin_frame_address(0); + stack_top = __builtin_frame_address(0); - uint64_t base = cheri_getbase(csp); - uint64_t length = cheri_getlength(csp); + uint64_t base = cheri_getbase(csp); + uint64_t length = cheri_getlength(csp); - uint32_t *values = test(); + uint32_t *values = test(); - for (int idx = 0; idx <= 1; idx++) - { - printf("Value: %d\n", values[idx]); - } + for (int idx = 0; idx <= 1; idx++) + { + printf("Value: %d\n", values[idx]); + } - scan_all(); - return 0; + scan_all(); + return 0; } diff --git a/syscall-restrict/syscall-restrict.c b/syscall-restrict/syscall-restrict.c index 22e5e6c..24b6767 100644 --- a/syscall-restrict/syscall-restrict.c +++ b/syscall-restrict/syscall-restrict.c @@ -17,18 +17,18 @@ typedef int (*stat_fn)(const char *restrict path, struct stat *restrict statbuf) void try_stat(stat_fn fn, char const *file) { - struct stat statbuf; - printf(" stat(\"%s\", &statbuf)\n", file); - if (fn(file, &statbuf) == 0) - { - // Print some easily-verifiable information to demonstrate that - // the syscall actually worked. - printf(" - Success, size is %zu bytes.\n", statbuf.st_size); - } - else - { - printf(" - Failed, errno = %d: %s\n", errno, strerror(errno)); - } + struct stat statbuf; + printf(" stat(\"%s\", &statbuf)\n", file); + if (fn(file, &statbuf) == 0) + { + // Print some easily-verifiable information to demonstrate that + // the syscall actually worked. + printf(" - Success, size is %zu bytes.\n", statbuf.st_size); + } + else + { + printf(" - Failed, errno = %d: %s\n", errno, strerror(errno)); + } } int stat_without_perm_syscall(const char *restrict path, struct stat *restrict statbuf); @@ -75,13 +75,13 @@ asm( ".type stat_without_perm_syscall, @function\n" int main(int argc, char *argv[]) { - // Arbitrarily pick the executable itself as the subject. - char const *file = argv[0]; + // Arbitrarily pick the executable itself as the subject. + char const *file = argv[0]; - printf("\nInitially, we can call syscalls as usual:\n"); - try_stat(stat, file); - printf("\nIf we remove CHERI_PERM_SYSCALL, CheriBSD rejects them:\n"); - try_stat(stat_without_perm_syscall, file); - printf("\nThe error is conventional (not an exception), so we can recover:\n"); - try_stat(stat, file); + printf("\nInitially, we can call syscalls as usual:\n"); + try_stat(stat, file); + printf("\nIf we remove CHERI_PERM_SYSCALL, CheriBSD rejects them:\n"); + try_stat(stat_without_perm_syscall, file); + printf("\nThe error is conventional (not an exception), so we can recover:\n"); + try_stat(stat, file); } diff --git a/timsort/include/timsort_lib.h b/timsort/include/timsort_lib.h index 2c9c990..bcd3343 100644 --- a/timsort/include/timsort_lib.h +++ b/timsort/include/timsort_lib.h @@ -19,37 +19,37 @@ const int MAX_ARRAY_SZ = 2048; int *random_chunk(size_t arr_length) { - srand(time(NULL)); + srand(time(NULL)); - int *arr = malloc(arr_length * sizeof(int)); - for (size_t ix = 0; ix < arr_length; ix++) - { - arr[ix] = rand(); - } + int *arr = malloc(arr_length * sizeof(int)); + for (size_t ix = 0; ix < arr_length; ix++) + { + arr[ix] = rand(); + } - return arr; + return arr; } int cmpfunc(const void *a, const void *b) { - return (*(int *) a - *(int *) b); + return (*(int *) a - *(int *) b); } bool arrEq(int arr_a[], int arr_b[], size_t lowerBound, size_t upperBound) { - if (lowerBound == upperBound || 0 == upperBound) - { - return true; - } + if (lowerBound == upperBound || 0 == upperBound) + { + return true; + } - for (size_t ix = lowerBound; ix <= upperBound; ix++) - { - if (arr_a[ix] != arr_b[ix]) - { - return false; - } - } - return true; + for (size_t ix = lowerBound; ix <= upperBound; ix++) + { + if (arr_a[ix] != arr_b[ix]) + { + return false; + } + } + return true; } /** @@ -60,14 +60,14 @@ bool arrEq(int arr_a[], int arr_b[], size_t lowerBound, size_t upperBound) */ size_t min(size_t a, size_t b) { - if (a <= b) - { - return a; - } - else - { - return b; - } + if (a <= b) + { + return a; + } + else + { + return b; + } } /** @@ -77,53 +77,53 @@ size_t min(size_t a, size_t b) */ bool isSorted(int arr[], size_t length) { - if (length <= 1) - { - return true; - } + if (length <= 1) + { + return true; + } - for (size_t ix = 1; ix < length; ix++) - { - if (arr[ix - 1] > arr[ix]) - { - return false; - } - } + for (size_t ix = 1; ix < length; ix++) + { + if (arr[ix - 1] > arr[ix]) + { + return false; + } + } - return true; + return true; } struct bp_array_s { - void *pointer; - size_t base; - size_t length; + void *pointer; + size_t base; + size_t length; }; struct bp_array_s packBP_mangled(void *pointer, size_t baseIndex, size_t sizeInBytes) { - struct bp_array_s ret; + struct bp_array_s ret; - ret.pointer = pointer; - ret.base = baseIndex; - ret.length = sizeInBytes; + ret.pointer = pointer; + ret.base = baseIndex; + ret.length = sizeInBytes; - return ret; + return ret; } void *get_pointer_mangled(struct bp_array_s bp) { - return bp.pointer; + return bp.pointer; } size_t get_base_mangled(struct bp_array_s bp) { - return bp.base; + return bp.base; } size_t get_length_mangled(struct bp_array_s bp) { - return bp.length; + return bp.length; } #ifndef __CHERI_PURE_CAPABILITY__ @@ -140,69 +140,69 @@ typedef void *bp_array; bp_array packBP(int *pointer, const size_t baseIndex, const size_t sizeInBytes) { - if (0 == baseIndex) - { + if (0 == baseIndex) + { - struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); - if (NULL == largeDescriptor) - { - exit(EXIT_FAILURE); - } - *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); + struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); + if (NULL == largeDescriptor) + { + exit(EXIT_FAILURE); + } + *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); - return (bp_array) largeDescriptor; - } + return (bp_array) largeDescriptor; + } - int *descriptor = cheri_setoffset(pointer, 0); - descriptor = cheri_setbounds(descriptor, sizeInBytes); - descriptor = cheri_setoffset(descriptor, baseIndex); + int *descriptor = cheri_setoffset(pointer, 0); + descriptor = cheri_setbounds(descriptor, sizeInBytes); + descriptor = cheri_setoffset(descriptor, baseIndex); - if (baseIndex != cheri_getoffset(descriptor) || sizeInBytes != cheri_getlen(descriptor)) - { - struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); - if (NULL == largeDescriptor) - { - exit(EXIT_FAILURE); - } - *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); + if (baseIndex != cheri_getoffset(descriptor) || sizeInBytes != cheri_getlen(descriptor)) + { + struct bp_array_s *largeDescriptor = malloc(sizeof(struct bp_array_s)); + if (NULL == largeDescriptor) + { + exit(EXIT_FAILURE); + } + *largeDescriptor = packBP_mangled(pointer, baseIndex, sizeInBytes); - return (bp_array) largeDescriptor; - } + return (bp_array) largeDescriptor; + } - return (bp_array) descriptor; + return (bp_array) descriptor; } bool isMangled(bp_array bp) { - return 0 == cheri_getoffset(bp); + return 0 == cheri_getoffset(bp); } void *get_pointer(bp_array bp) { - if (isMangled(bp)) - { - return cheri_setoffset(get_pointer_mangled(*(struct bp_array_s *) bp), 0); - } - void *ret = bp; - return cheri_setoffset(ret, 0); + if (isMangled(bp)) + { + return cheri_setoffset(get_pointer_mangled(*(struct bp_array_s *) bp), 0); + } + void *ret = bp; + return cheri_setoffset(ret, 0); } size_t get_base(bp_array bp) { - if (isMangled(bp)) - { - return get_base_mangled(*(struct bp_array_s *) bp); - } - return cheri_getoffset(bp); + if (isMangled(bp)) + { + return get_base_mangled(*(struct bp_array_s *) bp); + } + return cheri_getoffset(bp); } size_t get_length(bp_array bp) { - if (isMangled(bp)) - { - return get_length_mangled(*(struct bp_array_s *) bp); - } - return cheri_getlength(bp); + if (isMangled(bp)) + { + return get_length_mangled(*(struct bp_array_s *) bp); + } + return cheri_getlength(bp); } #endif @@ -211,32 +211,32 @@ size_t get_length(bp_array bp) void callBP_dispatch(void (*function)(bp_array), bp_array args) { - function(args); + function(args); } #define callBP(function, pointer, base, length) \ - callBP_dispatch((function), packBP((pointer), (base), (length))) + callBP_dispatch((function), packBP((pointer), (base), (length))) #else void call_and_free(void (*function)(bp_array), void *descriptor) { - function(descriptor); - free(descriptor); - return; + function(descriptor); + free(descriptor); + return; } void callBP_dispatch(void (*function)(bp_array), void *pointer, size_t base, size_t length) { - bp_array descriptor = packBP(pointer, base, length); + bp_array descriptor = packBP(pointer, base, length); - if (0 == base) - { - call_and_free(function, descriptor); - return; - } + if (0 == base) + { + call_and_free(function, descriptor); + return; + } - function(descriptor); - return; + function(descriptor); + return; } #define callBP(function, pointer, base, length) (callBP_dispatch(function, pointer, base, length)) diff --git a/timsort/timsort.c b/timsort/timsort.c index f6b8c69..bcc2b8e 100644 --- a/timsort/timsort.c +++ b/timsort/timsort.c @@ -6,25 +6,25 @@ */ void insertionSort(bp_array bp) { - size_t base = get_base(bp); - size_t length = (get_length(bp) / sizeof(int)) - base; - - int *arr = get_pointer(bp); - pp_cap(arr); - - int ix, ixValue, ixP; - for (ix = base + 1; ix < (base + length); ix++) - { - ixValue = arr[ix]; - ixP = ix - 1; - - while (ixP >= 0 && arr[ixP] > ixValue) - { - arr[ixP + 1] = arr[ixP]; - ixP = ixP - 1; - } - arr[ixP + 1] = ixValue; - } + size_t base = get_base(bp); + size_t length = (get_length(bp) / sizeof(int)) - base; + + int *arr = get_pointer(bp); + pp_cap(arr); + + int ix, ixValue, ixP; + for (ix = base + 1; ix < (base + length); ix++) + { + ixValue = arr[ix]; + ixP = ix - 1; + + while (ixP >= 0 && arr[ixP] > ixValue) + { + arr[ixP + 1] = arr[ixP]; + ixP = ixP - 1; + } + arr[ixP + 1] = ixValue; + } } /** @@ -33,53 +33,53 @@ void insertionSort(bp_array bp) */ void merge(bp_array bp) { - // allocations - size_t lengthFirstHalf = get_base(bp); - size_t lengthSecondHalf = (get_length(bp) / sizeof(int)) - lengthFirstHalf; - - int *arr = get_pointer(bp); - - int firstHalf[lengthFirstHalf]; - int secondHalf[lengthSecondHalf]; - - // copy to intermediate storage - memcpy(&firstHalf[0], &arr[0], lengthFirstHalf * sizeof(int)); - memcpy(secondHalf, &arr[lengthFirstHalf], lengthSecondHalf * sizeof(int)); - - // merge intermediate back to output - size_t ix_fst = 0; - size_t ix_snd = 0; - size_t ix_out = 0; - while ((ix_fst < lengthFirstHalf) && (ix_snd < lengthSecondHalf)) - { - if (firstHalf[ix_fst] <= secondHalf[ix_snd]) - { - arr[ix_out++] = firstHalf[ix_fst++]; - } - else - { - arr[ix_out++] = secondHalf[ix_snd++]; - } - } - - // copy stragglers - if (ix_fst < lengthFirstHalf) - { - size_t delta = lengthFirstHalf - ix_fst; - memcpy(&arr[ix_out], &firstHalf[ix_fst], delta * sizeof(int)); - ix_out += delta; - ix_fst += delta; - } - - if (ix_snd < lengthSecondHalf) - { - size_t delta = lengthSecondHalf - ix_snd; - memcpy(&arr[ix_out], &secondHalf[ix_snd], delta * sizeof(int)); - ix_out += delta; - ix_snd += delta; - } - - return; + // allocations + size_t lengthFirstHalf = get_base(bp); + size_t lengthSecondHalf = (get_length(bp) / sizeof(int)) - lengthFirstHalf; + + int *arr = get_pointer(bp); + + int firstHalf[lengthFirstHalf]; + int secondHalf[lengthSecondHalf]; + + // copy to intermediate storage + memcpy(&firstHalf[0], &arr[0], lengthFirstHalf * sizeof(int)); + memcpy(secondHalf, &arr[lengthFirstHalf], lengthSecondHalf * sizeof(int)); + + // merge intermediate back to output + size_t ix_fst = 0; + size_t ix_snd = 0; + size_t ix_out = 0; + while ((ix_fst < lengthFirstHalf) && (ix_snd < lengthSecondHalf)) + { + if (firstHalf[ix_fst] <= secondHalf[ix_snd]) + { + arr[ix_out++] = firstHalf[ix_fst++]; + } + else + { + arr[ix_out++] = secondHalf[ix_snd++]; + } + } + + // copy stragglers + if (ix_fst < lengthFirstHalf) + { + size_t delta = lengthFirstHalf - ix_fst; + memcpy(&arr[ix_out], &firstHalf[ix_fst], delta * sizeof(int)); + ix_out += delta; + ix_fst += delta; + } + + if (ix_snd < lengthSecondHalf) + { + size_t delta = lengthSecondHalf - ix_snd; + memcpy(&arr[ix_out], &secondHalf[ix_snd], delta * sizeof(int)); + ix_out += delta; + ix_snd += delta; + } + + return; } /** @@ -88,31 +88,31 @@ void merge(bp_array bp) */ void timSort(bp_array arr) { - size_t length = get_length(arr) / sizeof(int); - - if (length <= 1) - { - return; - } - - // insertion sort on `RUN_LENGTH` segments - for (size_t ix = 0; ix < length; ix += RUN_LENGTH) - { - size_t max_ix = min((ix + RUN_LENGTH) + 1, length); - callBP(insertionSort, get_pointer(arr), ix, max_ix * sizeof(int)); - } - // Merge window doubles every iteration - for (size_t size = RUN_LENGTH; size < length; size *= 2) - { - // Merge - for (size_t left = 0; left + size < length - 1; left += 2 * size) - { - size_t mid = left + size; - size_t right = min((left + 2 * size), length); - - callBP(merge, &(get_pointer(arr)[left]), mid, right * sizeof(int)); - } - } + size_t length = get_length(arr) / sizeof(int); + + if (length <= 1) + { + return; + } + + // insertion sort on `RUN_LENGTH` segments + for (size_t ix = 0; ix < length; ix += RUN_LENGTH) + { + size_t max_ix = min((ix + RUN_LENGTH) + 1, length); + callBP(insertionSort, get_pointer(arr), ix, max_ix * sizeof(int)); + } + // Merge window doubles every iteration + for (size_t size = RUN_LENGTH; size < length; size *= 2) + { + // Merge + for (size_t left = 0; left + size < length - 1; left += 2 * size) + { + size_t mid = left + size; + size_t right = min((left + 2 * size), length); + + callBP(merge, &(get_pointer(arr)[left]), mid, right * sizeof(int)); + } + } } /** @@ -124,36 +124,36 @@ void timSort(bp_array arr) */ int main(int argc, char *argv[]) { - for (size_t sz = 1; sz <= MAX_ARRAY_SZ; sz++) - { - // place data to be sorted on the heap and make a copy - int *arr = random_chunk(sz); - int *arr_cpy = malloc(sz * sizeof(int)); + for (size_t sz = 1; sz <= MAX_ARRAY_SZ; sz++) + { + // place data to be sorted on the heap and make a copy + int *arr = random_chunk(sz); + int *arr_cpy = malloc(sz * sizeof(int)); - assert(NULL != arr); - assert(NULL != arr_cpy); + assert(NULL != arr); + assert(NULL != arr_cpy); - memcpy(arr_cpy, arr, sz * sizeof(int)); + memcpy(arr_cpy, arr, sz * sizeof(int)); - // check copy - assert(arrEq(arr, arr_cpy, 0, sz - 1)); + // check copy + assert(arrEq(arr, arr_cpy, 0, sz - 1)); - // sort initial data set with TimSort - callBP(timSort, arr, 0, sz * sizeof(int)); + // sort initial data set with TimSort + callBP(timSort, arr, 0, sz * sizeof(int)); - // sort the copy with a stable quicksort - qsort(arr_cpy, sz, sizeof(int), cmpfunc); + // sort the copy with a stable quicksort + qsort(arr_cpy, sz, sizeof(int), cmpfunc); - // check we have done real work - assert(isSorted(arr, sz)); + // check we have done real work + assert(isSorted(arr, sz)); - // check the sorting is correct - assert(arrEq(arr, arr_cpy, 0, sz - 1)); + // check the sorting is correct + assert(arrEq(arr, arr_cpy, 0, sz - 1)); - // clean up - free(arr); - free(arr_cpy); - } + // clean up + free(arr); + free(arr_cpy); + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } \ No newline at end of file diff --git a/xor_pointers.c b/xor_pointers.c index b293a82..959a0b4 100644 --- a/xor_pointers.c +++ b/xor_pointers.c @@ -18,7 +18,7 @@ * 3) Complete spatial safety for C and C++ using * CHERI capabilities. * https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-949.pdf - See Section 3.9.1 XOR-linked lists (p56) + See Section 3.9.1 XOR-linked lists (p56) */ #include @@ -34,65 +34,65 @@ */ typedef struct cell { - WORD data; - WORD ptr; + WORD data; + WORD ptr; } cell_t; cell_t *alloc_cell(WORD payload, cell_t *tail) { - cell_t *cell = (cell_t *) malloc(sizeof(cell_t)); - cell->data = payload; - /* xor this cell's address into the tail's ptr field */ - if (tail != NULL) - tail->ptr ^= (WORD) cell; - /* store tail address in this cell's ptr field - * NOTE most recently allocated cell (i.e. head) - * only stores a single ptr, not an XOR'd pair - */ - cell->ptr = (WORD) tail; - return cell; + cell_t *cell = (cell_t *) malloc(sizeof(cell_t)); + cell->data = payload; + /* xor this cell's address into the tail's ptr field */ + if (tail != NULL) + tail->ptr ^= (WORD) cell; + /* store tail address in this cell's ptr field + * NOTE most recently allocated cell (i.e. head) + * only stores a single ptr, not an XOR'd pair + */ + cell->ptr = (WORD) tail; + return cell; } int main() { - int i = 0; - cell_t *head = NULL; - cell_t *prev; - cell_t *curr; - cell_t *next; - for (i = 0; i < NUM_CELLS; i++) - { - /* allocate a cell */ - head = alloc_cell(i, head); - } - /* now we have NUM_CELLS cells in a doubly-linked list */ - /* traverse list from head to tail */ - printf("%ld\n", head->data); - prev = head; - curr = (cell_t *) prev->ptr; - while (curr != NULL) - { - printf("%ld\n", curr->data); - // chase tail - next = (cell_t *) ((curr->ptr) ^ (WORD) prev); - // move along one - prev = curr; - curr = next; - } - /* now we are at the NULL ptr at the end of the list */ - /* let's traverse back to the start of the list */ - next = curr; - curr = prev; - prev = (cell_t *) ((curr->ptr) ^ (WORD) next); - while (curr != head) - { - printf("%ld\n", curr->data); - // chase reverse tail - next = curr; - curr = prev; - prev = (cell_t *) ((curr->ptr) ^ (WORD) next); - } - printf("%ld\n", head->data); + int i = 0; + cell_t *head = NULL; + cell_t *prev; + cell_t *curr; + cell_t *next; + for (i = 0; i < NUM_CELLS; i++) + { + /* allocate a cell */ + head = alloc_cell(i, head); + } + /* now we have NUM_CELLS cells in a doubly-linked list */ + /* traverse list from head to tail */ + printf("%ld\n", head->data); + prev = head; + curr = (cell_t *) prev->ptr; + while (curr != NULL) + { + printf("%ld\n", curr->data); + // chase tail + next = (cell_t *) ((curr->ptr) ^ (WORD) prev); + // move along one + prev = curr; + curr = next; + } + /* now we are at the NULL ptr at the end of the list */ + /* let's traverse back to the start of the list */ + next = curr; + curr = prev; + prev = (cell_t *) ((curr->ptr) ^ (WORD) next); + while (curr != head) + { + printf("%ld\n", curr->data); + // chase reverse tail + next = curr; + curr = prev; + prev = (cell_t *) ((curr->ptr) ^ (WORD) next); + } + printf("%ld\n", head->data); - return 0; + return 0; } From 063530a2b52330378e5c31fe8328ae29e6e364dc Mon Sep 17 00:00:00 2001 From: H Oliver Date: Fri, 3 Feb 2023 15:53:04 +0000 Subject: [PATCH 94/98] This example demonstrates a simple overflow of a pointer that has had 0x1001 bytes allocated using malloc(). It shows the expected behaviours in three different CHERI environments, displaying a message to the user immediately before the program is expected to crash in each respective environment: riscv64-purecap, morello-purecap and morello-hybrid. The program can also be compiled and run in a non-CHERI environment, but the behaviour is undefined, so the point at which the program crashes (or whether it crashes) is unpredictable. --- compare_platforms/Makefile.morello-hybrid | 6 + compare_platforms/Makefile.riscv64-purecap | 6 + compare_platforms/build/Makefile.review | 2 +- .../compare_platforms_overflow.c | 146 ++++++++++++++++++ tests/run_tests.sh | 2 + 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 compare_platforms/Makefile.morello-hybrid create mode 100644 compare_platforms/Makefile.riscv64-purecap create mode 100644 compare_platforms/compare_platforms_overflow.c diff --git a/compare_platforms/Makefile.morello-hybrid b/compare_platforms/Makefile.morello-hybrid new file mode 100644 index 0000000..777c033 --- /dev/null +++ b/compare_platforms/Makefile.morello-hybrid @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.morello-hybrid +include ../build/Makefile.vars.common +include build/Makefile.review \ No newline at end of file diff --git a/compare_platforms/Makefile.riscv64-purecap b/compare_platforms/Makefile.riscv64-purecap new file mode 100644 index 0000000..88e1794 --- /dev/null +++ b/compare_platforms/Makefile.riscv64-purecap @@ -0,0 +1,6 @@ +# Copyright (c) 2023 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../build/Makefile.vars.riscv64-purecap +include ../build/Makefile.vars.common +include build/Makefile.review \ No newline at end of file diff --git a/compare_platforms/build/Makefile.review b/compare_platforms/build/Makefile.review index d8bd778..763be48 100644 --- a/compare_platforms/build/Makefile.review +++ b/compare_platforms/build/Makefile.review @@ -4,7 +4,7 @@ # Common Makefile for the compare_platforms example. # This should not be invoked directly. -RUNDIR := $(RUNDIR)/review +RUNDIR := $(RUNDIR)/compare_platforms HFILES := $(wildcard include/*.h) $(wildcard ../include/*.h) export diff --git a/compare_platforms/compare_platforms_overflow.c b/compare_platforms/compare_platforms_overflow.c new file mode 100644 index 0000000..660aa9e --- /dev/null +++ b/compare_platforms/compare_platforms_overflow.c @@ -0,0 +1,146 @@ +/* + * This example demonstrates a simple overflow of a pointer that has had + * 0x1001 bytes allocated using malloc(). It shows the expected behaviours + * in three different CHERI environments, displaying a message to the user + * immediately before the program is expected to crash in each respective + * environment: + * + * - riscv64-purecap + * - morello-purecap + * - morello-hybrid + * + * The program can also be compiled and run in a non-CHERI environment, + * but the behaviour is undefined, so the point at which the program crashes + * (or whether it crashes) is unpredictable. + */ + +#ifdef __CHERI_PURE_CAPABILITY__ +#include "../include/common.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +struct review +{ + char *publicreview; + char *permissions; +}; + +void print_details(struct review *rv) +{ + printf("Public Review: %s\n", rv->publicreview); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); +} + +void overflow_publicreview(size_t offset, size_t reps, size_t overflow_amt, struct review *rv) +{ + printf("\nOverflowing publicreview by %zx\n", overflow_amt); + memset(rv->publicreview + offset, 'w', reps); + printf("Permissions: %s\n", rv->permissions); + fflush(stdout); +} + +char *min(char *a, char *b) +{ + return (a > b ? b : a); +} + +char *max(char *a, char *b) +{ + return (a > b ? a : b); +} + +void assert_positions(char *publicreview, char *permissions, size_t smsz, size_t bigsz) +{ + if (min(publicreview, permissions) == publicreview) + { + assert((publicreview + bigsz) <= permissions); + } + else + { + assert((permissions + smsz) <= publicreview); + } +} + +void free_pointers(struct review *rv) +{ + free(rv->publicreview); + free(rv->permissions); +} + +int main(int argc, char *argv[]) +{ + // initialization values for struct member sizes + const size_t smallsz = 0x20; + const size_t biggersz = 0x1001; + + struct review review; + + char *publicreview = malloc(biggersz); + review.publicreview = strcpy( + publicreview, "I am a little unclear as to the contribution. I think the authors could " + "strengthen their case considerably if they conducted an RCT. Weak reject."); + + char *permissions = malloc(smallsz); + review.permissions = strcpy(permissions, "r"); + + print_details(&review); + + // Just to prove that the allocated spaces of the pointers are not + // overlapping before we attempt to overflow them: + assert_positions(publicreview, permissions, smallsz, biggersz); + +#if (!defined(__CHERI_CAPABILITY_WIDTH__) && !defined(__CHERI_PURE_CAPABILITY__)) + printf("\nIn a non-CHERI environment, you should reach this line, after which " + "one of the attempts to overflow may or may not cause a crash.\n" + "This behaviour is undefined, so the timing of the crash is unpredictable, " + "if it happens at all.\n"); +#endif + +#if ((defined(__CHERI_PURE_CAPABILITY__)) && (defined(__aarch64__))) + printf("\nOn morello-purecap, you should reach this line, then the attempt to overflow by 1 " + "should " + "cause a SIGPROT.\n"); + printf("This is because the representable length of 0x%zx bytes is 0x%zx.\n", biggersz, + __builtin_cheri_round_representable_length(biggersz)); +#endif + + size_t offset = biggersz + 1; + overflow_publicreview(offset, 1, 1, &review); + + offset++; + const size_t oversz = review.permissions - review.publicreview + 2 - biggersz; + +#if ((defined(__CHERI_PURE_CAPABILITY__)) && (defined(__riscv))) + size_t riscv64_padding = (__builtin_cheri_round_representable_length(biggersz) - offset) + 1; + size_t overflow_amt = ((offset - biggersz) + riscv64_padding); + printf("\nOn riscv64-purecap, you should reach this line, then the attempt to overflow by %zx " + "should " + "cause a SIGPROT.\n", + overflow_amt); + printf("You got this far because the representable length of 0x%zx bytes is 0x%zx.\n", biggersz, + __builtin_cheri_round_representable_length(biggersz)); + overflow_publicreview(offset, riscv64_padding, overflow_amt, &review); +#endif + + overflow_publicreview(offset, oversz, oversz, &review); + + // If we reached this line, we should have acquired write privileges on the review. + print_details(&review); + + free_pointers(&review); + +#if (defined(__aarch64__) && defined(__CHERI_CAPABILITY_WIDTH__) && \ + !defined(__CHERI_PURE_CAPABILITY__)) + printf("\nOn morello-hybrid, you should make it all the way to the end without crashing.\n"); +#endif + + return 0; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index ad266e6..7c2dc26 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -70,6 +70,7 @@ if [ "$1" = "riscv64" ] || [ "$1" = "morello-purecap" ]; then run to_fail capability_sharing/cap-to-file cap_to_file run to_fail capability_sharing/leak-capability leak-capability run to_fail capability_sharing/mmap-shared-vs-private shared_file_main + run to_fail compare_platforms compare_platforms_overflow run to_fail compare_platforms compare_platforms_read_only elif [ "$1" = "morello-hybrid" ]; then # HYBRID TESTS @@ -80,6 +81,7 @@ elif [ "$1" = "morello-hybrid" ]; then run to_fail hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure-redirect_clr run to_fail hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure-update_ddc # Tests that should pass + run OK compare_platforms compare_platforms_overflow run OK hybrid/ddc_compartment_switching ddc_compartment_switching run OK hybrid basic_ddc run OK hybrid/compartment_examples/inter_comp_call/base main From c8debc26436c61110a48387d90fb6fcb21f5f151 Mon Sep 17 00:00:00 2001 From: Laurence Tratt Date: Fri, 22 Sep 2023 20:26:03 +0100 Subject: [PATCH 95/98] Clarify that GNU make is needed. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 90b8e33..eb1109a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Examples of CHERI fundamental operations, interesting corner cases, and simple demonstrative applications. -A makefile is provided for each supported platform, and a basic build command +A `Makefile` is provided for each supported platform, and a basic build command looks like this: ``` @@ -26,6 +26,8 @@ files for details. to the SDK can be given in `SDKBASE`. - For `run-` targets, a machine or model that can run the result, reachable by SSH. +- GNU make. Depending on your platform you might need to specify `gmake` rather + than `make`. [cheribuild]: https://github.com/CTSRD-CHERI/cheribuild From 5e4b679153a1d4408ceecf226810c697145dc5f4 Mon Sep 17 00:00:00 2001 From: Andrei Lascu Date: Mon, 13 Nov 2023 11:13:40 +0000 Subject: [PATCH 96/98] Migrate to Merge Queues --- .github/workflows/merge.yml | 9 +++++++++ bors.toml | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/merge.yml delete mode 100644 bors.toml diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 0000000..2890138 --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,9 @@ +on: + pull_request: + merge_group: + +jobs: + dummy: + runs-on: ubuntu-latest + steps: + - run: /usr/bin/true diff --git a/bors.toml b/bors.toml deleted file mode 100644 index 2fde3d7..0000000 --- a/bors.toml +++ /dev/null @@ -1,8 +0,0 @@ -status = ["buildbot/ci-builder"] - -timeout_sec = 600 # 10 minutes - -# Have bors delete auto-merged branches -delete_merged_branches = true - -cut_body_after = "" From c6a97cadd37f9d06854ff2a4a6bf6cb0826cf9ec Mon Sep 17 00:00:00 2001 From: Tom Wallis Date: Mon, 6 Nov 2023 15:17:07 +0000 Subject: [PATCH 97/98] Add `compartment_alloc` "side compartment" example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an example allocator which manages "side compartments", which are introduced in this example's README. There are two functions here which are relevant to creating compartments and allocating memory within them. 1. `init_compartment` `mmap`s an area of memory, creates a "compartment" (see `README` for a high-level definition, or `compartment_alloc.h` for the definition of the struct itself) and returns a sealed capability which can be used to identify a compartment when allocating memory. 2. `malloc_compartment` allocates memory within a compartment's `mmap`'d buffer. It accepts two arguments: a number of bytes to allocate, and a capability identifying a compartment (i.e. one returned by `init_compartment`). It returns a capability which points to memory within the compartment, assuming it's able to allocate (errors such as running our of space cause the program to exit with RC 1, because this is an example and it doesn't have to handle things particularly gracefully). There's also a function to free a compartment — `free_compartment` — which takes a compartment's identifying capability and frees the mmap'd buffer associated with it. This is naive and not a complete implementation; capabilities pointing to somewhere in that now-free buffer are still valid capabilities, but will be unsafe to use. --- .../compartment_alloc/Makefile.morello-hybrid | 6 + .../compartment_alloc/README.md | 84 ++++++++++++ .../compartment_alloc/compartment_alloc.c | 128 ++++++++++++++++++ .../compartment_alloc/compartment_alloc.h | 14 ++ example_allocators/compartment_alloc/main.c | 37 +++++ example_allocators/compartment_alloc/utils.h | 17 +++ tests/run_tests.sh | 1 + 7 files changed, 287 insertions(+) create mode 100644 example_allocators/compartment_alloc/Makefile.morello-hybrid create mode 100644 example_allocators/compartment_alloc/README.md create mode 100644 example_allocators/compartment_alloc/compartment_alloc.c create mode 100644 example_allocators/compartment_alloc/compartment_alloc.h create mode 100644 example_allocators/compartment_alloc/main.c create mode 100644 example_allocators/compartment_alloc/utils.h diff --git a/example_allocators/compartment_alloc/Makefile.morello-hybrid b/example_allocators/compartment_alloc/Makefile.morello-hybrid new file mode 100644 index 0000000..8e584f2 --- /dev/null +++ b/example_allocators/compartment_alloc/Makefile.morello-hybrid @@ -0,0 +1,6 @@ +# Copyright (c) 2021 The CapableVMs "CHERI Examples" Contributors. +# SPDX-License-Identifier: MIT OR Apache-2.0 + +include ../../build/Makefile.vars.morello-hybrid +include ../../build/Makefile.vars.common +include ../../build/Makefile.simple diff --git a/example_allocators/compartment_alloc/README.md b/example_allocators/compartment_alloc/README.md new file mode 100644 index 0000000..73d858d --- /dev/null +++ b/example_allocators/compartment_alloc/README.md @@ -0,0 +1,84 @@ +# Compartmentalising Allocator + +A modification of the "bump allocator" in this repo. This allocator creates +compartments on the heap, and returns capabilities to allocations within the +compartment. + +Because allocations are made against a compartment's (previously `mmap`'d) +buffer, we need to identify the compartment being used when we call `malloc`. +`init_compartment` returns a capability used to identify the compartment it +creates; calls to `malloc_compartment` accept this identifier as a parameter. + +`main.c` shows a simple use, with allocations against two different +compartments. + +Example is run using `make` in the typical fashion for the repo (i.e. along the +lines of `make -f Makefile.morello-hybrid run-main`). + +## What are "compartments" here? + +A "compartment" as created by this allocator is different conceptually to the +compartments created by the other examples in this repo. It's worth explaining +what "compartment" means in this context. + +Here, the "compartment" is some heap-allocated space that only capabilities are +used to access. There are obviously ways to abuse "just some space on the heap" +if the DDC isn't also restricted. The easiest way to explain this is to draw it +out. + +Assuming only the DDC is being used for compartmentalisation (so we're +constraining, say, dereferencing of addresses for data but not jump points etc) +and it's being used to bound which addresses can be legally dereferenced, we +could draw memory out as a "number line" of the address space and mark points +where a compartment's bounds begin and end. So we could diagram compartments +like so: + +``` +|--------------------| +0 g f F G ♾️ +``` + +…where a compartment's lower bound is a lowercase letter and its upper bound +the corresponding upper case letter (here ♾️ is the maximum address, and the `G` +compartment's lower bound is marked by `g`, and the upper case marked by `G`; +there's also a compartment, `F`, which is more restricted than `G` and is a +strict subset of `G`). Clearly, this just defines compartment bounds relative +to each other, and not in any kind of detail, but all we're interested in is +the relative positioning of compartment boundaries here. + +The compartments this example constructs are intended to sit _outwith_ all +other compartments, so that any access performed against them _must_ be done +through the capabilities returned by `malloc_compartment`. For this to work, +the DDC needs to be restricted when a program first runs, so that no future +compartment's bounds will overlap with the ones we construct on the heap. Then, +the ordinary compartments we create (derived from the now-restricted initial +DDC) can't overlap with these separated heap-y compartments. + +I've taken to thinking of them as _"side compartments"_ to differentiate them +from ordinary compartments. + +To illustrate what I mean, we can create a special space adjacent to our +ordinary compartments and allocations like this: + +``` +|---------------------| +0 >< ♾️ +``` + +…where `>` marks the upper bound of a DDC we install right when a program +launches, and `<` marks the lower bound of possible "side" compartments. To +extend the first diagram, we might make a side compartment that looks like…: + +``` +|------------------------------| +0 g f F G >< p P ♾️ +``` + +…and the "side" compartment `P` is what `compartment` means in this instance. +Those compartments are similar to the buffers created in the `bump-allocator` +example in their structure. + +The intended use of these compartments is to create an isolated area of memory +which is only accessible via capabilities, so that a process can't just +calculate an address within the isolated area and dereference it (because the +limited DDC prevents this). diff --git a/example_allocators/compartment_alloc/compartment_alloc.c b/example_allocators/compartment_alloc/compartment_alloc.c new file mode 100644 index 0000000..a6285b3 --- /dev/null +++ b/example_allocators/compartment_alloc/compartment_alloc.c @@ -0,0 +1,128 @@ +#include "compartment_alloc.h" +#include "utils.h" +#include +#include +#include +#include +#include + +heap_compartment compartments[maxCompartments]; +int numCompartments = 0; + +/* + * Initialise a compartment with `size_in_bytes` space, where all future + * allocations will be constrained by `dc` as if it was the DDC when allocation + * occurred. + */ +void *__capability init_compartment(size_t size_in_bytes, void *__capability dc) +{ + void *buf; + void *__capability compartment_id; + + if (numCompartments + 1 == maxCompartments) + { + perror("Too many compartments requested."); + exit(1); + } + + // Allocate memory for this compartment + buf = mmap(NULL, size_in_bytes, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); + if (buf == MAP_FAILED) + { + perror("error in initial mem allocation"); + exit(-1); + } + + // Create the new compartment + compartment_id = sealed_reference(buf); + compartments[numCompartments] = (heap_compartment){ + buf, // Space on the heap + 0, // bytes allocated (currently none!) + size_in_bytes, // Our maximum size, to check when allocating + dc, // The data capability to allocate against (like a DDC) + compartment_id // An identifier which allows a holder to allocate in this component + }; + numCompartments++; + + // Return the sealed capability which acts as a token of authentication for a component. + return compartment_id; +} + +/* + * Allocate `len` bytes in the compartment identified by `compartment_id`. + * if `compartment_id` doesn't exist, returns NULL + */ +void *__capability malloc_compartment(size_t len, void *__capability compartment_id) +{ + size_t rounded_length; + size_t new_allocated; + heap_compartment compartment = compartments[0]; + void *__capability allocated; + void *addr; + int i; + + // Search for a compartment with the given identifier + for (i = 0; i < maxCompartments && compartments[i].identifier != compartment_id; i++) + { + } + if (i == maxCompartments) + { + perror("Given an ID for a non-existent compartment"); + exit(1); + } + + compartment = compartments[i]; + + // Try to "bump-allocate" some space in the compartment's buffer + addr = compartment.buffer + compartment.bytes_allocated; + + addr = __builtin_align_up(addr, ~cheri_representable_alignment_mask(len) + 1); + rounded_length = cheri_representable_length(len); + + new_allocated = (addr + rounded_length) - compartment.buffer; + if (new_allocated > compartment.max_allocated) + { + perror("Maximum # bytes in compartment exceeded."); + exit(1); + } + + compartment.bytes_allocated = new_allocated; + + // We allocated some space! + // Create a capability pointing to it and return that. + // The capability inherits the metadata of data capability for this compartment. + allocated = cheri_address_set(compartment.datacap, (long) addr); + allocated = cheri_bounds_set_exact(allocated, rounded_length); + + return allocated; +} + +/* + * Unmaps the compartment identified by the `compartment_id` argument, + * effectively freeing it. + * + * Note that this is a naive implementation for demo purposes. The capabilities + * previously returned by `malloc_compartment` now point to de-allocated memory. + */ +void free_compartment(void *__capability compartment_id) +{ + int munmap_rc; + int i; + + // Search for a compartment with the given identifier + for (i = 0; i < maxCompartments && compartments[i].identifier != compartment_id; i++) + { + } + if (i == maxCompartments) + { + perror("Given an ID for a non-existent compartment"); + exit(1); + } + + munmap_rc = munmap(compartments[i].buffer, compartments[i].max_allocated); + if (munmap_rc != 0) + { + perror("Attempted to deallocate a compartment, but munmap errored"); + exit(1); + } +} diff --git a/example_allocators/compartment_alloc/compartment_alloc.h b/example_allocators/compartment_alloc/compartment_alloc.h new file mode 100644 index 0000000..7e4797a --- /dev/null +++ b/example_allocators/compartment_alloc/compartment_alloc.h @@ -0,0 +1,14 @@ +#define maxCompartments 5 + +typedef struct _heap_compartment +{ + void *buffer; + size_t bytes_allocated; + size_t max_allocated; + void *__capability datacap; + void *__capability identifier; // NOTE: THIS IS SEALED +} heap_compartment; + +void *__capability init_compartment(size_t size_in_bytes, void *__capability dc); +void *__capability malloc_compartment(size_t len, void *__capability component_id); +void free_compartment(void *__capability compartment_id); diff --git a/example_allocators/compartment_alloc/main.c b/example_allocators/compartment_alloc/main.c new file mode 100644 index 0000000..e5cedbd --- /dev/null +++ b/example_allocators/compartment_alloc/main.c @@ -0,0 +1,37 @@ +#include "../../include/common.h" +#include "compartment_alloc.c" + +int main() +{ + // Create capabilities which the heap compartments will construct capabilities against + // (as if they were the DDC when the allocation is made). + // One will be read-only, the other write-only, so we can differentiate when printing. + void *__capability dc1 = cheri_perms_and(cheri_ddc_get(), CHERI_PERM_STORE); + void *__capability dc2 = cheri_perms_and(cheri_ddc_get(), CHERI_PERM_LOAD); + + // Create two compartments, and receive the capability that gives us authority to use them + void *__capability compartment1 = init_compartment(4096, dc1); + void *__capability compartment2 = init_compartment(4096, dc2); + + // Allocate memory in each compartment + void *__capability c1_allocated_memory = malloc_compartment(64, compartment1); + void *__capability c2_allocated_memory = malloc_compartment(256, compartment2); + + // Print an explainer of each allocation (and the capability itself) + printf("\n\n\tThis capability is heap-allocated within our first compartment, which is " + "write-only.\n\tWe allocated 64 bytes.\n"); + pp_cap(c1_allocated_memory); + + printf("\n\n\tThis capability is heap-allocated within our second compartment, which is " + "read-only.\n\tWe allocated 256 bytes.\n"); + pp_cap(c2_allocated_memory); + + // Clean up compartments + free_compartment(compartment1); + free_compartment(compartment2); + + printf("\nFreed compartments.\n"); + + printf("\nCompleted successfully.\n"); + return 0; +} diff --git a/example_allocators/compartment_alloc/utils.h b/example_allocators/compartment_alloc/utils.h new file mode 100644 index 0000000..499ca22 --- /dev/null +++ b/example_allocators/compartment_alloc/utils.h @@ -0,0 +1,17 @@ +#include +#include + +// A shortcut to turn a pointer into a sealed capability +#define sealed_reference(ptr) cheri_seal((void *__capability) ptr, cheri_get_sealcap()) + +void *__capability cheri_get_sealcap() +{ + void *__capability sealcap; + size_t sealcap_size = sizeof(sealcap); + if (sysctlbyname("security.cheri.sealcap", &sealcap, &sealcap_size, NULL, 0) < 0) + { + printf("Fatal error. Cannot get `security.cheri.sealcap`."); + exit(1); + } + return sealcap; +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 7c2dc26..e585085 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -87,6 +87,7 @@ elif [ "$1" = "morello-hybrid" ]; then run OK hybrid/compartment_examples/inter_comp_call/base main run OK hybrid/compartment_examples/inter_comp_call/malicious_compartments inter_comp_call-secure run OK syscall-restrict syscall-restrict + run OK example_allocators/compartment_alloc main else echo "$1 not recognised." exit 1 From ae1be5c9a91a98d6c2fedfde4ff04f60f069a172 Mon Sep 17 00:00:00 2001 From: Tom Wallis Date: Tue, 14 Nov 2023 15:50:36 +0000 Subject: [PATCH 98/98] Compare `cheri_cap_build` & `cheri_address_set` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an example comparing `cheri_cap_build` and `cheri_address_set`. `cheri_cap_build` and `cheri_address_set` are very similar, but not quite the same. We were previously unsure of what the difference between the two was; this example uses both and explains the differences between the two. Briefly put, `cheri_address_set` takes two capabilities as arguments and returns a new capability with the metadata of the first arg and the address of the second. `cheri_cap_build` takes a capability and a `uintcap_t` — some bits to interpret as a capability, but not actually a capability — and returns a new capability constructed from the bits of the second (`uintcap_t`) argument, which is valid if and only if it is no more permissive than the first (cap) argument. There's a little more detail, but that's a summary. For more, there are a couple of useful comments on github: - https://github.com/capablevms/cheri-examples/pull/92#issuecomment-1818964541 - https://github.com/capablevms/cheri-examples/pull/39#discussion_r714883024 --- cap_build.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 cap_build.c diff --git a/cap_build.c b/cap_build.c new file mode 100644 index 0000000..5debca6 --- /dev/null +++ b/cap_build.c @@ -0,0 +1,154 @@ +// We've found little documentation of `cheri_cap_build`, and on the surface, +// it seems similar to `cheri_address_set`. These functions do similar things, +// but they're different. This example tries to demonstrate what those +// differences are. Briefly summarised…: +// +// - `cheri_address_set()` takes a cap and a ptr, and returns a new cap with +// the address set to the ptr passed in, derived from the cap passed in. +// It's basically taking an address and popping it into the appropriate +// place in an existing capability to create a new one. +// - `cheri_cap_build()` takes a cap and a uintcap_t (a uintptr_t equivalent +// for capabilities) and returns a new cap with the bits of the uintcap_t, +// which is valid if and only if it would be no more permissive than the cap +// passed as the first argument. This is taking all of the data in the +// `uintcap_t` and creating a cap from it, rather than deriving any of it +// from the first argument --- which differentiates `cheri_address_set` and +// `cheri_cap_build`. + +#include "cheriintrin.h" +#include "include/common.h" +#include +#include +#include + +int main() +{ + // We create two capabilities: x, and y, which is derived from x. We note + // that, though we use `cheri_address_set()`, it doesn't actually set (i.e. + // _change_) the address of its arguments, but construct a new capability + // with the address we pass. + // + // cheri_address_set(void* __capability, uintptr_t) -> void *__capability + void *__capability x = cheri_ddc_get(); + uintptr_t y_ptr = (uintptr_t) malloc(20); + void *__capability y = cheri_address_set(x, y_ptr); + + // The second capability printed below is created from the first using + // `cheri_address_set()`. Notice that `cheri_address_set()` created a new + // capability rather than setting the address on the first. + pp_cap(x); + pp_cap(y); + printf("\n\n"); + + // We create an (invalid!) capability by writing it directly. + void *my_cap_mem = malloc(16); + long cap; + cap = 0xdc5f400000030005; + memcpy(my_cap_mem + 8, &cap, 8); + cap = 0x00000000deadbeef; + memcpy(my_cap_mem + 0, &cap, 8); + void *__capability my_cap = (void *__capability) *(void *__capability *) my_cap_mem; + + // We build a new capability using `cheri_cap_build()`. Note that this + // capability is _valid_, because it's derived from `x`, which is valid. + // The address comes from `my_cap`, which isn't valid, but we can still + // create a valid capability beecause `cheri_cap_build` just reads it's + // address bits and constructs a capability. + + // To date we haven't found the specific type signature for this, but at + // https://www.morello-project.org/resources/morello-linux-morelloie/ + // we can see: + // void *__builtin_cheri_cap_build(const void *key, unsigned __intcap bits); + // After experimenting, we've found it wants to be treated as…: + // cheri_cap_build(void *__capability, uintcap_t) -> void *__capability + void *__capability valid_cap = cheri_cap_build(x, (uintcap_t) my_cap); + + // The first capability printed below is created by writing its structure + // into memory; it's not valid, it's just a bunch of bits. The second is an + // equivalent capability, which is constructed using `cheri_cap_build(). + // Notice that the second one is valid; that's because it's derived using a + // valid capability (the variable `x`, which contains a copy of the DDC). + pp_cap(my_cap); + pp_cap(valid_cap); + printf("\n\n"); + + + // It might appear that `cheri_cap_build` could be used to create + // capabilities with elevated permissions, by writing specific permission + // bits that we want to elevate before recreating a capability. That's not + // possible. To demonstrate, the rest of this example uses + // `cheri_cap_build` to rebuild capabilities from raw bits stored in the + // heap. + + // Write a copy of the DDC to heap, so we can play with it later + char *ddc_copy_space = malloc(16); + void *__capability ddc_copy_val = cheri_ddc_get(); + void **ddc_copy_raw_ptr = (void **) &ddc_copy_val; + memcpy(ddc_copy_space, ddc_copy_raw_ptr, 16); + + // Switch DDC, removing the mutable load permission, so we can attempt to + // restore permissions we _shouldn't_ have later on. + void *__capability newDDC = cheri_perms_and(cheri_ddc_get(), ~ARM_CAP_PERMISSION_MUTABLE_LOAD); + asm("MSR DDC, %[cap]\n\t" : : [cap] "C"(newDDC) : "memory"); + + // Create a capability and copy it into heap; use this to construct two new + // capabilities, one unmodified and one with restored permissions, both + // using cheri_cap_build We remove some permissions from the DDC and try to + // construct a capability that has them again. + int val = 5; + char *modspace = malloc(16); + void *__capability val_cap = (void *__capability) &val; + void **cap_bytes_raw_ptr = (void **) &val_cap; // a pointer to the cap's bytes + memcpy(modspace, cap_bytes_raw_ptr, 16); + + // Re-read the capability from heap and attempt to build it again. + // This works — so long as cheri_cap_build's first argument is a capability + // as permissive as the second argument, we can create a valid capability + // using the bits of the second argument. + // (a `uintcap_t` is the length of a capability) + void *__capability rebuilt_as_stored = cheri_cap_build( + cheri_ddc_get(), (uintcap_t) (void *__capability) *(void *__capability *) modspace); + pp_cap(rebuilt_as_stored); + + // Try to reconsitute the capability in memory with elevated permissions. + // First, restore all permissions to cap in heap using old DDC + modspace[14] = ddc_copy_space[14]; + modspace[15] = ddc_copy_space[15]; + + // Then use the copy of the DDC in heap to try to reconstruct a capability + // pointing to `val` with the additional permissions restored. We can't: + // when casting `ddc_copy_space` back to a capability, we create a + // capability using our currently installed DDC, which has been altered. + // That means the first argument doesn't have the permission we're trying + // to restore, so the second argument is more permissive, and we receive an + // invalid capability. + void *__capability rebuilt_constructed_from_ddc_in_heap = + cheri_cap_build((void *__capability) *(void *__capability *) ddc_copy_space, + (uintcap_t) (void *__capability) *(void *__capability *) modspace); + pp_cap(rebuilt_constructed_from_ddc_in_heap); + printf("\n"); + + // To demonstrate the difference between cheri_cap_build and + // cheri_address_set, here we remove all permissions on our heap-stored + // capability and restore it using `cheri_cap_build` and the DDC. Notice + // that the DDC has many permissions, but the capability we create still + // has none, because its value is determined by the second argument; _only + // its validity bit_ is determined by a comparison with the first. + // Afterwards, we recreate it using `cheri_address_set`, which takes + // metadata such as permissions from the first argument. + modspace[14] = 0x00; + modspace[15] = 0x00; + void *__capability rebuilt_with_no_perms = cheri_cap_build( + cheri_ddc_get(), (uintcap_t) (void *__capability) *(void *__capability *) modspace); + pp_cap(rebuilt_with_no_perms); + + void *__capability rebuilt_using_set_addr = cheri_address_set( + cheri_ddc_get(), (uintcap_t) (void *__capability) *(void *__capability *) modspace); + pp_cap(rebuilt_using_set_addr); + + free(ddc_copy_space); + free(modspace); + free((void *) y_ptr); + free(my_cap_mem); + return 0; +}