├── tools ├── cc++ └── ccc ├── .gitignore ├── src ├── cover │ └── README.md ├── missions │ ├── README.md │ ├── buffer-overflow-control-flow │ │ ├── btpalloc.h │ │ ├── buffer-overflow.c │ │ ├── main-asserts.inc │ │ ├── btpalloc.c │ │ └── README.md │ ├── kernel-FreeBSD-SA-18-13.nfs │ │ └── README.md │ ├── kernel-FreeBSD-SA-09-06.ktimer │ │ └── README.md │ ├── uninitialized-stack-frame-control-flow │ │ ├── README.md │ │ └── stack-mission.c │ └── use-after-free-control-flow │ │ ├── temporal-mission.c │ │ └── README.md ├── exercises │ ├── adapt-c │ │ ├── cat │ │ │ ├── Makefile │ │ │ ├── cat.h │ │ │ ├── cat.c │ │ │ └── methods.c │ │ ├── adapt-c.pptx │ │ ├── README.md │ │ └── answers.md │ ├── cheri-tags │ │ ├── Makefile │ │ ├── cheri-tags.pptx │ │ ├── corrupt-pointer.c │ │ ├── README.md │ │ └── answers.md │ ├── cheri-allocator │ │ ├── Makefile │ │ ├── cheri-allocator.pptx │ │ ├── answers.md │ │ ├── README.md │ │ └── cheri-allocator.c │ ├── pointer-injection │ │ ├── Makefile │ │ ├── answers.md │ │ ├── long-over-pipe.c │ │ ├── ptr-over-pipe.c │ │ └── README.md │ ├── type-confusion │ │ ├── Makefile │ │ ├── union-int-ptr.c │ │ ├── README.md │ │ └── answers.md │ ├── pointer-revocation │ │ ├── Makefile │ │ ├── temporal-control.c │ │ ├── answers.md │ │ └── README.md │ ├── buffer-overflow-heap │ │ ├── Makefile │ │ ├── buffer-overflow-heap.pptx │ │ ├── buffer-overflow-heap.c │ │ ├── README.md │ │ └── answers.md │ ├── control-flow-pointer │ │ ├── Makefile │ │ ├── asserts.inc │ │ ├── README.md │ │ ├── buffer-overflow-fnptr.c │ │ └── answers.md │ ├── subobject-bounds │ │ ├── Makefile │ │ ├── subobject-bounds.pptx │ │ ├── asserts.inc │ │ ├── buffer-overflow-subobject.c │ │ ├── subobject-list.c │ │ ├── README.md │ │ └── answers.md │ ├── buffer-overflow-global │ │ ├── Makefile │ │ ├── buffer-overflow-global.c │ │ ├── main-asserts.inc │ │ ├── README.md │ │ └── answers.md │ ├── buffer-overflow-stack │ │ ├── Makefile │ │ ├── buffer-overflow-stack.pptx │ │ ├── buffer-overflow-stack.c │ │ ├── README.md │ │ └── answers.md │ ├── cheriabi │ │ ├── Makefile │ │ ├── cheriabi.pptx │ │ ├── print-more.c │ │ ├── kern-read-over.c │ │ ├── perm-vmem.c │ │ ├── README.md │ │ └── answers.md │ ├── compile-and-run │ │ ├── print-pointer.c │ │ ├── Makefile │ │ ├── print-capability.c │ │ ├── answers.md │ │ └── README.md │ ├── common.mk │ ├── README.md │ └── debug-and-disassemble │ │ ├── README.md │ │ └── answers.md ├── appendix │ └── README.md ├── introduction │ ├── ccc.md │ ├── background.md │ ├── README.md │ └── cross-compilation-execution.md └── SUMMARY.md ├── book.toml ├── .github └── workflows │ ├── remove-old-artifacts.yml │ └── book.yml ├── README.md ├── LICENSES └── preferred │ ├── BSD-2-Clause │ └── BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 └── LICENSE /tools/cc++: -------------------------------------------------------------------------------- 1 | ccc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /src/cover/README.md: -------------------------------------------------------------------------------- 1 | {{#include ../../README.md:cover}} 2 | -------------------------------------------------------------------------------- /src/missions/README.md: -------------------------------------------------------------------------------- 1 | # Focused Adversarial Missions 2 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/cat/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= cat 2 | 3 | .include "../../common.mk" 4 | 5 | -------------------------------------------------------------------------------- /src/exercises/cheri-tags/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= corrupt-pointer 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/cheri-allocator/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= cheri-allocator 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/pointer-injection/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= long-over-pipe 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/type-confusion/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= union-int-ptr 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/pointer-revocation/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= temporal-control 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-heap/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= buffer-overflow-heap 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/control-flow-pointer/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= buffer-overflow-fnptr 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= buffer-overflow-subobject 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-global/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= buffer-overflow-global 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-stack/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= buffer-overflow-stack 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/Makefile: -------------------------------------------------------------------------------- 1 | PROGBASES= kern-read-over perm-vmem print-more 2 | 3 | .include "../common.mk" 4 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/adapt-c.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/adapt-c/adapt-c.pptx -------------------------------------------------------------------------------- /src/exercises/cheriabi/cheriabi.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/cheriabi/cheriabi.pptx -------------------------------------------------------------------------------- /src/exercises/cheri-tags/cheri-tags.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/cheri-tags/cheri-tags.pptx -------------------------------------------------------------------------------- /src/exercises/cheri-allocator/cheri-allocator.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/cheri-allocator/cheri-allocator.pptx -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/subobject-bounds.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/subobject-bounds/subobject-bounds.pptx -------------------------------------------------------------------------------- /src/appendix/README.md: -------------------------------------------------------------------------------- 1 | # Appendix 2 | 3 | This book and related source code are released under the following license: 4 | ``` 5 | {{#include ../../LICENSE}} 6 | ``` 7 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-heap/buffer-overflow-heap.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/buffer-overflow-heap/buffer-overflow-heap.pptx -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-stack/buffer-overflow-stack.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CTSRD-CHERI/cheri-exercises/HEAD/src/exercises/buffer-overflow-stack/buffer-overflow-stack.pptx -------------------------------------------------------------------------------- /src/missions/buffer-overflow-control-flow/btpalloc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 Jessica Clarke 4 | */ 5 | #include 6 | 7 | void *btpmalloc(size_t size); 8 | void btpfree(void *ptr); 9 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/asserts.inc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | _Static_assert(sizeof(b.buffer) == offsetof(struct buf, i), 8 | "There must be no padding in struct buf between buffer and i members"); 9 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["John Baldwin", "Jessica Clarke", "Brooks Davis", "Wes Filardo", "Robert N. M. Watson"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "Adversarial CHERI Exercises and Missions" 7 | 8 | [output.html] 9 | git-repository-url = "https://github.com/CTSRD-CHERI/cheri-exercises" 10 | site-url = "/cheri-exercises/" 11 | -------------------------------------------------------------------------------- /src/exercises/control-flow-pointer/asserts.inc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | _Static_assert(offsetof(struct buf, buffer) + sizeof(b.buffer) == 8 | offsetof(struct buf, callback), 9 | "There must be no padding between buffer and callback members"); 10 | -------------------------------------------------------------------------------- /src/exercises/compile-and-run/print-pointer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | int 8 | main(void) 9 | { 10 | printf("size of pointer: %zu\n", sizeof(void *)); 11 | /* XXX: ideally we'd use ptraddr_t below */ 12 | printf("size of address: %zu\n", sizeof(size_t)); 13 | 14 | return (0); 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/remove-old-artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Remove old artifacts 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | # Every day at 1am 7 | - cron: '0 1 * * *' 8 | 9 | jobs: 10 | remove-old-artifacts: 11 | runs-on: ubuntu-latest 12 | timeout-minutes: 10 13 | 14 | steps: 15 | - name: Remove old artifacts 16 | uses: c-hive/gha-remove-artifacts@v1 17 | with: 18 | age: '1 month' 19 | skip-tags: true 20 | skip-recent: 10 21 | -------------------------------------------------------------------------------- /src/exercises/pointer-injection/answers.md: -------------------------------------------------------------------------------- 1 | # Answers 2 | 3 | 2. Expected output: 4 | ``` 5 | # ./long-over-pipe-riscv 6 | received 42 7 | # ./long-over-pipe-cheri 8 | received 42 9 | ``` 10 | 4. Expected output: 11 | ``` 12 | # ./ptr-over-pipe-riscv 13 | received Hello world! 14 | # ./ptr-over-pipe-cheri 15 | In-address space security exception (core dumped) 16 | ``` 17 | 5. Because the tag is stripped when sent via message-passing IPC, leading 18 | to a tag violation on dereference. 19 | -------------------------------------------------------------------------------- /src/exercises/compile-and-run/Makefile: -------------------------------------------------------------------------------- 1 | .include "../common.mk" 2 | 3 | all: print-pointer-baseline print-pointer-cheri print-capability 4 | 5 | print-pointer-baseline: print-pointer.c 6 | $(CC) -o $@ ${.ALLSRC} $(CFLAGS) $(CFLAGS_BASEABI) 7 | 8 | print-pointer-cheri: print-pointer.c 9 | $(CC) -o $@ ${.ALLSRC} $(CFLAGS) $(CFLAGS_CHERIABI) 10 | 11 | print-capability: print-capability.c 12 | $(CC) -o $@ ${.ALLSRC} $(CFLAGS) $(CFLAGS_CAPABI) 13 | 14 | clean: 15 | rm print-pointer-baseline print-pointer-cheri print-capability 16 | -------------------------------------------------------------------------------- /src/exercises/compile-and-run/print-capability.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | #include 7 | 8 | int 9 | main(void) 10 | { 11 | int i; 12 | char *c; 13 | void *cap_to_int = &i; 14 | void *cap_to_cap = &c; 15 | 16 | printf("cap to int length: %lu\n", cheri_length_get(cap_to_int)); 17 | printf("cap to cap length: %lu\n", cheri_length_get(cap_to_cap)); 18 | 19 | return (0); 20 | } 21 | -------------------------------------------------------------------------------- /src/exercises/type-confusion/union-int-ptr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | const char hello[] = "Hello World!"; 8 | 9 | union long_ptr { 10 | long l; 11 | const char *ptr; 12 | } lp = { .ptr = hello }; 13 | 14 | void 15 | inc_long_ptr(union long_ptr *lpp) 16 | { 17 | lpp->l++; 18 | } 19 | 20 | int 21 | main(void) 22 | { 23 | printf("lp.ptr %s\n", lp.ptr); 24 | inc_long_ptr(&lp); 25 | printf("lp.ptr %s\n", lp.ptr); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/exercises/compile-and-run/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Compile and run RISC-V and CHERI-RISC-V programs 2 | 3 | This exercise explores the difference in size between addresses and 4 | pointers, drawing attention to the pointer-focused nature of CHERI memory 5 | protection. 6 | 7 | 2. Expected output: 8 | ``` 9 | # ./print-pointer-riscv 10 | size of pointer: 8 11 | size of address: 8 12 | ``` 13 | 4. Expected output: 14 | ``` 15 | # ./print-pointer-cheri 16 | size of pointer: 16 17 | size of address: 8 18 | ``` 19 | 6. Expected output: 20 | ``` 21 | # ./print-capability 22 | cap to int length: 4 23 | cap to cap length: 16 24 | ``` 25 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/buffer-overflow-subobject.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | struct buf { 8 | char buffer[128]; 9 | int i; 10 | } b; 11 | 12 | #pragma weak fill_buf 13 | void 14 | fill_buf(char *buf, size_t len) 15 | { 16 | for (size_t i = 0; i <= len; i++) 17 | buf[i] = 'b'; 18 | } 19 | 20 | int 21 | main(void) 22 | { 23 | b.i = 'c'; 24 | printf("b.i = %c\n", b.i); 25 | 26 | fill_buf(b.buffer, sizeof(b.buffer)); 27 | 28 | printf("b.i = %c\n", b.i); 29 | 30 | return 0; 31 | } 32 | 33 | #include "asserts.inc" 34 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-global/buffer-overflow-global.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | char buffer[128]; 8 | char c; 9 | 10 | #pragma weak fill_buf 11 | void 12 | fill_buf(char *buf, size_t len) 13 | { 14 | for (size_t i = 0; i <= len; i++) 15 | buf[i] = 'b'; 16 | } 17 | 18 | #include "main-asserts.inc" 19 | 20 | int 21 | main(void) 22 | { 23 | (void)buffer; 24 | main_asserts(); 25 | 26 | c = 'c'; 27 | printf("c = %c\n", c); 28 | 29 | fill_buf(buffer, sizeof(buffer)); 30 | 31 | printf("c = %c\n", c); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /src/exercises/common.mk: -------------------------------------------------------------------------------- 1 | CC=cc 2 | CFLAGS=-g -O2 -Wall -Wcheri 3 | 4 | ARCH!=uname -m 5 | .if ${ARCH} == "arm64" 6 | CFLAGS_BASELINE=-march=morello+noa64c -mabi=aapcs 7 | CFLAGS_CHERI=-march=morello -mabi=purecap 8 | .else 9 | CFLAGS_BASELINE=-march=rv64gc -mabi=lp64d 10 | CFLAGS_CHERI=-march=rv64gcxcheri -mabi=l64pc128d 11 | .endif 12 | 13 | .for progbase in ${PROGBASES} 14 | .for variant in baseline cheri 15 | CLEANFILES+= ${progbase}-${variant} 16 | 17 | all: ${progbase}-${variant} 18 | 19 | ${progbase}-${variant}: ${progbase}.c 20 | ${CC} -o ${.TARGET} ${.ALLSRC} ${CFLAGS} ${CFLAGS_${variant:tu}} 21 | .endfor 22 | .endfor 23 | 24 | clean: 25 | rm -f ${CLEANFILES} 26 | 27 | .PHONY: all clean 28 | -------------------------------------------------------------------------------- /src/exercises/pointer-injection/long-over-pipe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | int 10 | main(void) 11 | { 12 | int fds[2]; 13 | pid_t pid; 14 | long val; 15 | 16 | if (pipe(fds) == -1) 17 | err(1, "pipe"); 18 | if ((pid = fork()) == -1) 19 | err(1, "fork"); 20 | if (pid == 0) { 21 | val = 42; 22 | if (write(fds[0], &val, sizeof(val)) != sizeof(val)) 23 | err(1, "write"); 24 | } else { 25 | if (read(fds[1], &val, sizeof(val)) != sizeof(val)) 26 | err(1, "read"); 27 | printf("received %ld\n", val); 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/exercises/pointer-injection/ptr-over-pipe.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | const char hello[] = "Hello world!"; 10 | 11 | int 12 | main(void) 13 | { 14 | int fds[2]; 15 | pid_t pid; 16 | const char *ptr; 17 | 18 | if (pipe(fds) == -1) 19 | err(1, "pipe"); 20 | if ((pid = fork()) == -1) 21 | err(1, "fork"); 22 | if (pid == 0) { 23 | ptr = hello; 24 | if (write(fds[0], &ptr, sizeof(ptr)) != sizeof(ptr)) 25 | err(1, "write"); 26 | } else { 27 | if (read(fds[1], &ptr, sizeof(ptr)) != sizeof(ptr)) 28 | err(1, "read"); 29 | printf("received %s\n", ptr); 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/exercises/type-confusion/README.md: -------------------------------------------------------------------------------- 1 | # Exercise integer-pointer type confusion bug 2 | 3 | This exercise demonstrates how CHERI distinguishes between integer and pointer 4 | types, preventing certain types of type confusion. 5 | In this example, a union allows an integer value to be used as a pointer, 6 | which cannot then be dereferenced. 7 | 8 | 1. Compile `union-int-ptr.c` with a RISC-V target and binary name of 9 | `union-int-ptr-riscv`, and with a CHERI-RISC-V target and binary name 10 | `union-int-ptr-cheri`. 11 | 12 | **union-int-ptr.c** 13 | ```C 14 | {{#include union-int-ptr.c}} 15 | ``` 16 | 2. Run the RISC-V program. What is the result? 17 | 3. Run the CHERI-RISC-V program. What is the result? 18 | Run under `gdb` and explain why the program crashes in the second `printf`. 19 | -------------------------------------------------------------------------------- /src/exercises/type-confusion/answers.md: -------------------------------------------------------------------------------- 1 | # Answers: Exercise integer-pointer type confusion bug 2 | 3 | When the integer value is updated, with CHERI-RISC-V compilation the 4 | pointer side will no longer be dereferenceable, as the tag has been cleared. 5 | 6 | 2. Expected output: 7 | ``` 8 | # ./union-int-ptr-riscv 9 | lp.ptr Hello World! 10 | lp.ptr ello World! 11 | ``` 12 | The `long` member was loaded and stored as an integer (this is identical 13 | to the way it would have been handled if the pointer member were 14 | incremented instead). 15 | 16 | 3. Expected output: 17 | ``` 18 | # ./union-int-ptr-cheri 19 | lp.ptr Hello World! 20 | In-address space security exception (core dumped) 21 | ``` 22 | When the `long` member was loaded and stored, it caused the tag to be 23 | cleared on the pointer. 24 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-global/main-asserts.inc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | #include 7 | #ifdef __CHERI_PURE_CAPABILITY__ 8 | #include 9 | #endif 10 | 11 | #ifndef nitems 12 | #define nitems(x) (sizeof((x)) / sizeof((x)[0])) 13 | #endif 14 | 15 | static void 16 | main_asserts(void) 17 | { 18 | /* 19 | * Ensure that overflowing `buffer` by 1 will hit `c`. 20 | * In the pure-capabilty case, don't assert if the size of 21 | * `buffer` requires padding. 22 | */ 23 | assert((ptraddr_t)&buffer[nitems(buffer)] == (ptraddr_t)&c 24 | #ifdef __CHERI_PURE_CAPABILITY__ 25 | || sizeof(buffer) < cheri_representable_length(sizeof(buffer)) 26 | #endif 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-stack/buffer-overflow-stack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #pragma weak write_buf 10 | void 11 | write_buf(char *buf, size_t ix) 12 | { 13 | buf[ix] = 'b'; 14 | } 15 | 16 | int 17 | main(void) 18 | { 19 | char upper[0x10]; 20 | char lower[0x10]; 21 | 22 | printf("upper = %p, lower = %p, diff = %zx\n", 23 | upper, lower, (size_t)(upper - lower)); 24 | 25 | /* Assert that these get placed how we expect */ 26 | assert((ptraddr_t)upper == (ptraddr_t)&lower[sizeof(lower)]); 27 | 28 | upper[0] = 'a'; 29 | printf("upper[0] = %c\n", upper[0]); 30 | 31 | write_buf(lower, sizeof(lower)); 32 | 33 | printf("upper[0] = %c\n", upper[0]); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/exercises/control-flow-pointer/README.md: -------------------------------------------------------------------------------- 1 | # Corrupt a control-flow pointer using a subobject buffer overflow 2 | 3 | This exercise demonstrates how CHERI pointer integrity protection prevents 4 | a function pointer overwritten with data due to a buffer overflow from being 5 | used for further memory access. 6 | 7 | 1. Compile `buffer-overflow-fnptr.c` with a RISC-V target and binary name 8 | of `buffer-overflow-fnptr-riscv`, and a CHERI-RISC-V target and binary 9 | name of `buffer-overflow-fnptr-cheri`. Do not enable compilation with 10 | subobject bounds protection when compiling with the CHERI-RISC-V target. 11 | 12 | **buffer-overflow-fnptr.c** 13 | ```C 14 | {{#include buffer-overflow-fnptr.c}} 15 | ``` 16 | 2. Run the RISC-V program under GDB; why does it crash? 17 | 3. Run the CHERI-RISC-V program under GDB; why does it crash? 18 | 19 | ## Support code 20 | 21 | **asserts.inc** 22 | ```C 23 | {{#include asserts.inc}} 24 | ``` 25 | -------------------------------------------------------------------------------- /src/exercises/control-flow-pointer/buffer-overflow-fnptr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | #include 6 | 7 | struct buf { 8 | size_t length; 9 | int buffer[30]; 10 | size_t (*callback)(struct buf *); 11 | }; 12 | 13 | void 14 | fill_buf(struct buf *bp) 15 | { 16 | bp->length = sizeof(bp->buffer)/sizeof(*bp->buffer); 17 | for (size_t i = 0; i <= bp->length; i++) 18 | bp->buffer[i] = 0xAAAAAAAA; 19 | } 20 | 21 | size_t 22 | count_screams(struct buf *bp) 23 | { 24 | int screams = 0; 25 | 26 | for (size_t i = 0; i < bp->length; i++) 27 | screams += bp->buffer[i] == 0xAAAAAAAA ? 1 : 0; 28 | return screams; 29 | } 30 | 31 | struct buf b = {.callback = count_screams}; 32 | 33 | int 34 | main(void) 35 | { 36 | fill_buf(&b); 37 | 38 | printf("Words of screaming in b.buffer %zu\n", b.callback(&b)); 39 | 40 | return 0; 41 | } 42 | 43 | #include "asserts.inc" 44 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/print-more.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef __CHERI_PURE_CAPABILITY__ 12 | #define PRINTF_PTR "#p" 13 | #else 14 | #define PRINTF_PTR "p" 15 | #endif 16 | 17 | static const int rodata_const = 42; 18 | static int (*const relro_ptr)(const char *, ...) = printf; 19 | static int (*rw_ptr)(const char *, ...) = printf; 20 | 21 | int 22 | main(int argc, char **argv) 23 | { 24 | int stack_local; 25 | 26 | printf("&argv[0]=%" PRINTF_PTR "\n", &argv[0]); 27 | printf(" argv[0]=%" PRINTF_PTR "\n", argv[0]); 28 | printf("&stack_local=%" PRINTF_PTR "\n", &stack_local); 29 | 30 | printf("&rodata_const=%" PRINTF_PTR "\n", &rodata_const); 31 | 32 | printf("&relro_ptr=%" PRINTF_PTR "\n", &relro_ptr); 33 | printf("&rw_ptr=%" PRINTF_PTR "\n", &rw_ptr); 34 | 35 | printf("printf=%" PRINTF_PTR "\n", printf); 36 | } 37 | -------------------------------------------------------------------------------- /src/exercises/compile-and-run/README.md: -------------------------------------------------------------------------------- 1 | # Compile and run RISC-V and CHERI-RISC-V programs 2 | 3 | This exercise steps you through getting up and running with code compilation 4 | and execution for RISC-V and CHERI-RISC-V programs. 5 | 6 | The first test program is written in conventional C, and can be compiled 7 | to RISC-V or CHERI-RISC-V targets: 8 | 9 | 1. Compile `print-pointer.c` with a RISC-V target and a binary name of 10 | `print-pointer-riscv`. 11 | 12 | **print-pointer.c:** 13 | ```C 14 | {{#include print-pointer.c}} 15 | ``` 16 | 2. Run the binary. 17 | 3. Compile `print-pointer.c` with a CHERI-RISC-V target and a binary name 18 | of `print-pointer-cheri`. 19 | 4. Run the binary: it should print a pointer size of `16` and address size 20 | of `8`. 21 | 22 | The second test program is written in CHERI C: 23 | 24 | 5. Compile `print-capability.c` with a CHERI-RISC-V target and a binary name 25 | of `print-capability`. 26 | ```C 27 | {{#include print-capability.c}} 28 | ``` 29 | 6. Run the binary: note how the length of the capability depends on the size of 30 | the type it points to. 31 | -------------------------------------------------------------------------------- /src/exercises/pointer-injection/README.md: -------------------------------------------------------------------------------- 1 | # Demonstrate pointer injection 2 | 3 | This exercise demonstrates how CHERI's pointer provenance validity prevents 4 | injected pointer values from being dereferenced. 5 | In this example code, a pointer is injected via pipe IPC, and then 6 | dereferenced. 7 | 8 | 1. Compile `long-over-pipe.c` with a RISC-V target and a binary name of 9 | `long-over-pipe-riscv`, and with a CHERI-RISC-V target and a binary 10 | name of `long-over-pipe-cheri`. 11 | ```C 12 | {{#include long-over-pipe.c}} 13 | ``` 14 | 2. Run the two binaries, which both send long integers over pipe IPC, and 15 | print the sent and received values. 16 | 3. Compile `ptr-over-pipe.c` with a RISC-V target and a binary name of 17 | `ptr-over-pipe-riscv`, and with a CHERI-RISC-V target and a binary name of 18 | `ptr-over-pipe-cheri`. 19 | ```C 20 | {{#include ptr-over-pipe.c}} 21 | ``` 22 | 4. Run the two binaries, which both send pointers over pipe IPC, and then 23 | dereference the received copy to print a string. 24 | 5. Why does dereferencing the received pointer in a CHERI binary fail? 25 | -------------------------------------------------------------------------------- /src/exercises/control-flow-pointer/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Corrupt a control-flow pointer using a subobject buffer overflow 2 | 3 | 2. Example session: 4 | ``` 5 | (gdb) r 6 | Starting program: /root/buffer-overflow-fnptr-riscv 7 | 8 | Program received signal SIGSEGV, Segmentation fault. 9 | 0x00000000aaaaaaaa in ?? () 10 | ``` 11 | The program attempted an instruction fetch from a nonsensical address 12 | `0xaaaaaaaa`. 13 | 14 | 3. Example session: 15 | ``` 16 | (gdb) r 17 | Starting program: /root/buffer-overflow-fnptr-cheri 18 | 19 | Program received signal SIGPROT, CHERI protection violation 20 | Capability tag fault caused by register ca1. 21 | 0x0000000000101c5e in main () 22 | at src/exercises/control-flow-pointer/buffer-overflow-fnptr.c:34 23 | 34 src/exercises/control-flow-pointer/buffer-overflow-fnptr.c: No such file or directory. 24 | (gdb) info reg ca1 25 | ca1 0xd11720000801800600000000aaaaaaaa 0xaaaaaaaa [rxR,0xaaaa0000-0xaaaa4000] (sentry) 26 | (gdb) x/i $pcc 27 | => 0x101c5e : cjalr cra,ca1 28 | ``` 29 | The program attempted to load an instruction via an untagged capability `ca1`. 30 | -------------------------------------------------------------------------------- /src/exercises/cheri-tags/corrupt-pointer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __CHERI_PURE_CAPABILITY__ 10 | #define PRINTF_PTR "#p" 11 | #else 12 | #define PRINTF_PTR "p" 13 | #endif 14 | 15 | int 16 | main(void) 17 | { 18 | char buf[0x1FF]; 19 | 20 | volatile union { 21 | char *ptr; 22 | char bytes[sizeof(char*)]; 23 | } p; 24 | 25 | for (size_t i = 0; i < sizeof(buf); i++) { 26 | buf[i] = i; 27 | } 28 | p.ptr = &buf[0x10F]; 29 | 30 | printf("buf=%" PRINTF_PTR " &p=%" PRINTF_PTR "\n", buf, &p); 31 | printf("p.ptr=%" PRINTF_PTR " (0x%zx into buf) *p.ptr=%02x\n", 32 | p.ptr, p.ptr - buf, *p.ptr); 33 | 34 | /* One way to align the address down */ 35 | char *q = (char*)(((uintptr_t)p.ptr) & ~0xFF); 36 | printf("q=%" PRINTF_PTR " (0x%zx into buf)\n", q, q - buf); 37 | 38 | printf("*q=%02x\n", *q); 39 | 40 | /* Maybe another, assuming a little-endian machine. */ 41 | p.bytes[0] = 0; 42 | char *r = p.ptr; 43 | 44 | printf("r=%" PRINTF_PTR " (0x%zx)\n", r, r - buf); 45 | printf("*r=%02x\n", *r); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/missions/buffer-overflow-control-flow/buffer-overflow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 Jessica Clarke 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "btpalloc.h" 10 | 11 | void 12 | success(void) 13 | { 14 | puts("Exploit successful!"); 15 | } 16 | 17 | void 18 | failure(void) 19 | { 20 | puts("Exploit unsuccessful!"); 21 | } 22 | 23 | static uint16_t 24 | ipv4_checksum(uint16_t *buf, size_t words) 25 | { 26 | uint16_t *p; 27 | uint_fast32_t sum; 28 | 29 | sum = 0; 30 | for (p = buf; words > 0; --words, ++p) { 31 | sum += *p; 32 | if (sum > 0xffff) 33 | sum -= 0xffff; 34 | } 35 | 36 | return (~sum & 0xffff); 37 | } 38 | 39 | #include "main-asserts.inc" 40 | 41 | int 42 | main(void) 43 | { 44 | int ch; 45 | char *buf, *p; 46 | uint16_t sum; 47 | void (**fptr)(void); 48 | 49 | buf = btpmalloc(25000); 50 | fptr = btpmalloc(sizeof(*fptr)); 51 | 52 | main_asserts(buf, fptr); 53 | 54 | *fptr = &failure; 55 | 56 | p = buf; 57 | while ((ch = getchar()) != EOF) 58 | *p++ = (char)ch; 59 | 60 | if ((uintptr_t)p & 1) 61 | *p++ = '\0'; 62 | 63 | sum = ipv4_checksum((uint16_t *)buf, (p - buf) / 2); 64 | printf("Checksum: 0x%04x\n", sum); 65 | 66 | btpfree(buf); 67 | 68 | (**fptr)(); 69 | 70 | btpfree(fptr); 71 | 72 | return (0); 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Adversarial CHERI Exercises and Missions 4 | 5 | Robert N. M. Watson (University of Cambridge), Brooks Davis (SRI 6 | International), Wes Filardo (Microsoft Research), Jessica Clarke (University of 7 | Cambridge) and John Baldwin (Ararat River Consulting). 8 | 9 | This repository contains a series of skills development and adversarial 10 | exercises for [CHERI](http://cheri-cpu.org), specifically aimed at the 11 | CHERI-RISC-V implementation. 12 | 13 | ## Acknowledgements 14 | 15 | The authors gratefully acknowledge Reuben Broadfoot, Lawrence Esswood, Brett 16 | Gutstein, Joe Kiniry, Alex Richardson, Austin Roach, and Daniel Zimmerman for 17 | their feedback and support in developing these exercises. 18 | 19 | *Some portions of this document remain a work-in-progress. Feedback and 20 | contributions are welcomed. Please see our [GitHub 21 | Repository](https://github.com/CTSRD-CHERI/cheri-exercises) for the source 22 | code and an issue tracker.* 23 | 24 | 25 | 26 | ## Building 27 | 28 | Building the book from the Markdown sources requires 29 | [mdBook](https://github.com/rust-lang/mdBook). Once installed, `mdbook build` 30 | will build the static HTML files in the `book/` directory, whilst `mdbook 31 | serve` will build and serve them at `http://localhost:3000`. Please refer to 32 | the mdBook documentation for futher options. 33 | -------------------------------------------------------------------------------- /src/missions/buffer-overflow-control-flow/main-asserts.inc: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 Jessica Clarke 4 | */ 5 | #include 6 | #include 7 | #ifdef __CHERI_PURE_CAPABILITY__ 8 | #include 9 | #endif 10 | 11 | static void 12 | main_asserts(void *buf, void *fptr) 13 | { 14 | uintptr_t ubuf = (uintptr_t)buf; 15 | uintptr_t ufptr = (uintptr_t)fptr; 16 | #ifdef __CHERI_PURE_CAPABILITY__ 17 | ptraddr_t ubuf_top; 18 | #endif 19 | 20 | #ifdef __CHERI_PURE_CAPABILITY__ 21 | ubuf_top = cheri_base_get(ubuf) + cheri_length_get(ubuf); 22 | #endif 23 | 24 | #if defined(__CHERI_PURE_CAPABILITY__) && !defined(CHERI_NO_ALIGN_PAD) 25 | /* 26 | * For the normal pure-capability case, `buf`'s allocation should be 27 | * adequately padded to ensure precise capability bounds and `fptr` 28 | * should be adjacent. 29 | */ 30 | assert(ubuf_top == ufptr); 31 | #else 32 | /* 33 | * Otherwise `fptr` should be 8 bytes (not 0 due to malloc's alignment 34 | * requirements) after the end of `buf`. 35 | */ 36 | assert(ubuf + 25008 == ufptr); 37 | #ifdef __CHERI_PURE_CAPABILITY__ 38 | /* 39 | * For pure-capability code this should result in the bounds of the 40 | * large `buf` allocation including all of `fptr`. 41 | */ 42 | assert(ubuf_top >= ufptr + sizeof(void *)); 43 | #endif 44 | #endif 45 | } 46 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-stack/README.md: -------------------------------------------------------------------------------- 1 | # Exercise an inter-stack-object buffer overflow 2 | 3 | This exercise demonstrates an inter-object buffer overflow on baseline and 4 | CHERI-enabled architectures, and asks you to characterize and fix the bug 5 | detected by CHERI bounds enforcement. It also asks you to use GDB for 6 | debugging purposes. 7 | 8 | By contrast to [the globals-based example](../buffer-overflow-global), this 9 | example uses two *stack* objects to demonstrate the overflow. We will be able 10 | to see the CHERI C compiler generate code to apply spatial bounds on the 11 | capability used for the buffer pointer we pass around. 12 | 13 | 1. Compile `buffer-overflow-stack.c` for the baseline architecture to 14 | the binary `buffer-overflow-stack-baseline` and for the CHERI-aware 15 | architecture to `buffer-overflow-stack-cheri`. 16 | 17 | 2. Run both programs and observe their outputs. 18 | 19 | 3. Using GDB on the core dump (or run the CHERI program under `gdb`): 20 | Why has the CHERI program failed? 21 | 22 | 4. Compare and contrast the disassembly of the baseline and CHERI programs. 23 | In particular, focus on the `write_buf` function and `main`'s call to it 24 | and the information flow leading up to it. 25 | 26 | ## Source 27 | 28 | **buffer-overflow-stack.c** 29 | ```C 30 | {{#include buffer-overflow-stack.c}} 31 | ``` 32 | 33 | ## Courseware 34 | 35 | This exercise has [presentation materials](./buffer-overflow-stack.pptx) 36 | available. 37 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/kern-read-over.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | * Copyright (c) 2022 Microsoft Corporation 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define bsz 16 15 | 16 | int 17 | main(void) 18 | { 19 | int fds[2]; 20 | pid_t pid; 21 | 22 | if (pipe(fds) == -1) 23 | err(1, "pipe"); 24 | if ((pid = fork()) == -1) 25 | err(1, "fork"); 26 | if (pid == 0) { 27 | char out[2*bsz]; 28 | for (size_t i = 0; i < sizeof(out); i++) { 29 | out[i] = 0x10 + i; 30 | } 31 | 32 | if (write(fds[0], out, sizeof(out)) != sizeof(out)) { 33 | err(1, "write"); 34 | } 35 | printf("Write OK\n"); 36 | } else { 37 | int res; 38 | char upper[bsz] = { 0 }; 39 | char lower[bsz] = { 0 }; 40 | 41 | waitpid(pid, NULL, 0); 42 | 43 | printf("lower=%p upper=%p\n", lower, upper); 44 | assert((ptraddr_t)upper == (ptraddr_t)&lower[sizeof(lower)]); 45 | 46 | res = read(fds[1], lower, sizeof(lower) + sizeof(upper)); 47 | assert(res != 0); 48 | if (res > 0) { 49 | printf("Read 0x%x OK; lower[0]=0x%x upper[0]=0x%x\n", 50 | res, lower[0], upper[0]); 51 | } else if (res < 0) { 52 | printf("Bad read (%s); lower[0]=0x%x upper[0]=0x%x\n", 53 | strerror(errno), lower[0], upper[0]); 54 | } 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-heap/buffer-overflow-heap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int 12 | main(int argc, char **argv) 13 | { 14 | char *b1, *b2; 15 | 16 | assert(argc == 2); 17 | char *p; 18 | size_t sz = (size_t)strtoull(argv[1], &p, 0); 19 | 20 | assert(sz > 0); 21 | assert(sz <= 0x8000); 22 | 23 | b1 = malloc(sz); 24 | assert(b1 != NULL); 25 | 26 | b2 = malloc(sz); 27 | assert(b2 != NULL); 28 | 29 | #ifdef __CHERI_PURE_CAPABILITY__ 30 | printf("sz=%zx, CRRL(sz)=%zx\n", sz, 31 | __builtin_cheri_round_representable_length(sz)); 32 | printf("b1=%#p b2=%#p diff=%tx\n", b1, b2, b2 - b1); 33 | #else 34 | printf("b1=%p b2=%p diff=%tx\n", b1, b2, b2 - b1); 35 | #endif 36 | 37 | /* 38 | * The default CheriBSD malloc uses "size classes" for allocations. 39 | * Check that we've landed "nearby". 40 | */ 41 | assert((ptraddr_t)(b1 + sz) <= (ptraddr_t)b2); 42 | assert((ptraddr_t)(b1 + sz + sz/2) > (ptraddr_t)b2); 43 | 44 | memset(b2, 'B', sz); 45 | 46 | printf("Overflowing by 1\n"); 47 | memset(b1, 'A', sz + 1); 48 | 49 | printf("b2 begins: %.4s\n", b2); 50 | 51 | 52 | /* And now let's definitely make trouble */ 53 | const size_t oversz = b2 - b1 + 2 - sz; 54 | printf("Overflowing by %zx\n", oversz); 55 | memset(b1 + sz, 'A', oversz); 56 | 57 | printf("b2 begins: %.4s\n", b2); 58 | } 59 | -------------------------------------------------------------------------------- /src/missions/buffer-overflow-control-flow/btpalloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 Jessica Clarke 4 | */ 5 | #include "btpalloc.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #ifdef __CHERI_PURE_CAPABILITY__ 13 | #include 14 | #endif 15 | 16 | static void *btpmem; 17 | static size_t btpsize; 18 | 19 | static void 20 | btpinit(void) 21 | { 22 | btpsize = 0x100000; 23 | btpmem = mmap(NULL, btpsize, PROT_READ | PROT_WRITE, 24 | MAP_PRIVATE | MAP_ANON, -1, 0); 25 | assert(btpmem != MAP_FAILED); 26 | } 27 | 28 | void * 29 | btpmalloc(size_t size) 30 | { 31 | void *alloc; 32 | size_t allocsize; 33 | 34 | if (btpmem == NULL) 35 | btpinit(); 36 | 37 | alloc = btpmem; 38 | /* RISC-V ABIs require 16-byte alignment */ 39 | allocsize = __builtin_align_up(size, 16); 40 | 41 | #if defined(__CHERI_PURE_CAPABILITY__) && !defined(CHERI_NO_ALIGN_PAD) 42 | allocsize = cheri_representable_length(allocsize); 43 | alloc = __builtin_align_up(alloc, 44 | ~cheri_representable_alignment_mask(allocsize) + 1); 45 | allocsize += (char *)alloc - (char *)btpmem; 46 | #endif 47 | 48 | if (allocsize > btpsize) 49 | return (NULL); 50 | 51 | btpmem = (char *)btpmem + allocsize; 52 | btpsize -= allocsize; 53 | #ifdef __CHERI_PURE_CAPABILITY__ 54 | alloc = cheri_bounds_set(alloc, size); 55 | #endif 56 | return (alloc); 57 | } 58 | 59 | void 60 | btpfree(void *ptr) 61 | { 62 | (void)ptr; 63 | } 64 | -------------------------------------------------------------------------------- /src/exercises/cheri-tags/README.md: -------------------------------------------------------------------------------- 1 | # Demonstrate CHERI Tag Protection 2 | 3 | This exercise demonstrates CHERI's *capability provenance tags*, in particular 4 | by showing that capabilities and their constituent bytes are subtly different 5 | things! 6 | 7 | 1. Compile `corrupt-pointer.c` for the baseline architecture to the binary 8 | `corrupt-pointer-baseline` and for the CHERI-aware architecture to 9 | `corrupt-pointer-cheri`. 10 | 11 | 2. Run both programs and observe the output. 12 | 13 | 3. Use `gdb` to inspect the `SIGPROT` thrown to the CHERI program. 14 | 15 | Print out the pseudoregister `$_siginfo`. `si_signo` `34` is `SIGPROT`, a 16 | new signal introduced for conveying CHERI traps to user programs. The 17 | `si_code` values for `SIGPROT` signals are defined as the various 18 | `PROT_CHERI_*` values in `` (which can be found in 19 | `/usr/include` in a CheriBSD system). 20 | 21 | 4. Examine the disassembly of the construction of `q`, 22 | ``` 23 | uint8_t *q = (uint8_t*)(((uintptr_t)p.ptr) & ~0xFF) + 5; 24 | ``` 25 | and the byte-wise mutation of `p.ptr` to construct `r`, 26 | ``` 27 | p.bytes[0] = 5; 28 | uint8_t *r = p.ptr; 29 | ``` 30 | in both baseline and CHERI-enabled programs. 31 | 32 | What stands out? 33 | 34 | 5. Given that `q` and `r` appear to have identical byte representation in 35 | memory, why does the CHERI version crash when dereferencing `r`? 36 | 37 | ## Source 38 | 39 | **corrupt-pointer.c** 40 | ```C 41 | {{#include corrupt-pointer.c}} 42 | ``` 43 | 44 | ## Courseware 45 | 46 | This exercise has [presentation materials](./cheri-tags.pptx) available. 47 | -------------------------------------------------------------------------------- /src/exercises/README.md: -------------------------------------------------------------------------------- 1 | # Skills Development Exercises 2 | 3 | For a researcher to contribute effectively to CHERI-RISC-V evaluation, 4 | they will need a baseline skill-set that includes significant existing 5 | experience with: 6 | 7 | - C/C++-language memory-safety vulnerabilities 8 | - Binary reverse engineering for at least one ISA, such as x86, MIPS, ARMv7, or ARMv8 9 | - Low-level aspects of program representation, such as ELF, GOTs, and PLTs, as well as mechanisms such as dynamic linking and system-call handling 10 | - Attack techniques against program control flow and underlying data structures including ROP and JOP 11 | 12 | However, we expect that researchers may need to build specific 13 | additional skills with respect to the specifics of RISC-V machine code, 14 | assembly, language, and linkage, as well as knowledge about the CHERI 15 | C/C++ protection model and CHERI-RISC-V extensions to RISC-V. These 16 | exercises are intended to assist in these latter two areas, faulting 17 | in missing knowledge and experience while building on existing skills 18 | gained on other architectures (such as x86-64 and ARMv8). Participants 19 | successfully completing these exercises will be able to: 20 | - Compile, run, disassemble, and debug RISC-V compiled C/C++ programs 21 | - Compile, run, disassemble, and debug CHERI-RISC-V compiled C/C++ programs 22 | - Use specific debugging tools such as GDB and llvm-objdump with RISC-V and CHERI-RISC-V programs 23 | - Understand some of the implications of CHERI protections for specific aspects of C/C++ and process execution 24 | 25 | Each exercise includes: 26 | - Sample source code and build instructions 27 | - A short document describing what the program does and the objectives 28 | - Where there are exercise questions, sample answers 29 | -------------------------------------------------------------------------------- /src/introduction/ccc.md: -------------------------------------------------------------------------------- 1 | # Helper script 2 | 3 | Because the command line required to compile exercises is quite unwieldy, we've created a wrapper script to help out, shown below. If you've checked out this repository it's present in `tools/ccc`. The usage is: 4 | ``` 5 | ccc [...] 6 | 7 | Supported architectures: 8 | aarch64 - conventional AArch64 9 | morello-hybrid - AArch64 Morello supporting CHERI 10 | morello-purecap - AArch64 Morello pure-capability 11 | riscv64 - conventional RISC-V 64-bit 12 | riscv64-hybrid - RISC-V 64-bit supporting CHERI 13 | riscv64-purecap - RISC-V 64-bit pure-capability 14 | ``` 15 | and it can be used in place of your compiler. 16 | 17 | For the exercises in this book you will use the `riscv64` and `riscv64-purecap` architectures. The `riscv64-hybrid` architecture instantiates appropriately annotated pointers as capabilities leaving the rest as conventional integer addresses, but is not used here. 18 | 19 | If you have built a compiler and sysroot using `cheribuild` in the default location (`~/cheri`) then it should work out of the box. If you've configured a different location you can set the `CHERIBUILD_SDK` environment variable to point to to the location of your SDK. Alternatively, you can set the `CLANG` and `SYSROOT` variables to point to the respective locations. 20 | ```sh 21 | {{#include ../../tools/ccc}} 22 | ``` 23 | 24 | If you were provided a docker image along with these instructions (e.g. as part of a training exercise or bug-bounty), it should be configured such that `ccc` works without setting environment variables. 25 | 26 | Although not used by these exercises, the tool will instead function as a C++ compiler if invoked via the name `cc++`, and a `tools/cc++` symlink exists to facilitate this. 27 | -------------------------------------------------------------------------------- /LICENSES/preferred/BSD-2-Clause: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: BSD-2-Clause 2 | SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html 3 | Usage-Guide: 4 | To use the BSD 2-clause "Simplified" License put the following SPDX 5 | tag/value pair into a comment according to the placement guidelines in 6 | the licensing rules documentation: 7 | SPDX-License-Identifier: BSD-2-Clause 8 | License-Text: 9 | 10 | Copyright (c) . All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 26 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/perm-vmem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef __CHERI_PURE_CAPABILITY__ 13 | #include 14 | #define PRINTF_PTR "#p" 15 | #else 16 | #define PRINTF_PTR "p" 17 | #endif 18 | 19 | int 20 | main(void) 21 | { 22 | char *m, *p; 23 | int res; 24 | 25 | /* Get a page from the kernel and give it back */ 26 | p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANON, 27 | -1, 0); 28 | assert(p != MAP_FAILED); 29 | printf("Directly mapped page at p=%" PRINTF_PTR "\n", p); 30 | #ifdef __CHERI_PURE_CAPABILITY__ 31 | printf(" p.perms=0x%lx\n", __builtin_cheri_perms_get(p)); 32 | #endif 33 | res = madvise(p, 4096, MADV_FREE); 34 | assert(res == 0); 35 | 36 | p = mmap(p, 4096, PROT_READ|PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 37 | -1, 0); 38 | assert(p != MAP_FAILED); 39 | 40 | res = munmap(p, 4096); 41 | assert(res == 0); 42 | 43 | /* Get a pointer to a whole page of the heap*/ 44 | m = malloc(8192); 45 | p = __builtin_align_up(m, 4096); 46 | printf("Punching hole in the heap at p=%" PRINTF_PTR "\n", p); 47 | #ifdef __CHERI_PURE_CAPABILITY__ 48 | printf(" p.perms=0x%lx\n", __builtin_cheri_perms_get(p)); 49 | #endif 50 | 51 | char *q = mmap(p, 4096, PROT_READ|PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, 52 | -1, 0); 53 | assert(q != MAP_FAILED); 54 | 55 | if (madvise(p, 4096, MADV_FREE) != 0) { 56 | printf("madvise failed: %s\n", strerror(errno)); 57 | } 58 | 59 | if (munmap(p, 4096) != 0) { 60 | printf("munmap failed: %s\n", strerror(errno)); 61 | } 62 | 63 | printf("Done\n"); 64 | } 65 | -------------------------------------------------------------------------------- /src/missions/buffer-overflow-control-flow/README.md: -------------------------------------------------------------------------------- 1 | # Exploiting a buffer overflow to manipulate control flow 2 | 3 | The objective of this mission is to demonstrate arbitrary code execution 4 | through a control-flow attack, despite CHERI protections. You will attack three 5 | different versions of the program: 6 | 7 | 1. A baseline RISC-V compilation, to establish that the vulnerability is 8 | exploitable without any CHERI protections. 9 | 10 | 2. A baseline CHERI-RISC-V compilation, offering strong spacial safety between 11 | heap allocations, including accounting for imprecision in the bounds of large 12 | capabilities. 13 | 14 | 3. A weakened CHERI-RISC-V compilation, reflecting what would occur if a memory 15 | allocator failed to pad allocations to account for capability bounds 16 | imprecision. 17 | 18 | The success condition for an exploit, given attacker-provided input overflowing 19 | a buffer, is to modify control flow in the program such that the `success` 20 | function is executed. 21 | 22 | 1. Compile `buffer-overflow.c` and `btpalloc.c` together with a RISC-V target 23 | and exploit the binary to execute the `success` function. 24 | 25 | **buffer-overflow.c** 26 | ```C 27 | {{#include buffer-overflow.c}} 28 | ``` 29 | 2. Recompile with a CHERI-RISC-V target, attempt to exploit the binary and, if 30 | it cannot be exploited, explain why. 31 | 3. Recompile with a CHERI-RISC-V target but this time adding 32 | `-DCHERI_NO_ALIGN_PAD`, attempt to exploit the binary and, if it cannot be 33 | exploited, explain why. 34 | 35 | **btpalloc.c** 36 | ```C 37 | {{#include btpalloc.c}} 38 | ``` 39 | 40 | ## Support code 41 | 42 | **btpalloc.h** 43 | ```C 44 | {{#include btpalloc.h}} 45 | ``` 46 | 47 | **main-asserts.inc** 48 | ```C 49 | {{#include main-asserts.inc}} 50 | ``` 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 2 | 3 | Copyright (c) 2020 Jessica Clarke 4 | Copyright (c) 2020, 2022 Robert N. M. Watson 5 | Copyright (c) 2020 SRI International 6 | Copyright (c) 2022 Microsoft Corporation 7 | 8 | This software was developed by SRI International and the University of 9 | Cambridge Computer Laboratory (Department of Computer Science and 10 | Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the 11 | DARPA SSITH research programme. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions 15 | are met: 16 | 1. Redistributions of source code must retain the above copyright 17 | notice, this list of conditions and the following disclaimer. 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /src/missions/kernel-FreeBSD-SA-18-13.nfs/README.md: -------------------------------------------------------------------------------- 1 | # Exploit vulnerability FreeBSD-SA-18:13.nfs: out-of-bounds access 2 | 3 | This mission depends on the pure-capability CheriBSD FETT kernel included in FETT CHERI-RISC-V Release 2 (kernel spatial memory safety). 4 | 5 | The objective of this mission is to demonstrate arbitrary code execution in a pure-capability kernel. 6 | This must be achieved via a reintroduced past FreeBSD security vulnerability, [FreeBSD-SA-18:13.nfs](https://www.freebsd.org/security/advisories/FreeBSD-SA-18:13.nfs.asc). 7 | We have reintroduced this via change [015fdfd5a71c299c6288e1d789735ef6d3b46329](https://github.com/CTSRD-CHERI/cheribsd/commit/015fdfd5a71c299c6288e1d789735ef6d3b46329) in the pure-capability kernel branch of the CheriBSD repository. 8 | In this vulnerability, an out-of-bounds access is performed during received NFS packet processing, which is exploitable on a vanilla non-CHERI system to gain kernel privilege. 9 | More information on the NFSv4 packet format may be found in [RFC7530](https://tools.ietf.org/html/rfc7530). 10 | 11 | Successful completion of this mission requires demonstrating that the kernel function `flag_captured(9)` has executed with the integer argument `0xfe77c0de` using the use of an NFS packet exploiting this vulnerability. 12 | Use of privileged kernel manipulation mechanisms, such as reconfiguration of the boot-time environment, use of the kernel debugger, kernel module loading, and access to `/dev/mem`, is considered out-of-scope in this mission. 13 | If `flag_captured(9)` is called, the sysctl `security.flags_captured` counter will be incremented. 14 | This corresponds to a partially successful exploit. 15 | If the function is called with the designated argument, the sysctl `security.flags_captured_key` counter will be incremented. 16 | This corresponds to a fully successful exploit. 17 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-global/README.md: -------------------------------------------------------------------------------- 1 | # Exercise an inter-global-object buffer overflow 2 | 3 | This exercise demonstrates an inter-object buffer overflow on baseline and 4 | CHERI-enabled architectures, and asks you to characterize and fix the bug 5 | detected by CHERI bounds enforcement. It also asks you to use GDB for 6 | debugging purposes. 7 | 8 | This example uses two *global* objects (in `.data`) to demonstrate an overflow. 9 | It is worth pondering how the bounds for pointers to globals come to be set! 10 | 11 | 1. Compile `buffer-overflow-global.c` for the baseline architecture to 12 | the binary `buffer-overflow-global-baseline` and for the CHERI-aware 13 | architecture to `buffer-overflow-global-cheri`. 14 | 15 | **For this exercise, add `-G0` to your 16 | compiler flags** (this ensures `c` is not placed in the small data section 17 | away from `buffer`). 18 | 19 | 2. Run both programs and observe the output. 20 | 21 | 3. Using GDB on the core dump (or run the CHERI program under `gdb`): 22 | Why has the CHERI program failed? 23 | 24 | 4. Modify `buffer-overflow-global.c` to increase the buffer size from 128 bytes 25 | to 1Mbyte + 1 byte. 26 | 27 | 5. Recompile and re-run `buffer-overflow-global-cheri`. Why does it no longer 28 | crash, even though the buffer overflow exists in the source code? Is 29 | the adjacent field still corrupted (i.e., has spatial safety been 30 | violated between allocations)? 31 | 32 | 6. Modify `buffer-overflow-global.c` to restore the original buffer size of 128 33 | bytes, and fix the bug by correcting accesses to the allocated array. 34 | 35 | 7. Recompile and run `buffer-overflow-global-cheri` to demonstrate that the 36 | program is now able to continue. 37 | 38 | ## Source Files 39 | 40 | **buffer-overflow-global.c** 41 | ```C 42 | {{#include buffer-overflow-global.c}} 43 | ``` 44 | 45 | ### Support code 46 | 47 | **main-asserts.inc** 48 | ```C 49 | {{#include main-asserts.inc}} 50 | ``` 51 | -------------------------------------------------------------------------------- /src/introduction/background.md: -------------------------------------------------------------------------------- 1 | # Background reading 2 | 3 | To perform these exercises most effectively, we recommend first building a 4 | working knowledge of CHERI. The most critical references will be the 5 | *Introduction to CHERI* and *CHERI C/C++ Programming Guide*, but there is a 6 | broad variety of other reference material available regarding CHERI: 7 | 8 | - [An Introduction to CHERI](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-941.pdf) - An overview of the CHERI architecture, security model, and programming models. 9 | - [CHERI C/C++ Programming Guide](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf) - This use of CHERI capabilities to represent C/C++ pointers requires modest changes to the way C and C++ are used. This document describes those changes. 10 | - [CheriABI: Enforcing Valid Pointer Provenance and Minimizing Pointer Privilege in the POSIX C Run-time Environment](https://www.cl.cam.ac.uk/research/security/ctsrd/pdfs/201904-asplos-cheriabi.pdf) - This paper describes the CheriABI pure-capability process environment these exercises are expected to use. An extended [technical report](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-932.pdf) is also available. 11 | - [Cornucopia: Temporal Safety for CHERI Heaps](https://www.cl.cam.ac.uk/research/security/ctsrd/pdfs/2020oakland-cornucopia.pdf) - The temporal-safety exercises require the use of Cornucopia's quarantine and revocation infrastructure. 12 | - [Capability Hardware Enhanced RISC Instructions: 13 | CHERI Instruction-Set Architecture (Version 8)](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-951.pdf) - Instruction reference and design discussion. 14 | - [Complete spatial safety for C and C++ using CHERI capabilities](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-949.pdf) - This PhD dissertation provides an extensive overview of the CHERI-MIPS linking model (also relevant to the current CHERI-RISC-V model), an implementation of opportunistic subobject bounds, and general C/C++ compatibility issues. 15 | -------------------------------------------------------------------------------- /src/missions/kernel-FreeBSD-SA-09-06.ktimer/README.md: -------------------------------------------------------------------------------- 1 | # Exploit vulnerability FreeBSD-SA-09:06.ktimer: kernel buffer overflow 2 | 3 | This mission depends on the pure-capability CheriBSD FETT kernel included in FETT CHERI-RISC-V Release 2 (kernel spatial memory safety). 4 | 5 | The objective of this mission is to demonstrate arbitrary code execution in a pure-capability kernel. 6 | This must be achieved via a reintroduced past FreeBSD security vulnerability, [FreeBSD-SA-09:06.ktimer](https://www.freebsd.org/security/advisories/FreeBSD-SA-09:06.ktimer.asc). 7 | We have reintroduced this via change [69bb6a5e55fc94dd7338e22492971edbf55f8393](https://github.com/CTSRD-CHERI/cheribsd/commit/69bb6a5e55fc94dd7338e22492971edbf55f8393) in the pure-capability kernel branch of the CheriBSD repository. 8 | In this vulnerability, an integer system-call argument is not properly bounds checked, allowing an out-of-bounds access that on a vanilla non-CHERI system is exploitable to gain kernel privilege. 9 | More information on the timer system-call interface can be found in the [`timer_settime(2)`](https://www.freebsd.org/cgi/man.cgi?query=timer_settime&sektion=2) and related man pages. 10 | 11 | Successful completion of this mission requires demonstrating that the kernel function `flag_captured(9)` has executed with the integer argument `0xfe77c0de` using one of the `ktimer(2)` system calls triggered as an unprivileged (non-root) user. 12 | Use of privileged kernel manipulation mechanisms, such as reconfiguration of the boot-time environment, use of the kernel debugger, kernel module loading, and access to `/dev/mem`, is considered out-of-scope in this mission. 13 | If `flag_captured(9)` is called, the sysctl `security.kernel_flags_captured` counter will be incremented. 14 | This corresponds to a partially successful exploit. 15 | If the function is called with the designated argument, the sysctl `security.kernel_flags_captured_key` counter will be incremented. 16 | This corresponds to a fully successful exploit. 17 | -------------------------------------------------------------------------------- /src/exercises/pointer-revocation/temporal-control.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2020 Microsoft, Inc. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /* Ensure we're being run on a temporal-safety-aware system */ 11 | #ifdef __CHERI_PURE_CAPABILITY__ 12 | #include 13 | __attribute__((used)) 14 | static void *check_cheri_revoke = cheri_revoke; 15 | 16 | extern void malloc_revoke(void); 17 | __attribute__((used)) 18 | static void *check_malloc_revoke = malloc_revoke; 19 | #endif 20 | 21 | static void 22 | fn1(uintptr_t arg) 23 | { 24 | fprintf(stderr, " First function: %#p\n", (void *)arg); 25 | } 26 | 27 | static void 28 | fn2(uintptr_t arg) 29 | { 30 | fprintf(stderr, " Second function: %#p\n", (void *)arg); 31 | } 32 | 33 | struct obj { 34 | char buf[32]; 35 | /* 36 | * The following are marked volatile to ensure the compiler doesn't 37 | * constant propagate fn (making aliasing not work) and to ensure 38 | * neither stores to them are optimised away entirely as dead due 39 | * to calling free. 40 | */ 41 | void (* volatile fn)(uintptr_t); 42 | volatile uintptr_t arg; 43 | }; 44 | 45 | int 46 | main(void) 47 | { 48 | struct obj * volatile obj1 = calloc(1, sizeof(*obj1)); 49 | 50 | fprintf(stderr, "Installing function pointer in obj1 at %#p\n", obj1); 51 | obj1->fn = fn1; 52 | obj1->arg = (uintptr_t)obj1; 53 | 54 | free(obj1); 55 | 56 | fprintf(stderr, "Demonstrating use after free:\n"); 57 | obj1->fn(obj1->arg); 58 | 59 | #ifdef CAPREVOKE 60 | /* Force recycling the free queue now, but with a revocation pass */ 61 | malloc_revoke(); 62 | #endif 63 | 64 | struct obj * volatile obj2 = malloc(sizeof(*obj2)); 65 | #ifdef CAPREVOKE 66 | assert(obj1 == obj2); 67 | #endif 68 | 69 | fprintf(stderr, "Assigning function pointer through obj2 at %#p\n", 70 | obj2); 71 | obj2->fn = fn2; 72 | 73 | fprintf(stderr, "Calling function pointer through obj1 (now %#p):\n", 74 | obj1); 75 | obj1->fn(obj1->arg); 76 | 77 | return (0); 78 | } 79 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-heap/README.md: -------------------------------------------------------------------------------- 1 | # Exercise heap overflows 2 | 3 | This exercise demonstrates inter-object *heap* buffer overflows on baseline and 4 | CHERI-enabled architectures, and asks you to characterize and fix the bug 5 | detected by CHERI bounds enforcement. 6 | 7 | 1. Compile `buffer-overflow-heap.c` for the baseline architecture to the binary 8 | `buffer-overflow-heap-baseline` and for the CHERI-aware architecture to 9 | `buffer-overflow-heap-cheri`. 10 | 11 | 2. Run both versions, passing `0x20` as the (sole) command line argument. 12 | Observe that the CHERI version crashes with "In-address space security 13 | exception". 14 | 15 | 3. Run the CHERI version, again with `0x20`, under `gdb` and examine the crash 16 | in more detail. Where must the bounds on the capability implementing `b1` 17 | have come from? 18 | 19 | 4. Run both programs again, but now with `0x1001` as the argument. 20 | Draw a picture of the portion of the heap containing (the end of) `b1` and 21 | (the start of) `b2`. There are, in some sense, *two* different ends of `b1` 22 | in the baseline program and *three* in the CHERI program! What are they and 23 | how do they arise? 24 | 25 | 5. While this program does crash on CHERI, again of a bounds violation, this 26 | happens slightly later than might be expected looking at the program's 27 | source. In particular, this program actually commits *two* out of bounds 28 | stores using the `b1` capability. Examine the output carefully and describe, 29 | merely in terms of the mechanism, without venturing philosophical, why the 30 | first does not trigger a trap. 31 | 32 | 6. Now consider the bigger picture. Since CHERI uses *compressed* capability 33 | bounds, what additional steps must be taken, and by whom, to ensure spatial 34 | safety of a C program? 35 | 36 | ## Source Files 37 | 38 | **buffer-overflow-heap.c** 39 | ```C 40 | {{#include buffer-overflow-heap.c}} 41 | ``` 42 | 43 | ## Courseware 44 | 45 | This exercise has [presentation materials](./buffer-overflow-heap.pptx) 46 | available. 47 | -------------------------------------------------------------------------------- /src/missions/uninitialized-stack-frame-control-flow/README.md: -------------------------------------------------------------------------------- 1 | # Exploiting an uninitialized stack frame to manipulate control flow 2 | 3 | The objective of this mission is to demonstrate arbitrary code 4 | execution through the use of uninitialized variables on the stack, 5 | despite CHERI protections. You will attack three different versions 6 | of the program: 7 | 8 | 1. A baseline RISC-V compilation, to establish that the vulnerability is 9 | exploitable without any CHERI protections. 10 | 11 | 2. A hardened CHERI-RISC-V compilation with stack clearing, which 12 | should be non-exploitable. 13 | 14 | 3. A baseline CHERI-RISC-V compilation with no stack clearing, which 15 | should be non-exploitable due to pointer tagging. 16 | 17 | The success condition for an exploit, given attacker-provided input 18 | overriding an on-stack buffer, is to modify control flow in the program 19 | such that the `success` function is executed. 20 | 21 | ### Program overview 22 | 23 | Cookie monster is always hungry for more cookies. You can sate the 24 | monster's hunger by providing cookies as standard input. Cookies are 25 | provided as a pair of hexadecimal characters (case is ignored). Each 26 | cookie is stored at successive bytes in an on-stack character array. 27 | The character array aliases an uninitialized function pointer used in 28 | a subsequent function. A minus character ('-') can be used to skip 29 | over a character in the array without providing a new cookie. An 30 | equals sign ('=') can be used to skip over the number of characters in 31 | a pointer without providing any new cookies. Whitespace is ignored in 32 | the input line. Input is terminated either by a newline or end of 33 | file (EOF). 34 | 35 | ### Building and running 36 | 37 | The hardened CHERI-RISC-V version with stack clearing is built by 38 | adding `-ftrivial-auto-var-init=zero 39 | -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang` 40 | to the compiler command line. 41 | 42 | ### Source code 43 | 44 | ***stack-mission.c*** 45 | ```C 46 | {{#include stack-mission.c}} 47 | ``` 48 | -------------------------------------------------------------------------------- /LICENSES/preferred/BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 2 | Usage-Guide: 3 | To use the BSD 2-clause "Simplified" License put the following SPDX 4 | tag/value pair into a comment according to the placement guidelines in 5 | the licensing rules documentation: 6 | SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 7 | License-Text: 8 | 9 | SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 10 | 11 | Copyright (c) 2020 (holder) 12 | 13 | This software was developed by SRI International and the University of 14 | Cambridge Computer Laboratory (Department of Computer Science and 15 | Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the 16 | DARPA SSITH research programme. 17 | 18 | Redistribution and use in source and binary forms, with or without 19 | modification, are permitted provided that the following conditions 20 | are met: 21 | 1. Redistributions of source code must retain the above copyright 22 | notice, this list of conditions and the following disclaimer. 23 | 2. Redistributions in binary form must reproduce the above copyright 24 | notice, this list of conditions and the following disclaimer in the 25 | documentation and/or other materials provided with the distribution. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 28 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 31 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 | SUCH DAMAGE. 38 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/README.md: -------------------------------------------------------------------------------- 1 | # Adapt a C Program to CHERI C 2 | 3 | This excercise presents an example C program that includes capability-related 4 | issues that might appear as bugs in software initially developed for non-CHERI 5 | architectures. The example C program is `cat(1)` from CheriBSD (and hence 6 | FreeBSD) modified to introduce the issues that we want to investigate. 7 | 8 | 9 | 1. Read Sections 4.2, 4.2.1, 4.2.3 from the 10 | [CHERI C/C++ Programming Guide](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf). 11 | In Section 4.2.1, read only information on the following C-language types: 12 | `long`, `uintptr_t` and `char *,...` (pointer types). 13 | 14 | 2. Compile `cat/cat.c` and `cat/methods.c` for the baseline architecture to the 15 | binary `cat-baseline` and for the CHERI-aware architecture to `cat-cheri`. 16 | The compiler should print some warnings when compiling `cat-cheri`. Save the 17 | output to examine the warnings later. 18 | 19 | 3. Run both versions to print contents of an arbitrary file (e.g., 20 | `/etc/hostid`), once without any additional flags and once with the `-n` 21 | flag. 22 | 23 | 4. Run the CHERI version, again without any additional flags, under `gdb` and 24 | examine the crash in more detail. Use `gdb` and not `gdb-run.sh` to set 25 | appropriate breakpoints before your program is started. 26 | 27 | 5. Get back to the compiler warnings and try to solve a bug that triggered the 28 | crash. 29 | 30 | 6. Run the CHERI version, again with the `-n` flag, under `gdb` and examine the 31 | crash in more detail. You can use `gdb-run.sh` this time. 32 | 33 | 7. Get back to the compiler warnings and try to solve a bug that triggered the 34 | crash. 35 | 36 | 8. You just analysed two bugs in `cat`. How are they different and why they 37 | trigger crashes in different ways? 38 | 39 | ## Source Files 40 | 41 | **cat/cat.c** 42 | ```C 43 | {{#include cat/cat.c}} 44 | ``` 45 | 46 | **cat/cat.h** 47 | ```C 48 | {{#include cat/cat.h}} 49 | ``` 50 | 51 | **cat/methods.c** 52 | ```C 53 | {{#include cat/methods.c}} 54 | ``` 55 | 56 | ## Courseware 57 | 58 | This exercise has [presentation materials](./adapt-c.pptx) available. 59 | -------------------------------------------------------------------------------- /src/exercises/pointer-revocation/answers.md: -------------------------------------------------------------------------------- 1 | # Answers 2 | ## Indirect control flow through aliased heap objects 3 | 4 | 2. Expected output (addresses may vary): 5 | ``` 6 | Installing function pointer in obj1 at 0x40809000 7 | Demonstrating use after free: 8 | First function: 0x40809000 9 | Assigning function pointer through obj2 at 0x40809000 10 | Calling function pointer through obj1 (now 0x40809000): 11 | Second function: 0x40809000 12 | ``` 13 | 4. Expected output (addresses may vary): 14 | ``` 15 | Installing function pointer in obj1 at v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 16 | Demonstrating use after free: 17 | First function: v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 18 | Assigning function pointer through obj2 at v:1 s:0 p:0006817d b:00000000412000c0 l:0000000000000040 o:0 t:-1 19 | Calling function pointer through obj1 (now v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1): 20 | First function: v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 21 | ``` 22 | 6. Expected output (addresses may vary): 23 | ``` 24 | Installing function pointer in obj1 at v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 25 | Demonstrating use after free: 26 | First function: v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 27 | Assigning function pointer through obj2 at v:1 s:0 p:0006817d b:0000000041200040 l:0000000000000040 o:0 t:-1 28 | Calling function pointer through obj1 (now v:1 s:0 p:00000000 b:0000000041200040 l:0000000000000040 o:0 t:-1): 29 | In-address space security exception (core dumped) 30 | ``` 31 | 7. The process is attempting a load through a capability with valid tag but no 32 | permissions, as can be readily seen in `gdb`: 33 | ``` 34 | Program received signal SIGPROT, CHERI protection violation 35 | Capability permission fault caused by register ca2. 36 | 0x0000000000102140 in main () 37 | 38 | (gdb) x/i 0x0000000000102140 39 | => 0x102140 : clc ca2,32(ca2) 40 | 41 | (gdb) p $ca2 42 | $1 = (void *) 0x41200040 [,0x41200040-0x41200080] 43 | ``` 44 | `malloc_revoke()` must have replaced the `obj1` capability, which previously 45 | had permissions for loading and storing both data and capabilities to the 46 | memory backing `obj1`, with this permissionless form. 47 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-global/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Exercise an inter-global-object buffer overflow 2 | 3 | 2. Expected output: 4 | ``` 5 | # ./buffer-overflow-global-baseline 6 | c = c 7 | c = b 8 | # ./buffer-overflow-global-cheri 9 | c = c 10 | In-address space security exception (core dumped) 11 | ``` 12 | 13 | 3. Example session: 14 | ``` 15 | Program received signal SIGPROT, CHERI protection violation 16 | Capability bounds fault caused by register ca4. 17 | fill_buf (buf=0x104160 [rwRW,0x104160-0x1041e0] 'b' , "c", len=128) at buffer-overflow-global.c:15 18 | 15 buf[i] = 'b'; 19 | (gdb) info reg ca4 20 | ca4 0xf17d00000479816400000000001041e0 0x1041e0 [rwRW,0x104160-0x1041e0] 21 | (gdb) x/i $pcc 22 | => 0x101d2c : csb a3,0(ca4) 23 | ``` 24 | The array has been incremented beyond the end of the allocation as out 25 | of bounds store has been attempted (`Capability bounds fault`). 26 | 27 | 5. Expected output: 28 | ``` 29 | # ./buffer-overflow-global-cheri 30 | c = c 31 | c = c 32 | ``` 33 | To see why this occurs, examine the bounds of the buffer in `fill_buf`. 34 | ``` 35 | (gdb) b fill_buf 36 | Breakpoint 1 at 0x101d26: file buffer-overflow-global.c, line 15. 37 | (gdb) r 38 | Starting program: /root/buffer-overflow-global-cheri 39 | c = c 40 | 41 | Breakpoint 1, fill_buf (buf=0x105000 [rwRW,0x105000-0x205800] "", len=1048577) at buffer-overflow-global.c:15 42 | 15 buf[i] = 'b'; 43 | ``` 44 | This indicates that buffer has been allocated (1024 * 1026) bytes. This 45 | is due to the padding required to ensure that the bounds of `buffer` 46 | don't overlap with other allocations. As a result, there as an area beyond 47 | the end of the C-language object that is nonetheless in bounds. 48 | 49 | 6. Solution: 50 | ```diff 51 | --- buffer-overflow-global.c 52 | +++ buffer-overflow-global.c 53 | @@ -6,7 +6,7 @@ char c; 54 | void 55 | fill_buf(char *buf, size_t len) 56 | { 57 | - for (size_t i = 0; i <= len; i++) 58 | + for (size_t i = 0; i < len; i++) 59 | buf[i] = 'b'; 60 | } 61 | ``` 62 | 63 | 7. Expected output: 64 | ``` 65 | # ./buffer-overflow-global-cheri 66 | c = c 67 | c = c 68 | ``` 69 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/cat/cat.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1989, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Kevin Fall. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef _CAT_H_ 36 | 37 | /* 38 | * Memory strategy threshold, in pages: if physmem is larger than this, 39 | * use a large buffer. 40 | */ 41 | #define PHYSPAGES_THRESHOLD (32 * 1024) 42 | 43 | /* Maximum buffer size in bytes - do not allow it to grow larger than this. */ 44 | #define BUFSIZE_MAX (2 * 1024 * 1024) 45 | 46 | /* 47 | * Small (default) buffer size in bytes. It's inefficient for this to be 48 | * smaller than MAXPHYS. 49 | */ 50 | #define BUFSIZE_SMALL (MAXPHYS) 51 | 52 | #define SUPPORTED_FLAGS "belnstuv" 53 | 54 | void do_cat(long file, int verbose); 55 | 56 | #endif /* !_CAT_H_ */ 57 | -------------------------------------------------------------------------------- /src/missions/uninitialized-stack-frame-control-flow/stack-mission.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2020 SRI International 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void 15 | success(void) 16 | { 17 | fprintf(stderr, "Exploit successful, yum!\n"); 18 | exit(42); 19 | } 20 | 21 | void 22 | no_cookies(void) 23 | { 24 | fprintf(stderr, "No cookies??\n"); 25 | exit(1); 26 | } 27 | 28 | #pragma weak init_pointer 29 | void 30 | init_pointer(void *p) 31 | { 32 | } 33 | 34 | static void __attribute__((noinline)) 35 | init_cookie_pointer(void) 36 | { 37 | void *pointers[12]; 38 | void (* volatile cookie_fn)(void); 39 | 40 | for (size_t i = 0; i < sizeof(pointers) / sizeof(pointers[0]); i++) 41 | init_pointer(&pointers[i]); 42 | cookie_fn = no_cookies; 43 | } 44 | 45 | static void __attribute__((noinline)) 46 | get_cookies(void) 47 | { 48 | alignas(void *) char cookies[sizeof(void *) * 32]; 49 | char *cookiep; 50 | int ch, cookie; 51 | 52 | printf("Cookie monster is hungry, provide some cookies!\n"); 53 | printf("'=' skips the next %zu bytes\n", sizeof(void *)); 54 | printf("'-' skips to the next character\n"); 55 | printf("XX as two hex digits stores a single cookie\n"); 56 | printf("> "); 57 | 58 | cookiep = cookies; 59 | for (;;) { 60 | ch = getchar(); 61 | 62 | if (ch == '\n' || ch == EOF) 63 | break; 64 | 65 | if (isspace(ch)) 66 | continue; 67 | 68 | if (ch == '-') { 69 | cookiep++; 70 | continue; 71 | } 72 | 73 | if (ch == '=') { 74 | cookiep += sizeof(void *); 75 | continue; 76 | } 77 | 78 | if (isxdigit(ch)) { 79 | cookie = digittoint(ch) << 4; 80 | ch = getchar(); 81 | if (ch == EOF) 82 | errx(1, "Half-eaten cookie, yuck!"); 83 | if (!isxdigit(ch)) 84 | errx(1, "Malformed cookie"); 85 | cookie |= digittoint(ch); 86 | *cookiep++ = cookie; 87 | continue; 88 | } 89 | 90 | errx(1, "Malformed cookie"); 91 | } 92 | } 93 | 94 | static void __attribute__((noinline)) 95 | eat_cookies(void) 96 | { 97 | void *pointers[12]; 98 | void (* volatile cookie_fn)(void); 99 | 100 | for (size_t i = 0; i < sizeof(pointers) / sizeof(pointers[0]); i++) 101 | init_pointer(&pointers[i]); 102 | cookie_fn(); 103 | } 104 | 105 | int 106 | main(void) 107 | { 108 | init_cookie_pointer(); 109 | get_cookies(); 110 | eat_cookies(); 111 | return (0); 112 | } 113 | -------------------------------------------------------------------------------- /src/exercises/debug-and-disassemble/README.md: -------------------------------------------------------------------------------- 1 | # Disassemble and debug RISC-V and CHERI-RISC-V programs 2 | 3 | This exercise steps you through disassembling and debugging 4 | RISC-V and CHERI-RISC-V programs. It draws attention to differences in 5 | program structure and code generation, particularly relating to control 6 | flow, between the two compilation targets. 7 | 8 | First, use `llvm-objdump` on the host (which you can find at 9 | `~/cheri/output/sdk/bin/llvm-objdump`, unless you have altered `cheribuild`'s 10 | default paths) to disassemble and explore the two binaries from the previous 11 | exercise: 12 | 13 | 1. Using `llvm-objdump -dS`, disassemble the `print-pointer-riscv` and 14 | `print-pointer-cheri` binaries. 15 | 2. What jump instruction is used to call `printf()` in `print-pointer-riscv`? 16 | Where does the target address for that jump originate? 17 | 3. What jump instruction is used to call `printf()` in `print-pointer-cheri`? 18 | Where does the target capability for that jump originate? 19 | (Hint, you may find it helpful to add the `-s` flag to your 20 | `llvm-objdump` command to see all sections.) 21 | 22 | Next use GDB to explore binary execution for RISC-V: 23 | 24 | 4. Run `print-pointer-riscv` under GDB, setting a breakpoint at the start 25 | of `printf()`. 26 | *Note:* GDB can't find the run-time linker of binaries of the 27 | non-default ABI on its own so you need to invoke 28 | `set program-interpreter /libexec/ld-elf64.so.1` 29 | before running the program. 30 | 31 | 5. Run the program and at the breakpoint, print out the value of the 32 | string pointer argument. 33 | 6. Use `info proc mappings` in GDB to print out the layout of the 34 | process address space. 35 | 7. Print out the program counter (`info reg pc`). 36 | What memory mapping is it derived from? 37 | 38 | And for CHERI-RISC-V: 39 | 40 | 8. Run `print-pointer-cheri` under GDB, setting a breakpoint at the start 41 | of `printf()`. 42 | 9. Print out the value of the string pointer argument. 43 | 10. Use `info proc mappings` (in GDB) to print out the layout of the 44 | process address space. 45 | 11. Print out the program counter (`info reg pcc`). 46 | What memory mapping is it derived from? 47 | Where do its bounds appear to originate from? 48 | 12. Print out the register file using `info registers`. 49 | What mappings do the capabilities in the register file point to? 50 | Notice that some capabilities are labeled with `(sentry)` (or `(sealed)` in 51 | the case of older versions of GDB which do not distinguish sentries from 52 | other sealed capabilities). 53 | Sentry capabilities are sealed (cannot be modified or used to load or 54 | store), but can be used as a jump target (where they are unsealed and 55 | installed in `pcc`). 56 | What implications does this have for attackers? 57 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/subobject-list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2022 Microsoft Corporation 4 | * 5 | * This exercise investigates a circular doubly-linked list with sentinels. 6 | */ 7 | #include 8 | 9 | /* 10 | * A list element is an intrusive structure (subobject) with a pointer to the 11 | * next list element and a pointer to the previous node's next pointer. In the 12 | * case of an empty list, ile_prevnp points to ile_next. 13 | */ 14 | struct ilist_elem { 15 | struct ilist_elem **ile_prevnp; 16 | struct ilist_elem *ile_next; 17 | }; 18 | 19 | static void 20 | ilist_init_sentinel(struct ilist_elem *s) { 21 | s->ile_next = s; 22 | s->ile_prevnp = &s->ile_next; 23 | } 24 | 25 | static void 26 | ilist_insert_after(struct ilist_elem *p, struct ilist_elem *n) { 27 | n->ile_next = p->ile_next; 28 | p->ile_next = n; 29 | n->ile_next->ile_prevnp = &n->ile_next; 30 | n->ile_prevnp = &p->ile_next; 31 | } 32 | 33 | static void 34 | ilist_remove(struct ilist_elem *e) { 35 | e->ile_next->ile_prevnp = e->ile_prevnp; 36 | *(e->ile_prevnp) = e->ile_next; 37 | } 38 | 39 | #define ILIST_FOREACH(h, c) \ 40 | for(c = (h)->ile_next; c != (h); c = c->ile_next) 41 | 42 | #ifdef USE_CDEFS_CONTAINEROF 43 | #define ILIST_CONTAINER(elem, type, field) \ 44 | (((elem) == NULL) ? ((type *)NULL) : __containerof((elem), type, field)) 45 | #else 46 | #define ILIST_CONTAINER(elem, type, field) \ 47 | (((elem) == NULL) ? ((type *)NULL) : \ 48 | __DEQUALIFY(type*, (const volatile char *)(elem) - \ 49 | __offsetof(type, field))) 50 | #endif 51 | 52 | struct obj { 53 | int val; 54 | struct ilist_elem ilist __subobject_use_container_bounds; 55 | }; 56 | 57 | struct ilist_elem l; /* Sentinel element serves as list head */ 58 | struct obj obj1 = {1, {}}; 59 | struct obj obj2 = {2, {}}; 60 | struct obj obj3 = {3, {}}; 61 | 62 | int 63 | main() { 64 | struct ilist_elem *cursor; 65 | 66 | ilist_init_sentinel(&l); 67 | ilist_insert_after(&l, &obj2.ilist); 68 | ilist_insert_after(&obj2.ilist, &obj3.ilist); 69 | ilist_insert_after(&l, &obj1.ilist); 70 | ilist_remove(&obj2.ilist); 71 | 72 | printf("Traversing list=%#p first=%#p lastnp=%#p\n", 73 | &l, l.ile_next, l.ile_prevnp); 74 | ILIST_FOREACH(&l, cursor) { 75 | struct obj *co = ILIST_CONTAINER(cursor, struct obj, ilist); 76 | printf(" Ilist cursor=%#p\n next=%#p\n prevnp=%#p\n", 77 | cursor, cursor->ile_next, cursor->ile_prevnp); 78 | printf(" val field at %#p\n", 79 | /* 80 | * This ugly bit of syntax is unfortunate, but avoids 81 | * a subobject-bounds-induced trap that isn't the first 82 | * one you should think about. I'm sorry. Just pretend 83 | * this says "&co->val" and, for extra credit, later, 84 | * explain why it isn't spelled like that. 85 | */ 86 | ((char *)co) + __offsetof(struct obj, val)); 87 | } 88 | 89 | printf("Traversing list again, accessing superobject field...\n"); 90 | ILIST_FOREACH(&l, cursor) { 91 | struct obj *co = ILIST_CONTAINER(cursor, struct obj, ilist); 92 | printf(" Ilist cursor=%#p value=%d (at %#p)\n", cursor, 93 | co->val, &co->val); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Adversarial CHERI Exercises and Missions](cover/README.md) 4 | - [Introduction](introduction/README.md) 5 | - [Background reading](introduction/background.md) 6 | - [Cross compilation and execution](introduction/cross-compilation-execution.md) 7 | - [Helper script](introduction/ccc.md) 8 | - [Skills Development Exercises](exercises/README.md) 9 | - [Compile and run RISC-V and CHERI-RISC-V programs](exercises/compile-and-run/README.md) 10 | - [Answers](exercises/compile-and-run/answers.md) 11 | - [Disassemble and debug RISC-V and CHERI-RISC-V programs](exercises/debug-and-disassemble/README.md) 12 | - [Answers](exercises/debug-and-disassemble/answers.md) 13 | - [Demonstrate CHERI Tag Protection](exercises/cheri-tags/README.md) 14 | - [Answers](exercises/cheri-tags/answers.md) 15 | - [Exercise an inter-object stack buffer overflow](exercises/buffer-overflow-stack/README.md) 16 | - [Answers](exercises/buffer-overflow-stack/answers.md) 17 | - [Exercise an inter-object global buffer overflow](exercises/buffer-overflow-global/README.md) 18 | - [Answers](exercises/buffer-overflow-global/answers.md) 19 | - [Explore subobject bounds](exercises/subobject-bounds/README.md) 20 | - [Answers](exercises/subobject-bounds/answers.md) 21 | - [Corrupt a control-flow pointer using a subobject buffer overflow](exercises/control-flow-pointer/README.md) 22 | - [Answers](exercises/control-flow-pointer/answers.md) 23 | - [Exercise heap overflows](exercises/buffer-overflow-heap/README.md) 24 | - [Answers](exercises/buffer-overflow-heap/answers.md) 25 | - [Exercise integer-pointer type confusion bug](exercises/type-confusion/README.md) 26 | - [Answers](exercises/type-confusion/answers.md) 27 | - [Demonstrate pointer injection](exercises/pointer-injection/README.md) 28 | - [Answers](exercises/pointer-injection/answers.md) 29 | - [Adapt a C Program to CHERI C](exercises/adapt-c/README.md) 30 | - [Answers](exercises/adapt-c/answers.md) 31 | - [CheriABI Showcase](exercises/cheriabi/README.md) 32 | - [Answers](exercises/cheriabi/answers.md) 33 | - [Extending Heap Allocators for CHERI](exercises/cheri-allocator/README.md) 34 | - [Answers](exercises/cheri-allocator/answers.md) 35 | - [Demonstrate pointer revocation](exercises/pointer-revocation/README.md) 36 | - [Answers](exercises/pointer-revocation/answers.md) 37 | - [Focused Adversarial Missions](missions/README.md) 38 | - [Exploiting a buffer overflow to manipulate control flow](missions/buffer-overflow-control-flow/README.md) 39 | - [Exploiting an uninitialized stack frame to manipulate control flow](missions/uninitialized-stack-frame-control-flow/README.md) 40 | - [Exploiting heap use-after-free to manipulate control flow](missions/use-after-free-control-flow/README.md) 41 | - [Exploiting kernel system-call vulnerability to manipulate control flow](missions/kernel-FreeBSD-SA-09-06.ktimer/README.md) 42 | - [Exploiting kernel NFS-server vulnerability to manipulate control flow](missions/kernel-FreeBSD-SA-18-13.nfs/README.md) 43 | - [Appendix](appendix/README.md) 44 | -------------------------------------------------------------------------------- /src/introduction/README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This set of exercises and adversarial missions is intended to: 4 | 5 | - Build a baseline skillset with RISC-V and CHERI-RISC-V, as well as awareness 6 | of some of the dynamics of CHERI-enabled software, through skills development 7 | exercises. 8 | - Develop adversarial experience with CHERI-RISC-V performing basic 9 | investigation around gradations of CHERI feature deployment through focused 10 | adversarial missions. 11 | 12 | These activities supplement existing experience 13 | with reverse engineering and exploitation on conventional architectures 14 | and software stacks. 15 | 16 | ## Platform 17 | 18 | These exercises are designed to be run on the CheriBSD operating system in its 19 | pure-capability CheriABI process environment. 20 | They can be run on various instantiations of CHERI-RISC-V, including on QEMU 21 | and on FPGA implementations. 22 | QEMU-CHERI is a convenient instruction-set-level emulator, and is usually the 23 | best starting point for most users (even those intending to eventually run on 24 | hardware). 25 | You can use our [cheribuild](https://github.com/CTSRD-CHERI/cheribuild) tool 26 | to build the CHERI-RISC-V SDK, CheriBSD, and QEMU on macOS, FreeBSD, and Linux. 27 | 28 | ## Skills development exercises 29 | 30 | **Skills development exercises** are intended to take 1-2 hours each, 31 | and ask you to build and perform minor modifications to simple 32 | RISC-V and CHERI-RISC-V C/C++ programs. These exercises 33 | facilitate building skills such as compiling, executing, 34 | and debugging RISC-V and CHERI-RISC-V programs, as well as to build basic 35 | understanding of CHERI C/C++ properties. We highlight some key edge 36 | cases in CHERI, including the effects of bounds imprecision, subobject 37 | bounds, weaker temporal safety, and C type confusion. 38 | 39 | These exercises take for granted a strong existing understanding of: 40 | - The C/C++ languages 41 | - UNIX program compilation, execution, and debugging 42 | - RISC ISAs and binary structures/reverse engineering (e.g., on MIPS or ARMv8) 43 | 44 | ## Focused adversarial missions 45 | **Focused adversarial missions** are intended to take 46 | 1-3 days, and ask you to exploit, first on RISC-V, and 47 | then on CHERI-RISC-V, documented vulnerabilities in simple "potted" 48 | C/C++-language programs provided by the CHERI-RISC-V team. These missions 49 | engage you more specifically in RISC-V exploitation, and CHERI's 50 | security objectives and mechanisms. 51 | 52 | **These take for granted good existing experience with 53 | memory-safety-related attack techniques, such as buffer overflows, 54 | integer-pointer type confusion, Return-Oriented Programming (ROP), and 55 | Jump-Oriented Programming (JOP).** 56 | 57 | Successful exploitation of RISC-V variants depends only upon 58 | widely published understanding and techniques (e.g., buffer overflows 59 | combined with ROP). For those familiar with conventional low-level 60 | attack techniques, this will also act as a warm-up exercise on the 61 | baseline RISC-V architecture and expand experience with RISC-V reverse 62 | engineering and exploitation. 63 | 64 | The CHERI-RISC-V team has confirmed exploitability for the RISC-V binary 65 | in advance. We strongly recommend exploiting the RISC-V version of the code 66 | first, as a starting point for understanding potential CHERI-RISC-V 67 | exploitability. 68 | -------------------------------------------------------------------------------- /src/exercises/cheri-allocator/answers.md: -------------------------------------------------------------------------------- 1 | # Answers 2 | 3 | ## Introducing heap-allocator bounds 4 | 5 | 2. GDB will show a CHERI tag violation resulting from `memset()` overwriting 6 | the `a_next` field in the second allocation entry, which is tripped over by 7 | a later call to `alloc_allocate()`: 8 | 9 | ``` 10 | Starting program: /root/cheri-allocator 11 | Allocator initialised 12 | Allocating memory 13 | Allocation returned 0x104550 14 | Preparing to overflow 0x104550 15 | Overflowed allocation 0x104550 16 | Freeing allocation 0x104550 17 | Allocation 0x104550 freed 18 | Allocating memory 19 | Allocation returned 0x104550 20 | Allocating memory 21 | Allocation returned 0x1045e0 22 | Allocating memory 23 | 24 | Program received signal SIGPROT, CHERI protection violation. 25 | Capability tag fault caused by register ca0. 26 | alloc_allocate () at cheri-allocator.c:83 27 | 83 alloc_nextfree = alloc->a_next; 28 | (gdb) p alloc 29 | $1 = (struct alloc_storage *) 0x4141414141414141 [,0x4141402800000000-0x414142a000000000] (invalid,sealed) 30 | ``` 31 | 32 | 3. When compiling for CHERI C, use `cheri_bounds_set()` to set bounds on the 33 | returned pointer: 34 | 35 | ``` 36 | /* Return pointer to allocated memory. */ 37 | #ifdef __CHERI_PURE_CAPABILITY__ 38 | return (cheri_bounds_set(alloc->a_bytes, ALLOC_SIZE)); 39 | #else 40 | return (alloc->a_bytes); 41 | #endif 42 | ``` 43 | 44 | 4. With this change, the `memset()` call in `main()` triggers a bounds 45 | violation exception on overflow: 46 | 47 | ``` 48 | Starting program: /root/cheri-allocator 49 | Allocator initialised 50 | Allocating memory 51 | Allocation returned 0x104550 52 | Preparing to overflow 0x104550 53 | 54 | Program received signal SIGPROT, CHERI protection violation. 55 | Capability bounds fault caused by register ca3. 56 | memset (dst0=0x1045d0 [rwRW,0x104550-0x1045d0], c0=65, length=15) at /usr/home/john/work/cheri/git/cheribsd/lib/libc/string/memset.c:94 57 | 94 *dst++ = VAL; 58 | ``` 59 | 60 | ## Reaching allocator metadata 61 | 62 | 6. Following this change, `alloc_free()` crashes with a bounds violation, 63 | due to reaching outside the bounds of the passed memory allocation: 64 | 65 | ``` 66 | Starting program: /root/cheri-allocator 67 | Allocator initialised 68 | Allocating memory 69 | Allocation returned 0x104420 70 | Freeing allocation 0x104420 71 | 72 | Program received signal SIGPROT, CHERI protection violation. 73 | Capability bounds fault caused by register cfp. 74 | alloc_free (ptr=) at cheri-allocator.c:106 75 | 106 alloc->a_next = alloc_nextfree; 76 | (gdb) bt 77 | #0 alloc_free (ptr=) at cheri-allocator.c:106 78 | #1 main () at cheri-allocator.c:137 79 | (gdb) p alloc 80 | $1 = (struct alloc_storage *) 0x104410 [rwRW,0x104420-0x1044a0] 81 | ``` 82 | 83 | 7. We need to create a new capability, derived from `alloc_array` but with the 84 | address generated from pointer to the memory being freed. 85 | One way to do this is using the `cheri_address_get()` and 86 | `cheri_address_set()`, reading the address from one capability and setting 87 | it on the other: 88 | 89 | ``` 90 | #ifdef __CHERI_PURE_CAPABILITY__ 91 | /* 92 | * Generate a new pointer to the allocation that is derived from the 93 | * one passed by the consumer. 94 | */ 95 | ptr = cheri_address_set(alloc_array, cheri_address_get(ptr)); 96 | #endif 97 | ``` 98 | 99 | Note that this is not a complete solution to providing spatial safety here: 100 | software could still accidentally pass an out-of-bounds pointer. 101 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/README.md: -------------------------------------------------------------------------------- 1 | # Explore Subobject Bounds 2 | 3 | In the CheriABI run-time environment, bounds are typically associated with 4 | memory allocations rather than C types. 5 | For example, if a heap memory allocation is made for 1024 bytes, and the 6 | structure within it is 768 bytes, then the bounds associated with a pointer 7 | will be for the allocation size rather than the structure size. 8 | 9 | ## Subobject Overflows 10 | 11 | With subobject bounds, enforcement occurs on C-language objects within 12 | allocations. 13 | This exercise is similar to earlier buffer-overflow exercises, but is for such 14 | an intra-object overflow. In our example, we consider an array within 15 | another structure, overflowing onto an integer in the same allocation. 16 | 17 | 1. Compile `buffer-overflow-subobject.c` with a baseline target and binary 18 | name of `buffer-overflow-subobject-baseline`, and with a CHERI-enabled 19 | target and binary name of `buffer-overflow-subobject-cheri`. 20 | 21 | 2. As in the prior exercises, run the binaries. 22 | 23 | 3. Explore why the CHERI binary didn't fail. 24 | Run `buffer-overflow-subobject-cheri` under `gdb` and examine the bounds 25 | of the `buffer` argument to `fill_buf()`. 26 | To what do they correspond? 27 | 28 | 4. Recompile the `buffer-overflow-subobject-cheri` binary with the compiler 29 | flags `-Xclang -cheri-bounds=subobject-safe`. 30 | 31 | 5. Run the program to demonstrate that the buffer overflow is now caught. 32 | 33 | 6. Run the program under `gdb` and examine the bounds again. What has changed? 34 | 35 | ## Deliberately Using Larger Bounds 36 | 37 | Operations like `&object->field` that move from super-object to sub-object are 38 | very natural in C, and there is no similarly concise syntax for the reverse 39 | operation. Nevertheless, C programs occasionally do make use of `containerof` 40 | constructs to do exactly that: derive a pointer to the superobject given a 41 | pointer to a subobject within. 42 | 43 | A common example is *intrusive* linked lists, as found, for example, in the BSD 44 | ``. `subobject-list.c` is an extremely minimal example of such, 45 | which we will use to explore the behavior of CHERI C here. 46 | 47 | 1. Compile `subobject-list.c` for your CHERI-enabled target to 48 | `subobject-list-cheri` and run it. 49 | 50 | 2. What is the length (limit - base) for capabilities to... 51 | - the sentinel node (`&l`) 52 | - a next pointer (`ile_next`) to a non-sentinel element 53 | - a previous-next pointer (`ile_prevnp`) to a non-sentinel element 54 | 55 | 3. Recompile this program, now with `-Xclang -cheri-bounds=subobject-safe`, and 56 | run the result. What happens and why? 57 | 58 | 4. The CheriBSD system headers have been extended so that examples like this 59 | which use the `` definition of `__containerof` (or things built 60 | atop that) will trip static assertions. Try compiling again with `-Xclang 61 | -cheri-bounds=subobject-safe -DUSE_CDEFS_CONTAINEROF` and observe what the 62 | compiler tells you. 63 | 64 | 5. Make the suggested change, marking `struct ilist_elem` as `` and recompile 65 | once again (with the same flags as just above). Run the resulting program 66 | and observe its output. Which bounds have not been narrowed? Which have? 67 | Why is that OK? 68 | 69 | ## Source Files 70 | 71 | ### Subobject Overflows 72 | 73 | **buffer-overflow-subobject.c** 74 | ```C 75 | {{#include buffer-overflow-subobject.c}} 76 | ``` 77 | 78 | **asserts.inc** 79 | ```C 80 | {{#include asserts.inc}} 81 | ``` 82 | 83 | ### Deliberately Using Larger Bounds 84 | 85 | **subobject-list.c** 86 | ```C 87 | {{#include subobject-list.c}} 88 | ``` 89 | 90 | ## Courseware 91 | 92 | This exercise has [presentation materials](./subobject-bounds.pptx) available. 93 | -------------------------------------------------------------------------------- /src/exercises/pointer-revocation/README.md: -------------------------------------------------------------------------------- 1 | # Demonstrate pointer revocation 2 | ## Indirect control flow through aliased heap objects 3 | This exercise demonstrates CheriBSD's *pointer revocation* facility and its use 4 | by the system `malloc`. It asks you to contrast the same program, 5 | `temporal-control.c`, built and run in three slightly different environments. 6 | It must be run on a **heap-temporal-safety enabled** version of CheriBSD; at the 7 | time of writing, heap temporal safety remains an experimental feature not yet 8 | merged to mainline CheriBSD. 9 | 10 | 1. Compile `temporal-control.c` with a RISC-V target and a binary name of 11 | `temporal-control-riscv`. 12 | 13 | **temporal-control.c** 14 | ```C 15 | {{#include temporal-control.c}} 16 | ``` 17 | 2. Run the resulting program and observe that the system malloc has **reused** 18 | a location on the heap, such that `obj1` and `obj2` point to the same address. 19 | Moreover, the assignment of `fn2` into `obj2` causes the last printout to be 20 | from `fn2`, not `fn1`, even though the function pointer was fetched through 21 | `obj1` and `obj1->fn` was last set to `fn1`. 22 | 3. Recompile `temporal-control.c` with a CHERI-RISC-V target and binary name of 23 | `temporal-control-cheri`. 24 | 4. Run this program instead. Why does it no longer exhibit the behavior from 25 | step 2? Ponder the suitability of using just this approach for fixing temporal 26 | aliasing. 27 | 5. Recompile `temporal-control.c`, adding `-DCAPREVOKE` to the command line 28 | this time, with a CHERI-RISC-V target and a binary name of 29 | `temporal-control-cheri-revoke`. 30 | 6. Run this third program instead and note that it crashes, catching a 31 | `SIGPROT` between declaring its intent to call `obj1->fn` and declaring that it 32 | has made the call. Can you spot why it has crashed? 33 | 7. Rerun the third program under `gdb` and look at both the instruction 34 | triggering the `SIGPROT` and the register(s) involved. Why is the program 35 | crashing? What must have happened while the system was executing the 36 | mysterious `malloc_revoke()` function? 37 | 8. Modify `temporal-control.c` to try to induce aliasing by making many 38 | allocations: call `malloc` and `free` repeatedly until the new allocation 39 | compares equal to `obj1`. Ah ha, you've caught the allocator now! But wait, 40 | what is `obj1` in full (i.e., as a capability, not merely a virtual address)? 41 | You likely have to call `free` in the loop for this exercise to work; merely 42 | calling `malloc` may instead simply always return new addresses, even if the 43 | initial `obj1` has been `free`-d. 44 | 45 | ## More attacks through aliased heap objects 46 | The program is called `temporal-control.c` because it exhibits *temporal 47 | aliasing* of heap pointers and because the class of bugs it mimics involve 48 | transfers of control through function pointers held in heap objects. While 49 | CHERI protects against pointer *injection*, it cannot so easily defend against 50 | either: 51 | 52 | * *capability farming*: as in the example, a legitimately-held capability can 53 | be (caused to be) stored to a "new" heap object, altering an aliased view while 54 | preserving the set tag bit; or 55 | * *data-based* corruption through temporal aliasing. 56 | 57 | These windows open wider considering that, unlike this example, temporal 58 | aliasing often comes paired with *type-confusion*, so it may be possible to 59 | overlap an easily-controlled structure with an exploitable one. 60 | 61 | 1. Write a program like `temporal-control.c` in which changing a *data byte* 62 | within a temporally-aliased heap object suffices to cause the program to error. 63 | Perhaps the heap object is the state associated with a client session and 64 | contains a flag that indicates superuser status. 65 | 2. Demonstrate that this program fails as expected on RISC-V but that any 66 | attempt to induce aliasing is thwarted on CHERI-RISC-V with heap temporal 67 | safety: aliasing becomes possible only after revocation, ensuring that attempts 68 | to use the old session object fail-stop. 69 | -------------------------------------------------------------------------------- /.github/workflows/book.yml: -------------------------------------------------------------------------------- 1 | name: Build book 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - gh-pages 7 | release: 8 | types: 9 | - created 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - name: Install cargo 16 | uses: actions-rs/toolchain@v1 17 | with: 18 | toolchain: stable 19 | profile: minimal 20 | 21 | - name: Install mdbook 22 | run: curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.40/mdbook-v0.4.40-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=$HOME/.cargo/bin 23 | 24 | - name: Checkout source 25 | uses: actions/checkout@v4 26 | 27 | - name: Build 28 | run: mdbook build 29 | 30 | - name: Upload artifact 31 | uses: actions/upload-artifact@v4 32 | with: 33 | name: book 34 | path: book 35 | 36 | draft-release: 37 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 38 | needs: build 39 | runs-on: ubuntu-20.04 40 | steps: 41 | - name: Download artifact 42 | uses: actions/download-artifact@v4 43 | with: 44 | name: book 45 | path: cheri-exercises-snapshot 46 | 47 | - name: Create archive 48 | run: zip -r cheri-exercises-snapshot-${{ github.sha }}.zip cheri-exercises-snapshot 49 | 50 | - name: Get current date 51 | id: date 52 | run: echo "::set-output name=date::$(date -u +'%Y%m%d')" 53 | 54 | - name: Create release 55 | id: create_release 56 | uses: actions/create-release@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | tag_name: draft-${{ steps.date.outputs.date }}-${{ github.sha }} 61 | release_name: Draft release ${{ steps.date.outputs.date }} 62 | body: Latest snapshot (${{ github.sha }}) 63 | prerelease: true 64 | 65 | - name: Upload release asset 66 | id: upload-release-asset 67 | uses: actions/upload-release-asset@v1 68 | env: 69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 70 | with: 71 | upload_url: ${{ steps.create_release.outputs.upload_url }} 72 | asset_path: cheri-exercises-snapshot-${{ github.sha }}.zip 73 | asset_name: cheri-exercises-snapshot-${{ github.sha }}.zip 74 | asset_content_type: application/zip 75 | 76 | release: 77 | if: github.event_name == 'release' && github.event.action == 'created' 78 | needs: build 79 | runs-on: ubuntu-20.04 80 | steps: 81 | - name: Download artifact 82 | uses: actions/download-artifact@v4 83 | with: 84 | name: book 85 | path: cheri-exercises-${{ github.event.release.tag_name }} 86 | 87 | - name: Create archive 88 | run: zip -r cheri-exercises-${{ github.event.release.tag_name }}.zip cheri-exercises-${{ github.event.release.tag_name }} 89 | 90 | - name: Upload release asset 91 | id: upload-release-asset 92 | uses: actions/upload-release-asset@v1 93 | env: 94 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 95 | with: 96 | upload_url: ${{ github.event.release.upload_url }} 97 | asset_path: cheri-exercises-${{ github.event.release.tag_name }}.zip 98 | asset_name: cheri-exercises-${{ github.event.release.tag_name }}.zip 99 | asset_content_type: application/zip 100 | 101 | deploy: 102 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 103 | needs: build 104 | runs-on: ubuntu-20.04 105 | steps: 106 | - name: Download artifact 107 | uses: actions/download-artifact@v4 108 | with: 109 | name: book 110 | path: public 111 | 112 | - name: Deploy 113 | uses: peaceiris/actions-gh-pages@v4 114 | with: 115 | github_token: ${{ secrets.GITHUB_TOKEN }} 116 | -------------------------------------------------------------------------------- /src/exercises/cheri-tags/answers.md: -------------------------------------------------------------------------------- 1 | # Answers 2 | 3 | 2. Example output for the baseline program: 4 | ``` 5 | buf=0x8085ba59 &p=0x8085ba50 6 | p.ptr=0x8085bb68 (0x10f into buf) *p.ptr=0f 7 | q=0x8085bb00 (0xa7 into buf) 8 | *q=a7 9 | r=0x8085bb00 (0xa7) 10 | *r=a7 11 | ``` 12 | 13 | And for the CHERI-enabled program: 14 | ``` 15 | buf=0x3fffdffd71 [rwRW,0x3fffdffd71-0x3fffdfff70] &p=0x3fffdffd60 [rwRW,0x3fffdffd60-0x3fffdffd70] 16 | p.ptr=0x3fffdffe80 [rwRW,0x3fffdffd71-0x3fffdfff70] (0x10f into buf) *p.ptr=0f 17 | q=0x3fffdffe00 [rwRW,0x3fffdffd71-0x3fffdfff70] (0x8f into buf) 18 | *q=8f 19 | r=0x3fffdffe00 [rwRW,0x3fffdffd71-0x3fffdfff70] (invalid) (0x8f) 20 | In-address space security exception 21 | ``` 22 | 23 | 3. `gdb` should report something like 24 | ``` 25 | Program received signal SIGPROT, CHERI protection violation 26 | Capability tag fault caused by register cs1. 27 | main () at ./src/exercises/cheri-tags/corrupt-pointer.c:45 28 | 45 ./src/exercises/cheri-tags/corrupt-pointer.c: No such file or directory. 29 | 30 | Thread 1 (LWP 100057 of process 1231): 31 | #0 main () at ./src/exercises/cheri-tags/corrupt-pointer.c:45 32 | ``` 33 | 34 | We can ask `gdb` to print out the faulting instruction: 35 | ``` 36 | (gdb) x/i $pcc 37 | => 0x101d84 : clbu a1,0(cs1) 38 | ``` 39 | 40 | We can also ask `gdb` for more information about the signal we received: 41 | ``` 42 | (gdb) p $_siginfo 43 | $1 = {si_signo = 34, si_errno = 0, si_code = 2, si_pid = 0, si_uid = 0, 44 | si_status = 0, 45 | si_addr = 0x101d84 [rxR,0x100000-0x104120] (invalid), si_value = { 46 | sival_int = 0, sival_ptr = 0x0}, _reason = {_fault = {si_trapno = 28, 47 | si_capreg = 9}, _timer = {si_timerid = 28, si_overrun = 9}, _mesgq = { 48 | si_mqd = 28}, _poll = {si_band = 38654705692}, __spare__ = { 49 | __spare1__ = 38654705692, __spare2__ = {0, 0, 0, 0, 0, 0, 0}}}} 50 | ``` 51 | As said, `si_signo = 34` is `SIGPROT`, for which `si_code = 2` is 52 | `PROT_CHERI_TAG`, indicating a missing (clear) tag as an input to a 53 | capability instruction. `gdb` in fact does this decoding for you, in the 54 | reported line `Capability tag fault caused by register cs1`. It will be 55 | helpful to look for similar reports associated with `SIGPROT`s throughout 56 | this book. 57 | 58 | 4. Constructing `r` is very similar on the two targets, differing only by the 59 | use of integer- or capability-based memory instructions: 60 | 61 | | | Baseline | CHERI | 62 | | ----- | :------- | :---- | 63 | | Store | `sb zero, 0(sp)` | `csb zero, 32(csp)` | 64 | | Load | `ld s0, 0(sp)` | `clc cs1, 32(csp)` | 65 | 66 | The significant difference is in the construction of `q`. On the baseline 67 | architecture, it is a direct bitwise `and` of a pointer loaded from memory: 68 | 69 | ``` 70 | ld a0, 0(sp) 71 | andi s0, a0, -256 72 | ``` 73 | 74 | On CHERI, on the other hand, the program makes explicit use of capability 75 | manipulation instructions to... 76 | 77 | | Instruction | Action | 78 | | ----------- | ------ | 79 | | `clc ca0, 32(csp)` | Load the capability from memory | 80 | | `cgetaddr a1, ca0` | Extract its address field to a register | 81 | | `andi a1, a1, -256` | Perform the mask operation | 82 | | `csetaddr cs1, ca0, a1` | Update the address field | 83 | 84 | This longer instruction sequence serves to prove to the processor that the 85 | resulting capability (in `cs1`) was constructed using valid transformations. 86 | In particular, the `csetaddr` allows the processor to check that the 87 | combination of the old capability (in `ca0`) and the new address (in `a1`) 88 | remains *representable*. 89 | 90 | 5. While the in-memory, byte representation of `q` and `r` are identical, `r` 91 | has been manipulated as *bytes* rather than as a *capability* and so has had 92 | its tag zeroed. (Specifically, the `csb zero, 32(csp)` instruction cleared 93 | the tag associated with the 16-byte granule pointed to by `32(csp)`; the 94 | subsequent `clc` transferred this zero tag to `cs1`.) 95 | -------------------------------------------------------------------------------- /src/introduction/cross-compilation-execution.md: -------------------------------------------------------------------------------- 1 | # Cross compilation and execution 2 | 3 | ## Obtaining a compiler and sysroot 4 | 5 | If you already have a compiler and sysroot (e.g. you have a docker image with pre-compiled versions), you will need to know the path to `clang` and the path to your sysroot. You can then proceed to [**Compiler command line**](#compiler-command-line). 6 | 7 | ### Building a cross build environment with cheribuild 8 | 9 | First, clone the cheribuild repo: 10 | ``` 11 | git clone https://github.com/CTSRD-CHERI/cheribuild.git 12 | ``` 13 | The [README.md](https://github.com/CTSRD-CHERI/cheribuild/blob/master/README.md) file contains considerable information, but to get started, you'll need to bootstrap an LLVM compiler and a CheriBSD build and sysroot. The easiest path to doing this is: 14 | ``` 15 | cheribuild.py cheribsd-riscv64-purecap -d 16 | ``` 17 | This will churn away, prompting occasionally as it bootstraps assorted dependencies. On a fast machine this will take several hours. 18 | 19 | Upon completion, you will find a usable Clang compiler in `~/cheri/output/sdk/bin/clang` and a sysroot in `~/cheri/output/rootfs-riscv64-purecap` (unless you have altered `cheribuild`'s default paths). 20 | 21 | ## Compiler command line 22 | In this set of exercises we cross compile in two basic modes. 23 | Conventional RISC-V ABI and the CheriABI pure-capability ABI. 24 | 25 | ### Common elements 26 | All command lines will share some comment elements to target 64-bit RISC-V, select the linker, and indicate where to find the sysroot. 27 | 28 | Some conventions: 29 | - `$SYSROOT` is the path to your sysroot. 30 | - `$CLANG` is the path to your compiler. 31 | - All compiler commands begin with `$CLANG -target riscv64-unknown-freebsd --sysroot="$SYSROOT" -fuse-ld=lld -mno-relax` 32 | - As a rule, you will want to add `-g` to the command line to compile with debug symbols. 33 | - You will generally want to compile with `-O2` as the unoptimized assembly is verbose and hard to follow. 34 | - We strongly recommend you compile with warnings on including `-Wall` and `-Wcheri`. 35 | 36 | ### RISC-V 37 | Two additional arguments are required to specify the supported architectural features and ABI. For conventional RISC-V, those are: ` 38 | -march=rv64gc -mabi=lp64d`. 39 | Putting it all together: 40 | ``` 41 | $CLANG -g -O2 -target riscv64-unknown-freebsd --sysroot="$SYSROOT" -fuse-ld=lld -mno-relax -march=rv64gc -mabi=lp64d -Wall -Wcheri 42 | ``` 43 | ### CheriABI 44 | For CheriABI, the architecture and ABI flags are: 45 | `-march=rv64gcxcheri -mabi=l64pc128d`. 46 | Putting it all together: 47 | ``` 48 | $CLANG -g -O2 -target riscv64-unknown-freebsd --sysroot="$SYSROOT" -fuse-ld=lld -mno-relax -march=rv64gcxcheri -mabi=l64pc128d -Wall -Wcheri 49 | ``` 50 | 51 | ## Executing binaries 52 | CheriBSD supports running RISC-V and CHERI-RISC-V side-by-side on the same instance, so provided the instance has all features available for the exercise or mission in question, you should be able to complete it on a single CheriBSD instance. 53 | 54 | CheriBSD's `file(1)` has been extended to distinguish RISC-V binaries from CHERI-RISC-V (CheriABI) binaries. For example, on a CheriBSD instance: 55 | ``` 56 | # file riscv-binary 57 | riscv-binary: ELF 64-bit LSB shared object, UCB RISC-V, RVC, double-float ABI, version 1 (SYSV), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 13.0 (1300097), FreeBSD-style, with debug_info, not stripped 58 | # file cheri-binary 59 | cheri-binary: ELF 64-bit LSB shared object, UCB RISC-V, RVC, double-float ABI, capability mode, CheriABI, version 1 (SYSV), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 13.0 (1300097), FreeBSD-style, with debug_info, not stripped 60 | ``` 61 | 62 | CHERI-LLVM and the elfutils in CheriBSD also recognise the relevant ELF flags. For example, CHERI-LLVM on the host used for cross-compiling will report: 63 | ``` 64 | # llvm-readelf -h riscv-binary | grep Flags 65 | Flags: 0x5, RVC, double-float ABI 66 | # llvm-readelf -h cheri-binary | grep Flags 67 | Flags: 0x30005, RVC, double-float ABI, cheriabi, capability mode 68 | ``` 69 | and elfutils on a CheriBSD instance will report: 70 | ``` 71 | # readelf -h riscv-binary | grep Flags 72 | Flags: 0x5, double-float ABI, RVC 73 | # readelf -h cheri-binary | grep Flags 74 | Flags: 0x30005, double-float ABI, RVC, cheriabi, capmode 75 | ``` 76 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-heap/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Exercise heap overflows 2 | 3 | 2. Example output: 4 | ``` 5 | # ./buffer-overflow-heap-baseline 0x20 6 | b1=0x83e82000 b2=0x83e82020 diff=20 7 | Overflowing by 1 8 | b2 begins: ABBB 9 | Overflowing by 2 10 | b2 begins: AABB 11 | ``` 12 | ``` 13 | # ./buffer-overflow-heap-cheri 0x20 14 | sz=20, CRRL(sz)=20 15 | b1=0x407c7000 [rwRW,0x407c7000-0x407c7020] b2=0x407c7020 [rwRW,0x407c7020-0x407c7040] diff=20 16 | Overflowing by 1 17 | In-address space security exception 18 | ``` 19 | 20 | 3. Example session (abridged): 21 | ``` 22 | # gdb-run.sh ./buffer-overflow-heap-cheri 0x20 23 | 24 | Starting program: ./buffer-overflow-heap-cheri 0x20 25 | sz=20, CRRL(sz)=20 26 | b1=0x407c7000 [rwRW,0x407c7000-0x407c7020] b2=0x407c7020 [rwRW,0x407c7020-0x407c7040] diff=20 27 | Overflowing by 1 28 | 29 | Program received signal SIGPROT, CHERI protection violation 30 | Capability bounds fault caused by register ca4. 31 | memset (dst0=0x407c7000 [rwRW,0x407c7000-0x407c7020], c0=65, length=) at /cheri/source/mainline/cheribsd/lib/libc/string/memset.c:131 32 | 131 /cheri/source/mainline/cheribsd/lib/libc/string/memset.c: No such file or directory. 33 | 34 | Thread 1 (LWP 100057 of process 960): 35 | #0 memset (dst0=0x407c7000 [rwRW,0x407c7000-0x407c7020], c0=65, length=) at /cheri/source/mainline/cheribsd/lib/libc/string/memset.c:131 36 | #1 0x00000000001020d2 in main (argc=, argv=) at ./src/exercises/buffer-overflow-heap/buffer-overflow-heap.c:49 37 | (gdb) i r ca4 38 | ca4 0xd17d00000409b00400000000407c7020 0x407c7020 [rwRW,0x407c7000-0x407c7020] 39 | ``` 40 | 41 | The capability in `ca4` is, as expected, a reference to the first allocation 42 | (`b1`). The bounds on this capability must have been imposed *by malloc*. 43 | 44 | 4. Example output: 45 | ``` 46 | # /mnt/buffer-overflow-heap-baseline 0x1001 47 | b1=0x840ec000 b2=0x840ed400 diff=1400 48 | Overflowing by 1 49 | b2 begins: BBBB 50 | Overflowing by 401 51 | b2 begins: AABB 52 | ``` 53 | ``` 54 | # /mnt/buffer-overflow-heap-cheri 0x1001 55 | sz=1001, CRRL(sz)=1008 56 | b1=0x407c7000 [rwRW,0x407c7000-0x407c8008] b2=0x407c8400 [rwRW,0x407c8400-0x407c9408] diff=1400 57 | Overflowing by 1 58 | Overflowing by 401 59 | In-address space security exception 60 | ``` 61 | 62 | Using addresses from the CHERI run, we might draw something that highlighted 63 | these key addresses: 64 | 65 | | Address | Contents | 66 | | ------: | -------- | 67 | | ↑ `0x000...00` | | 68 | | `0x407c7000` | Start of `b1` | 69 | | `0x407c8001` | Last byte of `b1` allocation ("end" #1) | 70 | | `0x407c8002` | Start of CHERI representation padding | 71 | | `0x407c8007` | Last byte of CHERI representation padding ("end" #2) | 72 | | `0x407c8008` | Start of allocator size-class padding | 73 | | `0x407c83FF` | Last byte of allocator size-class padding ("end" #3) | 74 | | `0x407c8400` | `b2` | 75 | | ↓ `0xFFF...FF` | | 76 | 77 | 5. The first overflow, by 1 byte, is *within bounds* due to *architectural 78 | precision* and so, as far as the CPU is concerned, is not an overflow despite 79 | writing outside the logical bounds of the `b1` allocation. 80 | 81 | 6. In order to set bounds large enough to encapsulate large objects, CHERI's 82 | compressed capability representation may be able to express only larger 83 | bounds than the requested size. (More generally, the *base* and *limit*s of 84 | a capability have increased alignment requirements as they are moved further 85 | apart, that is, as the capability length increases. For the capabilities 86 | in this example, the bases have strong alignments, of at least 10 bits, due 87 | to the allocator's use of size classes.) 88 | 89 | If bounds were simply widened with no additional consideration, then pointers 90 | to different objects might come to authorize access to (parts of) each 91 | other's memory! In order to ensure that capabilities to distinct C objects 92 | do not alias like this, various system components must take CHERI capability 93 | compression into consideration: 94 | 95 | - The compiler, for on-stack allocations and [address-taken subobjects](../subobject-bounds). 96 | - The linker, for objects in static storage 97 | - The heap allocator(s), for objects in dynamic storage 98 | 99 | These concerns do not usually reach "application level" C. 100 | -------------------------------------------------------------------------------- /tools/ccc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # ccc - Cross compilation script 4 | set -e 5 | set -u 6 | 7 | name=$(basename "$0") 8 | 9 | VERBOSE=${VERBOSE:-0} 10 | QUIET=${QUIET:-0} 11 | 12 | usage() 13 | { 14 | cat < [...] 16 | 17 | Supported architectures: 18 | aarch64 - conventional AArch64 19 | morello-hybrid - AArch64 Morello supporting CHERI 20 | morello-purecap - AArch64 Morello pure-capability 21 | riscv64 - conventional RISC-V 64-bit 22 | riscv64-hybrid - RISC-V 64-bit supporting CHERI 23 | riscv64-purecap - RISC-V 64-bit pure-capability 24 | EOF 25 | exit 1 26 | } 27 | 28 | err() 29 | { 30 | ret=$1 31 | shift 32 | echo >&2 "$@" 33 | exit "$ret" 34 | } 35 | 36 | warn() 37 | { 38 | echo >&2 "$@" 39 | } 40 | 41 | debug() 42 | { 43 | if [ "$VERBOSE" -ne 0 ]; then 44 | echo >&2 "$@" 45 | fi 46 | } 47 | 48 | info() 49 | { 50 | if [ "$QUIET" -eq 0 ]; then 51 | echo >&2 "$@" 52 | fi 53 | } 54 | 55 | run() 56 | { 57 | debug # add space before normal multiline output 58 | info "Running:" "$@" 59 | "$@" 60 | } 61 | 62 | if [ $# -eq 0 ]; then 63 | usage 64 | fi 65 | 66 | arch=$1 67 | shift 68 | 69 | cheri_arch_basename=${arch%%-*} 70 | cheri_sdk_name=sdk 71 | case $arch in 72 | aarch64) 73 | cheri_arch_basename=morello 74 | cheri_sdk_name=morello-sdk 75 | arch_flags="-target aarch64-unknown-freebsd -march=morello+noa64c" 76 | ;; 77 | morello-hybrid) 78 | cheri_sdk_name=morello-sdk 79 | arch_flags="-target aarch64-unknown-freebsd -march=morello -Xclang -morello-vararg=new" 80 | ;; 81 | morello-purecap) 82 | cheri_sdk_name=morello-sdk 83 | arch_flags="-target aarch64-unknown-freebsd -march=morello -mabi=purecap -Xclang -morello-vararg=new" 84 | ;; 85 | riscv64) 86 | arch_flags="-target riscv64-unknown-freebsd -march=rv64gc -mabi=lp64d -mno-relax" 87 | ;; 88 | riscv64-hybrid) 89 | arch_flags="-target riscv64-unknown-freebsd -march=rv64gcxcheri -mabi=lp64d -mno-relax" 90 | ;; 91 | riscv64-purecap) 92 | arch_flags="-target riscv64-unknown-freebsd -march=rv64gcxcheri -mabi=l64pc128d -mno-relax" 93 | ;; 94 | *) 95 | err 1 "Unsupported architecture '$arch'" 96 | ;; 97 | esac 98 | 99 | # Find our SDK, using the first of these that expands only defined variables: 100 | # ${CHERIBUILD_SDK_${cheri_sdk_name}} (if that syntax worked) 101 | # ${CHERIBUILD_SDK} 102 | # ${CHERIBUILD_OUTPUT}/${cheri_sdk_name} 103 | # ${CHERIBUILD_SOURCE}/output/${cheri_sdk_name} 104 | # ~/cheri/output/${cheri_sdk_name} 105 | 106 | SDKDIR_SOURCE=${CHERIBUILD_SOURCE:-${HOME}/cheri} 107 | SDKDIR_OUTPUT=${CHERIBUILD_OUTPUT:-${SDKDIR_SOURCE}/output} 108 | SDKDIR_SDK=${CHERIBUILD_SDK:-${SDKDIR_OUTPUT}/${cheri_sdk_name}} 109 | SDKDIR=$(eval echo \${CHERIBUILD_SDK_"${cheri_arch_basename}":-}) 110 | SDKDIR=${SDKDIR:-${SDKDIR_SDK}} 111 | 112 | enverr() 113 | { 114 | echo >&2 $1 115 | echo "Perhaps set or adjust one of the following environment variables:" 116 | for v in SOURCE OUTPUT SDK; do 117 | echo " " CHERIBUILD_$v \(currently: \ 118 | $(eval echo \${CHERIBUILD_$v:-unset, tried \$SDKDIR_$v})\) 119 | done 120 | 121 | A="CHERIBUILD_SDK_${cheri_arch_basename}" 122 | echo " " "$A" \(currently: $(eval echo \${$A:-unset, tried \$SDKDIR})\) 123 | 124 | echo " " "$2" \(currently: $(eval echo \${$2:-unset, tried \$SDK_$2})\) 125 | 126 | err 1 "Please check your build environment" 127 | } 128 | 129 | SDK_CLANG=${CLANG:-${SDKDIR}/bin/clang} 130 | 131 | case $name in 132 | *clang|*cc) prog="${SDK_CLANG}" ;; 133 | *clang++|*c++) prog="${SDK_CLANG}++" ;; 134 | *) err 1 "Unsupported program name '$name'" ;; 135 | esac 136 | if [ ! -x "$prog" ]; then 137 | enverr "Target compiler '$prog' not found." "CLANG" 138 | fi 139 | debug "prog: $prog" 140 | 141 | SDK_SYSROOT=${SYSROOT:-${SDKDIR}/sysroot-${cheri_arch_basename}-purecap} 142 | if [ ! -d "$SDK_SYSROOT" ]; then 143 | enverr "Sysroot '$SDK_SYSROOT' does not exist." "SYSROOT" 144 | fi 145 | debug "sysroot: $SDK_SYSROOT" 146 | 147 | debug "arch_flags: $arch_flags" 148 | 149 | debug_flags="-g" 150 | debug "debug_flags: $debug_flags" 151 | 152 | opt_flags="-O2" 153 | debug "opt_flags: $opt_flags" 154 | 155 | sysroot_flags="--sysroot='$SDK_SYSROOT'" 156 | debug "sysroot_flags: $sysroot_flags" 157 | 158 | linker_flags="-fuse-ld=lld" 159 | debug "linker_flags: $linker_flags" 160 | 161 | diag_flags="-Wall -Wcheri" 162 | debug "diag_flags: $diag_flags" 163 | 164 | all_flags="$arch_flags $sysroot_flags $debug_flags $opt_flags $linker_flags $diag_flags" 165 | 166 | all_flags_rev= 167 | # shellcheck disable=SC2086 # intentional 168 | eval 'for flag in '$all_flags'; do 169 | all_flags_rev="'"'"'$flag'"'"'${all_flags_rev:+ $all_flags_rev}" 170 | done' 171 | 172 | # shellcheck disable=SC2086 # intentional 173 | eval 'for flag in '$all_flags_rev'; do 174 | set -- "$flag" "$@" 175 | done' 176 | 177 | run "$prog" "$@" 178 | -------------------------------------------------------------------------------- /src/exercises/cheri-allocator/README.md: -------------------------------------------------------------------------------- 1 | # Extending heap allocators for CHERI 2 | 3 | CHERI's architectural protection is driven by software -- the compiler, 4 | linker, OS kernel, run-time linker, run-time libraries, and so on all manage 5 | capabilities as part of their program execution. Heap allocators, which are 6 | integrally tied into our notions of spatial and temporal safety, are typically 7 | extended to use CHERI in five ways: 8 | 9 | 1. To implement spatial safety, bounds and permissions are set on returned 10 | pointers. 11 | *(In this exercise.)* 12 | 13 | 2. To prevent bounds overlap on larger allocations from arising due to 14 | imprecise bounds caused by capability compression, large allocations are 15 | aligned and padded more strongly. 16 | *(Not in this exercise.)* 17 | 18 | 3. If the allocator's `free()` implementation relies on reaching allocator 19 | metadata via its pointer argument (e.g., by looking immediately before or 20 | after to reach free-list pointers), then the implementation must be 21 | changed as access will otherwise be prevented by CHERI bounds and 22 | monotonicity. 23 | *(In this exercise.)* 24 | 25 | 4. To implement temporal safety, allocated memory is registered with a 26 | temporal-safety run-time library when allocated, to implement 27 | kernel-assisted revocation. 28 | On free, the memory is is held in quarantine until revocation has been 29 | performed. 30 | *(Not in this exercise.)* 31 | 32 | 5. To handle a further set of classes of misuse and pointer corruption, it is 33 | also important to perform validation of arguments to `free()`, such as by 34 | checking that the pointer is to the first byte of a valid allocation. 35 | *(Not in this exercise.)* 36 | 37 | This exercise asks you to extend a simplified memory allocator with CHERI 38 | focusing only on (1) and (3) above. 39 | It supports only small fixed-size allocations that will not require further 40 | alignment or padding, and we will not consider temporal safety in this 41 | exercise. 42 | 43 | The complete exercise is embodied in `cheri-allocator.c`, including the 44 | simplified allocator and also a `main()` routine that initializes and uses the 45 | allocator. 46 | `main()` allocates memory, and then overflows the allocation to corrupt 47 | internal allocator metadata, leading to a crash. 48 | Heap metadata corruption is a powerful exploitation tool; CHERI assists with 49 | mitigating it through pointer integrity features, but it is preferable to 50 | deterministically close vulnerabilities (e.g., via spatial safety). 51 | 52 | 1. Compile `cheri-allocator.c` with a CHERI-enabled target. 53 | Run the binary, which will crash. 54 | 55 | 2. Use GDB to demonstrate to yourself that the overflow has corrupted 56 | allocator metadata, leading to an eventual crash during a later call to 57 | `alloc_allocate()`. 58 | 59 | 3. Modify the allocator to use the `cheri_bounds_set()` API to set suitable 60 | bounds on the pointer returned by `alloc_allocate()`. 61 | Recompile `cheri-allocator.c` with a CHERI-enabled target. 62 | 63 | 4. Use GDB to demonstrate to yourself that the overflow operation now causes 64 | an immediate crash as a result of attempting to store out of bounds, rather 65 | than triggering a later crash due to heap metadata corruption. 66 | 67 | 5. Remove the overflow (performed with `memset()`) from the program. 68 | Recompile `cheri-allocator.c` with a CHERI-enabled target. 69 | 70 | 6. Use GDB to explore why the program now crashes in `alloc_free()`: How did 71 | adding bounds during allocation break later freeing of that memory? 72 | 73 | 7. Correct the bug through the use of the `cheri_address_get()` and 74 | `cheri_address_set()` APIs, which allow transferring an address from one 75 | capability (with one set of bounds) to another (with a different set of 76 | bounds). 77 | What capability should we be using to provide the new bounds? 78 | Recompile `cheri-allocator.c` with a CHERI-enabled target. 79 | 80 | 8. Demonstrate that the program now runs successfully to completion. 81 | 82 | The resulting allocator is now substantially safer with respect to spatial 83 | safety, preventing underflows and overflows from corrupting allocator metadata 84 | or the contents of other allocations. 85 | However, to continue hardening the allocator against various attacks, further 86 | work would be required, including better validating the argument of the 87 | `free()` function. 88 | This would ideally test that the pointer being freed points to memory managed 89 | by the allocator, that the pointer is in bounds, and that it points to the 90 | start of a current allocation. 91 | Further temporal safety also requires quarantining freed memory until all 92 | pointers to it have been revoked. 93 | 94 | ## Source Files 95 | 96 | **cheri-allocator.c** 97 | ```C 98 | {{#include cheri-allocator.c}} 99 | ``` 100 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/cat/cat.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1989, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Kevin Fall. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | */ 34 | 35 | #if 0 36 | #ifndef lint 37 | static char const copyright[] = 38 | "@(#) Copyright (c) 1989, 1993\n\ 39 | The Regents of the University of California. All rights reserved.\n"; 40 | #endif /* not lint */ 41 | #endif 42 | 43 | #ifndef lint 44 | #if 0 45 | static char sccsid[] = "@(#)cat.c 8.2 (Berkeley) 4/27/95"; 46 | #endif 47 | #endif /* not lint */ 48 | #include 49 | __FBSDID("$FreeBSD$"); 50 | 51 | #include 52 | #include 53 | #ifndef NO_UDOM_SUPPORT 54 | #include 55 | #include 56 | #include 57 | #endif 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | #include "cat.h" 72 | 73 | int bflag, eflag, lflag, nflag, sflag, tflag, vflag; 74 | int rval; 75 | const char *filename; 76 | 77 | static void usage(void) __dead2; 78 | static void scanfiles(char *argv[], int verbose); 79 | 80 | int 81 | main(int argc, char *argv[]) 82 | { 83 | int ch; 84 | struct flock stdout_lock; 85 | 86 | setlocale(LC_CTYPE, ""); 87 | 88 | while ((ch = getopt(argc, argv, SUPPORTED_FLAGS)) != -1) 89 | switch (ch) { 90 | case 'b': 91 | bflag = nflag = 1; /* -b implies -n */ 92 | break; 93 | case 'e': 94 | eflag = vflag = 1; /* -e implies -v */ 95 | break; 96 | case 'l': 97 | lflag = 1; 98 | break; 99 | case 'n': 100 | nflag = 1; 101 | break; 102 | case 's': 103 | sflag = 1; 104 | break; 105 | case 't': 106 | tflag = vflag = 1; /* -t implies -v */ 107 | break; 108 | case 'u': 109 | setbuf(stdout, NULL); 110 | break; 111 | case 'v': 112 | vflag = 1; 113 | break; 114 | default: 115 | usage(); 116 | } 117 | argv += optind; 118 | 119 | if (lflag) { 120 | stdout_lock.l_len = 0; 121 | stdout_lock.l_start = 0; 122 | stdout_lock.l_type = F_WRLCK; 123 | stdout_lock.l_whence = SEEK_SET; 124 | if (fcntl(STDOUT_FILENO, F_SETLKW, &stdout_lock) == -1) 125 | err(EXIT_FAILURE, "stdout"); 126 | } 127 | 128 | if (bflag || eflag || nflag || sflag || tflag || vflag) 129 | scanfiles(argv, 1); 130 | else 131 | scanfiles(argv, 0); 132 | if (fclose(stdout)) 133 | err(1, "stdout"); 134 | exit(rval); 135 | /* NOTREACHED */ 136 | } 137 | 138 | static void 139 | usage(void) 140 | { 141 | 142 | fprintf(stderr, "usage: cat [-" SUPPORTED_FLAGS "] [file ...]\n"); 143 | exit(1); 144 | /* NOTREACHED */ 145 | } 146 | 147 | static void 148 | scanfiles(char *argv[], int verbose) 149 | { 150 | int fd, i; 151 | char *path; 152 | FILE *fp; 153 | 154 | i = 0; 155 | fd = -1; 156 | while ((path = argv[i]) != NULL || i == 0) { 157 | if (path == NULL || strcmp(path, "-") == 0) { 158 | filename = "stdin"; 159 | fd = STDIN_FILENO; 160 | } else { 161 | filename = path; 162 | fd = open(path, O_RDONLY); 163 | } 164 | if (fd < 0) { 165 | warn("%s", path); 166 | rval = 1; 167 | } else if (verbose) { 168 | if (fd == STDIN_FILENO) 169 | do_cat((long)stdin, verbose); 170 | else { 171 | fp = fdopen(fd, "r"); 172 | do_cat((long)fp, verbose); 173 | fclose(fp); 174 | } 175 | } else { 176 | do_cat(fd, verbose); 177 | if (fd != STDIN_FILENO) 178 | close(fd); 179 | } 180 | if (path == NULL) 181 | break; 182 | ++i; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/exercises/cheri-allocator/cheri-allocator.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016 3 | * Copyright (c) 2022 Robert N. M. Watson 4 | */ 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __CHERI_PURE_CAPABILITY__ 16 | #include 17 | #endif 18 | 19 | /* 20 | * Implement a very simple allocator for a fixed-size data type, with inline 21 | * metadata. Calls to alloc_allocate() return a pointer to a fixed-size byte 22 | * array. Calls to alloc_free() return it to the allocator for reuse. 23 | * 24 | * The implementation is simplistic, and is designed to support an exercise 25 | * relating to: (a) bounds setting; and (b) monotonicty and rederivation. 26 | * Each allocation is described by 'struct allocation', which consists of 27 | * free-list pointers and an array of bytes that make up the allocation 28 | * itself. Those allocations are stored as a sequential array in a global 29 | * variable initialised by BSS: 30 | * 31 | * /--------- index 0 ----------\ /--------- index 1 ----------\ /--... 32 | * 33 | * +--------+-----------------...-+--------+-----------------...-+---... 34 | * | a_next | a_bytes[ALLOC_SIZE] | a_next | a_bytes[ALLOC_SIZE] | 35 | * +--------+-----------------...-+--------+-----------------...-+---... 36 | * 37 | * ^ ^ 38 | * \_________________________/ \_________________________/ 39 | * If unallocated, pointer If unallocated, pointer 40 | * to next free allocation. to next free allocation. 41 | * 42 | * Allocation storage is sized below the threshold requiring extra alignment 43 | * or padding to account for capability bounds compression. 44 | */ 45 | #define ALLOC_SIZE 128 /* Allocation data size. */ 46 | struct alloc_storage { 47 | struct alloc_storage *a_next; /* Free list. */ 48 | uint8_t a_bytes[ALLOC_SIZE]; /* Allocated memory. */ 49 | }; 50 | 51 | #define ALLOC_MAX 16 /* Availaable allocations. */ 52 | struct alloc_storage alloc_array[ALLOC_MAX]; /* Underlying storage. */ 53 | struct alloc_storage *alloc_nextfree; /* Next available memory. */ 54 | 55 | /* 56 | * Initialise the free list, pointing alloc_nextfree at the array, and then 57 | * chaining array entries into the list. 58 | */ 59 | static void 60 | alloc_init(void) 61 | { 62 | int i; 63 | 64 | alloc_nextfree = &alloc_array[0]; 65 | for (i = 0; i < ALLOC_MAX - 1; i++) 66 | alloc_array[i].a_next = &alloc_array[i + 1]; 67 | alloc_array[ALLOC_MAX - 1].a_next = NULL; 68 | assert(alloc_array[ALLOC_MAX - 1].a_next == NULL); 69 | } 70 | 71 | /* 72 | * Allocate memory, pulling it off the free list and updating pointers as 73 | * needed. 74 | */ 75 | static void * 76 | alloc_allocate(void) 77 | { 78 | struct alloc_storage *alloc; 79 | 80 | if (alloc_nextfree == NULL) 81 | return (NULL); 82 | alloc = alloc_nextfree; 83 | alloc_nextfree = alloc->a_next; 84 | alloc->a_next = NULL; 85 | 86 | /* Return pointer to allocated memory. */ 87 | return (alloc->a_bytes); 88 | }; 89 | 90 | /* 91 | * Free memory, inserting it back into the free list. Note use of 92 | * __containerof() to convert pointer to a_bytes back into the container 93 | * struct pointer. 94 | */ 95 | static void 96 | alloc_free(void *ptr) 97 | { 98 | struct alloc_storage *alloc; 99 | 100 | /* Convert pointer to allocated memory into pointer to metadata. */ 101 | alloc = __containerof(ptr, struct alloc_storage, a_bytes); 102 | alloc->a_next = alloc_nextfree; 103 | alloc_nextfree = alloc; 104 | } 105 | 106 | int 107 | main(void) 108 | { 109 | void *ptr1, *ptr2, *ptr3; 110 | 111 | /* Initialise allocator. */ 112 | alloc_init(); 113 | printf("Allocator initialised\n"); 114 | 115 | /* 116 | * Allocate some memory. 117 | */ 118 | printf("Allocating memory\n"); 119 | ptr1 = alloc_allocate(); 120 | printf("Allocation returned %p\n", ptr1); 121 | 122 | /* 123 | * Run off the end of the memory allocation, corrupting the next 124 | * allocation's metadata. Free when done. 125 | */ 126 | printf("Preparing to overflow %p\n", ptr1); 127 | memset(ptr1 + ALLOC_SIZE, 'A', sizeof(void *)); 128 | printf("Overflowed allocation %p\n", ptr1); 129 | 130 | printf("Freeing allocation %p\n", ptr1); 131 | alloc_free(ptr1); 132 | printf("Allocation %p freed\n", ptr1); 133 | 134 | /* 135 | * Perform three sequential allocations to cause the allocator to 136 | * dereference the corrupted pointer, performing a store. 137 | */ 138 | printf("Allocating memory\n"); 139 | ptr1 = alloc_allocate(); 140 | printf("Allocation returned %p\n", ptr1); 141 | 142 | printf("Allocating memory\n"); 143 | ptr2 = alloc_allocate(); 144 | printf("Allocation returned %p\n", ptr2); 145 | 146 | printf("Allocating memory\n"); 147 | ptr3 = alloc_allocate(); 148 | printf("Allocation returned %p\n", ptr3); 149 | 150 | /* 151 | * Clear up the mess. 152 | */ 153 | printf("Freeing allocation %p\n", ptr3); 154 | alloc_free(ptr3); 155 | printf("Allocation %p freed\n", ptr3); 156 | 157 | printf("Freeing allocation %p\n", ptr2); 158 | alloc_free(ptr2); 159 | printf("Allocation %p freed\n", ptr2); 160 | 161 | printf("Freeing allocation %p\n", ptr1); 162 | alloc_free(ptr1); 163 | printf("Allocation %p freed\n", ptr1); 164 | 165 | exit(EX_OK); 166 | } 167 | -------------------------------------------------------------------------------- /src/exercises/buffer-overflow-stack/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Exercise an inter-stack-object buffer overflow 2 | 3 | 2. Expected output: 4 | ``` 5 | # ./buffer-overflow-stack-baseline 6 | upper = 0x80d879d0, lower = 0x80d879c0, diff = 10 7 | upper[0] = a 8 | upper[0] = b 9 | # ./buffer-overflow-stack-cheri 10 | upper = 0x3fffdfff50, lower = 0x3fffdfff40, diff = 10 11 | upper[0] = a 12 | In-address space security exception 13 | ``` 14 | 15 | 3. An example session of `gdb-run.sh ./buffer-overflow-stack-cheri` on CHERI-RISC-V: 16 | ``` 17 | Reading symbols from ./buffer-overflow-stack-cheri... 18 | Starting program: /mnt/buffer-overflow-stack-cheri 19 | upper = 0x3fffdfff50, lower = 0x3fffdfff40, diff = 10 20 | upper[0] = a 21 | 22 | Program received signal SIGPROT, CHERI protection violation 23 | Capability bounds fault caused by register ca0. 24 | 0x0000000000101cf0 in write_buf (buf=, ix=) at buffer-overflow-stack.c:13 25 | 13 buf[ix] = 'b'; 26 | 27 | Thread 1 (LWP 100055 of process 829): 28 | #0 0x0000000000101cf0 in write_buf (buf=, ix=) at buffer-overflow-stack.c:13 29 | #1 0x0000000000101d7a in main () at buffer-overflow-stack.c:31 30 | (gdb) disass 31 | Dump of assembler code for function write_buf: 32 | 0x0000000000101ce8 <+0>: cincoffset ca0,ca0,a1 33 | 0x0000000000101cec <+4>: li a1,98 34 | => 0x0000000000101cf0 <+8>: csb a1,0(ca0) 35 | 0x0000000000101cf4 <+12>: cret 36 | End of assembler dump. 37 | ``` 38 | 39 | Asking `gdb` about the registers with `info registers` and focusing 40 | on the ones involved here, we see 41 | ``` 42 | a0 0x3fffdfff50 274875809616 43 | a1 0x62 98 44 | 45 | ca0 0xd17d000007d5bf440000003fffdfff50 0x3fffdfff50 [rwRW,0x3fffdfff40-0x3fffdfff50] 46 | ca1 0x62 0x62 47 | ``` 48 | The capability in `ca0`, which is a pointer into the `lower` buffer, has been 49 | taken beyond the end of the allocation, as out of bounds store has been 50 | attempted (`Capability bounds fault`). 51 | 52 | But where did those bounds originate? Heading `up` a stack frame and 53 | `disass`embling, we see (eliding irrelevant instructions): 54 | ``` 55 | (gdb) up 56 | #1 0x0000000000101d7a in main () at buffer-overflow-stack.c:31 57 | 31 write_buf(lower, sizeof(lower)); 58 | (gdb) disass 59 | Dump of assembler code for function main: 60 | 0x0000000000101cf8 <+0>: cincoffset csp,csp,-144 61 | 62 | 0x0000000000101d14 <+28>: cincoffset ca0,csp,48 63 | 0x0000000000101d18 <+32>: csetbounds cs0,ca0,16 64 | 65 | 0x0000000000101d6c <+116>: li a1,16 66 | 0x0000000000101d6e <+118>: cmove ca0,cs0 67 | 0x0000000000101d72 <+122>: auipcc cra,0x0 68 | 0x0000000000101d76 <+126>: cjalr -138(cra) 69 | => 0x0000000000101d7a <+130>: clbu a0,0(cs1) 70 | ``` 71 | The compiler has arranged for `main` to allocate 144 bytes on the stack by 72 | decrementing the *capability stack pointer* register (`csp`) by 144 bytes. 73 | Further, the compiler has placed `lower` 48 bytes up into that allocation: 74 | `ca0` is made to point at its lowest address and then the pointer to `lower` 75 | is materialized in `cs0` by *bounding* the capability in `ca0` to be 16 76 | (`sizeof(lower)`) bytes long. This capability is passed to `write_buf` in 77 | `ca0`. 78 | 79 | 4. The code for `write_buf` function is only slightly changed. On 80 | RISC-V it compiles to 81 | ``` 82 | : 83 | add a0, a0, a1 84 | addi a1, zero, 98 85 | sb a1, 0(a0) 86 | ret 87 | ``` 88 | while on CHERI-RISC-V, it is 89 | ``` 90 | : 91 | cincoffset ca0, ca0, a1 92 | addi a1, zero, 98 93 | csb a1, 0(ca0) 94 | cret 95 | ``` 96 | In both cases, it amounts to displacing the pointer passed in `a0` (resp. 97 | `ca0`) by the offset passed in `a1` and then performing a store-byte 98 | instruction before returning. In the baseline case, the store-byte takes an 99 | *integer* address for its store, while in the CHERI case, the store-byte takes 100 | a *capability authorizing the store*. There are no conditional branches or 101 | overt bounds checks in the CHERI instruction stream; rather, the `csb` 102 | instruction itself enforces the requirement for authority to write to memory, 103 | in the shape of a valid, in-bounds capability. 104 | 105 | We have already seen the CHERI program's call site to `write_buf` in `main`, 106 | and the derivation of the capability to the `lower` buffer, above. In the 107 | baseline version, the corresponding instructions are shown as 108 | ``` 109 | (gdb) disass main 110 | Dump of assembler code for function main: 111 | 0x0000000000011b44 <+0>: addi sp,sp,-48 112 | 113 | 0x0000000000011b8a <+70>: mv a0,sp 114 | 0x0000000000011b8c <+72>: li a1,16 115 | 0x0000000000011b8e <+74>: auipc ra,0x0 116 | 0x0000000000011b92 <+78>: jalr -86(ra) # 0x11b38 117 | ``` 118 | Here, the compiler has reserved only 48 bytes of stack space and has placed the 119 | `lower` buffer at the lowest bytes of this reservation. Thus, to pass a 120 | pointer to the `lower` buffer to `write_buf`, the program simply copies the 121 | stack pointer register (an *integer* register, holding an *address*) to the 122 | argument register `a0`. The subsequent address arithmetic derives an address 123 | out of bounds, clobbering a byte of the `upper` register. 124 | -------------------------------------------------------------------------------- /src/exercises/debug-and-disassemble/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Disassemble and debug RISC-V and CHERI-RISC-V programs 2 | 3 | 2. `jalr`. The target address is a pc-relative address in the `.plt` 4 | section addressed by a sequence like: 5 | ``` 6 | 1182a: 97 00 00 00 auipc ra, 0 7 | 1182e: e7 80 60 0e jalr 230(ra) 8 | ``` 9 | 3. `cjalr`. The target capability is loaded from the `.captable` section 10 | by a sequence like: 11 | ``` 12 | 1b2e: 17 24 00 00 auipcc cs0, 2 13 | 1b32: 0f 24 24 27 clc cs0, 626(cs0) 14 | 1b36: db 00 c4 fe cjalr cs0 15 | ``` 16 | 4. Example session: 17 | ``` 18 | (gdb) b printf 19 | Breakpoint 1 at 0x11914 20 | ``` 21 | 5. Example session: 22 | ``` 23 | (gdb) r 24 | Starting program: /root/print-pointer-riscv 25 | 26 | Breakpoint 1, printf (fmt=) 27 | at /Volumes/CheriBSD/cheribsd/lib/libc/stdio/printf.c:56 28 | 56 /Volumes/CheriBSD/cheribsd/lib/libc/stdio/printf.c: No such file or directory. 29 | (gdb) info reg a0 30 | a0 0x1054f 66895 31 | ``` 32 | 6. Example session: 33 | ``` 34 | (gdb) info proc mappings 35 | process 764 36 | Mapped address spaces: 37 | 38 | Start Addr End Addr Size Offset Flags File 39 | 0x10000 0x11000 0x1000 0x0 r-- CN-- /root/print-pointer-riscv 40 | 0x11000 0x12000 0x1000 0x0 r-x C--- /root/print-pointer-riscv 41 | 0x12000 0x13000 0x1000 0x0 r-- C--- /root/print-pointer-riscv 42 | 0x13000 0x14000 0x1000 0x0 rw- ---- 43 | 0x40013000 0x40018000 0x5000 0x0 r-- CN-- /libexec/ld-elf64.so.1 44 | 0x40018000 0x4002a000 0x12000 0x4000 r-x C--- /libexec/ld-elf64.so.1 45 | 0x4002a000 0x4002b000 0x1000 0x15000 rw- C--- /libexec/ld-elf64.so.1 46 | 0x4002b000 0x4004e000 0x23000 0x0 rw- ---- 47 | 0x4004f000 0x400c2000 0x73000 0x0 r-- CN-- /usr/lib64/libc.so.7 48 | 0x400c2000 0x401de000 0x11c000 0x72000 r-x C--- /usr/lib64/libc.so.7 49 | 0x401de000 0x401e8000 0xa000 0x18d000 r-- C--- /usr/lib64/libc.so.7 50 | 0x401e8000 0x401ef000 0x7000 0x196000 rw- C--- /usr/lib64/libc.so.7 51 | 0x401ef000 0x40419000 0x22a000 0x0 rw- ---- 52 | 0x40600000 0x40e00000 0x800000 0x0 rw- ---- 53 | 0x3f3ef00000 0x3f7eee0000 0x3ffe0000 0x0 --- ---- 54 | 0x3f7eee0000 0x3f7ef00000 0x20000 0x0 rw- ---D 55 | 0x3f7efff000 0x3f7f000000 0x1000 0x0 r-x ---- 56 | ``` 57 | 7. Example session: 58 | ``` 59 | (gdb) info reg pc 60 | pc 0x401bf640 1075574336 61 | ``` 62 | In this example, the pointer resides in: 63 | ``` 64 | 0x400c2000 0x401de000 0x11c000 0x72000 r-x C--- /usr/lib64/libc.so.7 65 | ``` 66 | 8. Example session: 67 | ``` 68 | (gdb) b printf 69 | Function "printf" not defined. 70 | Make breakpoint pending on future shared library load? (y or [n]) y 71 | Breakpoint 1 (printf) pending. 72 | ``` 73 | 9. Example session: 74 | ``` 75 | (gdb) r 76 | Starting program: /root/print-pointer-cheri 77 | 78 | Breakpoint 1, printf ( 79 | fmt=0x1004ef [rR,0x1004ef-0x100505] "size of pointer: %zu\n") 80 | at /Volumes/CheriBSD/cheribsd/lib/libc/stdio/printf.c:54 81 | 54 /Volumes/CheriBSD/cheribsd/lib/libc/stdio/printf.c: No such file or directory. 82 | (gdb) p fmt 83 | $1 = 0x1004ef [rR,0x1004ef-0x100505] "size of pointer: %zu\n" 84 | ``` 85 | 10. Example session: 86 | ``` 87 | (gdb) info proc mappings 88 | process 767 89 | Mapped address spaces: 90 | 91 | Start Addr End Addr Size Offset Flags File 92 | 0x100000 0x101000 0x1000 0x0 r-- CN-- /root/print-pointer-cheri 93 | 0x101000 0x102000 0x1000 0x0 r-x CN-- /root/print-pointer-cheri 94 | 0x102000 0x103000 0x1000 0x0 r-- C--- /root/print-pointer-cheri 95 | 0x103000 0x104000 0x1000 0x0 rw- ---- 96 | 0x40103000 0x4010a000 0x7000 0x0 rw- ---- 97 | 0x4010a000 0x4010c000 0x2000 0x0 --- CN-- 98 | 0x4010d000 0x40193000 0x86000 0x0 r-- CN-- /lib/libc.so.7 99 | 0x40193000 0x4028c000 0xf9000 0x85000 r-x C--- /lib/libc.so.7 100 | 0x4028c000 0x40293000 0x7000 0x17d000 r-- C--- /lib/libc.so.7 101 | 0x40293000 0x402ac000 0x19000 0x183000 rw- C--- /lib/libc.so.7 102 | 0x402ac000 0x402c9000 0x1d000 0x0 rw- ---- 103 | 0x402c9000 0x402f1000 0x28000 0x0 rw- ---- 104 | 0x41000000 0x4100b000 0xb000 0x0 r-- CN-- /libexec/ld-elf.so.1 105 | 0x4100b000 0x4102a000 0x1f000 0xa000 r-x C--- /libexec/ld-elf.so.1 106 | 0x4102a000 0x4102b000 0x1000 0x28000 rw- C--- /libexec/ld-elf.so.1 107 | 0x4102b000 0x4102e000 0x3000 0x28000 rw- C--- /libexec/ld-elf.so.1 108 | 0x4102e000 0x41030000 0x2000 0x0 rw- ---- 109 | 0x3f3ef00000 0x3f7ece0000 0x3fde0000 0x0 --- ---- 110 | 0x3f7ece0000 0x3f7eee0000 0x200000 0x0 rw- ---D 111 | 0x3f7eee0000 0x3f7ef00000 0x20000 0x0 rw- ---D 112 | 0x3f7efff000 0x3f7f000000 0x1000 0x0 r-x ---- 113 | 0x3f7f000000 0x4000000000 0x81000000 0x0 rw- ---- 114 | ``` 115 | 11. Example session: 116 | ``` 117 | (gdb) info reg pcc 118 | pcc 0xf11720000325d0d4000000004026e9e6 0x4026e9e6 [rxR,0x4010d000-0x402c9000] 119 | ``` 120 | The capability points at: 121 | ``` 122 | 0x40193000 0x4028c000 0xf9000 0x85000 r-x C--- /lib/libc.so.7 123 | ``` 124 | 12. Left as an exercise to the reader. 125 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/cat/methods.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * SPDX-License-Identifier: BSD-3-Clause 3 | * 4 | * Copyright (c) 1989, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Kevin Fall. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | */ 34 | 35 | #include 36 | __FBSDID("$FreeBSD$"); 37 | 38 | #include 39 | #include 40 | #ifndef NO_UDOM_SUPPORT 41 | #include 42 | #include 43 | #include 44 | #endif 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #include "cat.h" 59 | 60 | typedef uintptr_t ptroff_t; 61 | 62 | extern int bflag, eflag, lflag, nflag, sflag, tflag, vflag; 63 | extern int rval; 64 | extern const char *filename; 65 | 66 | static ssize_t 67 | write_off(int fildes, const void *buf, ptroff_t off, size_t nbyte) 68 | { 69 | 70 | return (write(fildes, (const void *)(off + (uintptr_t)buf), nbyte)); 71 | } 72 | 73 | static void 74 | verbose_cat(long file) 75 | { 76 | FILE *fp; 77 | int ch, gobble, line, prev; 78 | wint_t wch; 79 | 80 | fp = (FILE *)file; 81 | 82 | /* Reset EOF condition on stdin. */ 83 | if (fp == stdin && feof(stdin)) 84 | clearerr(stdin); 85 | 86 | line = gobble = 0; 87 | for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 88 | if (prev == '\n') { 89 | if (sflag) { 90 | if (ch == '\n') { 91 | if (gobble) 92 | continue; 93 | gobble = 1; 94 | } else 95 | gobble = 0; 96 | } 97 | if (nflag) { 98 | if (!bflag || ch != '\n') { 99 | (void)fprintf(stdout, "%6d\t", ++line); 100 | if (ferror(stdout)) 101 | break; 102 | } else if (eflag) { 103 | (void)fprintf(stdout, "%6s\t", ""); 104 | if (ferror(stdout)) 105 | break; 106 | } 107 | } 108 | } 109 | if (ch == '\n') { 110 | if (eflag && putchar('$') == EOF) 111 | break; 112 | } else if (ch == '\t') { 113 | if (tflag) { 114 | if (putchar('^') == EOF || putchar('I') == EOF) 115 | break; 116 | continue; 117 | } 118 | } else if (vflag) { 119 | (void)ungetc(ch, fp); 120 | /* 121 | * Our getwc(3) doesn't change file position 122 | * on error. 123 | */ 124 | if ((wch = getwc(fp)) == WEOF) { 125 | if (ferror(fp) && errno == EILSEQ) { 126 | clearerr(fp); 127 | /* Resync attempt. */ 128 | #ifdef __FreeBSD__ 129 | memset(&fp->_mbstate, 0, sizeof(mbstate_t)); 130 | #endif 131 | if ((ch = getc(fp)) == EOF) 132 | break; 133 | wch = ch; 134 | goto ilseq; 135 | } else 136 | break; 137 | } 138 | if (!iswascii(wch) && !iswprint(wch)) { 139 | ilseq: 140 | if (putchar('M') == EOF || putchar('-') == EOF) 141 | break; 142 | wch = toascii(wch); 143 | } 144 | if (iswcntrl(wch)) { 145 | ch = toascii(wch); 146 | ch = (ch == '\177') ? '?' : (ch | 0100); 147 | if (putchar('^') == EOF || putchar(ch) == EOF) 148 | break; 149 | continue; 150 | } 151 | if (putwchar(wch) == WEOF) 152 | break; 153 | ch = -1; 154 | continue; 155 | } 156 | if (putchar(ch) == EOF) 157 | break; 158 | } 159 | if (ferror(fp)) { 160 | warn("%s", filename); 161 | rval = 1; 162 | clearerr(fp); 163 | } 164 | if (ferror(stdout)) 165 | err(1, "stdout"); 166 | } 167 | 168 | static void 169 | raw_cat(long file) 170 | { 171 | long pagesize; 172 | int off, rfd, wfd; 173 | ssize_t nr, nw; 174 | static size_t bsize; 175 | static char *buf = NULL; 176 | struct stat sbuf; 177 | 178 | rfd = (int)file; 179 | 180 | wfd = fileno(stdout); 181 | if (buf == NULL) { 182 | if (fstat(wfd, &sbuf)) 183 | err(1, "stdout"); 184 | if (S_ISREG(sbuf.st_mode)) { 185 | /* If there's plenty of RAM, use a large copy buffer */ 186 | if (sysconf(_SC_PHYS_PAGES) > PHYSPAGES_THRESHOLD) 187 | bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 188 | else 189 | bsize = BUFSIZE_SMALL; 190 | } else { 191 | bsize = sbuf.st_blksize; 192 | pagesize = sysconf(_SC_PAGESIZE); 193 | if (pagesize > 0) 194 | bsize = MAX(bsize, (size_t)pagesize); 195 | } 196 | if ((buf = malloc(bsize)) == NULL) 197 | err(1, "malloc() failure of IO buffer"); 198 | } 199 | while ((nr = read(rfd, buf, bsize)) > 0) 200 | for (off = 0; nr; nr -= nw, off += nw) 201 | if ((nw = write_off(wfd, buf, off, (size_t)nr)) < 0) 202 | err(1, "write(2) failed"); 203 | if (nr < 0) { 204 | warn("%s", filename); 205 | rval = 1; 206 | } 207 | } 208 | 209 | void 210 | do_cat(long file, int verbose) 211 | { 212 | 213 | if (verbose) { 214 | verbose_cat(file); 215 | } else { 216 | raw_cat(file); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/missions/use-after-free-control-flow/temporal-mission.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * Copyright (c) 2020 Microsoft, Inc. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #if defined(__CHERI_PURE_CAPABILITY__) 15 | #include 16 | static void __attribute__((used)) *check_cheri_revoke = cheri_revoke; 17 | extern void malloc_revoke(void); 18 | static void __attribute__((used)) *check_malloc_revoke = malloc_revoke; 19 | #endif 20 | 21 | struct farm; 22 | struct crop; 23 | 24 | static void 25 | success(struct farm *f) 26 | { 27 | fprintf(stderr, "Exploit successful: computer bought the farm!\n"); 28 | exit(42); 29 | } 30 | 31 | TAILQ_HEAD(cropq, crop); 32 | 33 | struct farm { 34 | struct cropq cropq; 35 | struct crop *cursor; 36 | }; 37 | 38 | struct crop { 39 | void (*describe)(struct crop *); 40 | TAILQ_ENTRY(crop) farm_cropq; 41 | }; 42 | 43 | _Static_assert(sizeof(struct crop) == sizeof(struct farm), 44 | "Structure size mismatch"); 45 | 46 | union ufo { 47 | void *ptrA; 48 | char buf[sizeof(struct crop)]; 49 | }; 50 | 51 | _Static_assert(sizeof(struct crop) == sizeof(union ufo), 52 | "Structure size mismatch"); 53 | 54 | static void 55 | descr_1(struct crop *c) 56 | { 57 | fprintf(stderr, "Chelan at %p\n", c); /* Pacific Northwest */ 58 | } 59 | 60 | static void 61 | descr_2(struct crop *c) 62 | { 63 | fprintf(stderr, "Colt at %p\n", c); /* United Kingdom */ 64 | } 65 | 66 | static unsigned int cid; 67 | static union ufo *ufo; 68 | 69 | #define NFARM 4 70 | struct farm *farmp[NFARM]; 71 | 72 | static void 73 | rm_farm(int fix) 74 | { 75 | struct farm *f = farmp[fix]; 76 | 77 | farmp[fix] = NULL; 78 | if (f != NULL) { 79 | struct crop *c, *tc; 80 | 81 | TAILQ_FOREACH_SAFE(c, &f->cropq, farm_cropq, tc) { 82 | TAILQ_REMOVE(&f->cropq, c, farm_cropq); 83 | free(c); 84 | } 85 | 86 | fprintf(stderr, "Tear down farm (index %d) at %p\n", fix, f); 87 | free(f); 88 | } 89 | } 90 | 91 | static struct farm * 92 | mk_farm(int fix) 93 | { 94 | struct farm *f; 95 | 96 | rm_farm(fix); 97 | 98 | f = malloc(sizeof(struct farm)); 99 | assert(f != NULL); /* Surely infinite memory */ 100 | 101 | TAILQ_INIT(&f->cropq); 102 | f->cursor = NULL; 103 | 104 | farmp[fix] = f; 105 | 106 | fprintf(stderr, "New farm (index %d) at %p\n", fix, f); 107 | return f; 108 | } 109 | 110 | static void 111 | rm_crop(struct farm *f, struct crop *c) 112 | { 113 | fprintf(stderr, "Del crop at %p\n", c); 114 | TAILQ_REMOVE(&f->cropq, c, farm_cropq); 115 | free(c); 116 | } 117 | 118 | static struct crop * 119 | mk_crop(struct farm *f) 120 | { 121 | struct crop *c; 122 | 123 | c = malloc(sizeof(struct crop)); 124 | assert(c != NULL); 125 | 126 | if (f->cursor != NULL) { 127 | /* Inherit description of current cursor */ 128 | c->describe = f->cursor->describe; 129 | } else { 130 | c->describe = (cid & 1) ? descr_1 : descr_2 ; 131 | } 132 | cid++; 133 | 134 | TAILQ_INSERT_HEAD(&f->cropq, c, farm_cropq); 135 | 136 | fprintf(stderr, "New crop at %p\n", c); 137 | 138 | return c; 139 | } 140 | 141 | static void 142 | rm_ufo(void) 143 | { 144 | if (ufo != NULL) { 145 | fprintf(stderr, "Del UFO at %p\n", ufo); 146 | free(ufo); 147 | } 148 | } 149 | 150 | int 151 | main(void) 152 | { 153 | int c; 154 | size_t fix = 0; 155 | 156 | #if defined(__CHERI_PURE_CAPABILITY__) 157 | if (getenv("MALLOC_DISABLE_REVOCATION") == NULL) { 158 | fprintf(stderr, "Ready (CHERI-RISC-V)\n"); 159 | } else { 160 | fprintf(stderr, "Ready (CHERI-RISC-V, reduced heap safety)\n"); 161 | } 162 | #else 163 | fprintf(stderr, "Ready (RISC-V)\n"); 164 | #endif 165 | 166 | while ((c = getchar()) != EOF) { 167 | if (isspace(c)) 168 | continue; 169 | 170 | if (('0' <= c) && (c < '0' + NFARM)) { 171 | fix = c - '0'; 172 | fprintf(stderr, "Selected farm %zu (%p)\n", fix, 173 | farmp[fix]); 174 | continue; 175 | } 176 | 177 | struct farm *f = farmp[fix]; 178 | switch (c) { 179 | case '!': 180 | #if defined(__CHERI_PURE_CAPABILITY__) 181 | malloc_revoke(); 182 | #else 183 | fprintf(stderr, "No revocation without CHERI!\n"); 184 | #endif 185 | break; 186 | 187 | /* Crop management */ 188 | case 'C': 189 | if (f != NULL) 190 | mk_crop(f); 191 | break; 192 | case 'c': 193 | if ((f != NULL) && (f->cursor != NULL)) 194 | rm_crop(f, f->cursor); 195 | break; 196 | case 'D': 197 | fprintf(stderr, "FYI: Current farm is %p\n", f); 198 | if ((f != NULL) && (f->cursor != NULL)) { 199 | fprintf(stderr, "FYI: cursor %p\n", f->cursor); 200 | fprintf(stderr, "FYI: cursor->describe %p\n", 201 | f->cursor->describe); 202 | f->cursor->describe(f->cursor); 203 | } 204 | break; 205 | 206 | /* Farm management */ 207 | case 'F': 208 | mk_farm(fix); 209 | break; 210 | case 'f': 211 | rm_farm(fix); 212 | break; 213 | 214 | /* Cursor control */ 215 | case 'L': 216 | if (f != NULL) { 217 | if (f->cursor != NULL) { 218 | f->cursor = TAILQ_PREV(f->cursor, cropq, 219 | farm_cropq); 220 | } else { 221 | f->cursor = TAILQ_LAST(&f->cropq, 222 | cropq); 223 | } 224 | } 225 | fprintf(stderr, "Farm %zu cursor %p\n", fix, f->cursor); 226 | break; 227 | case 'R': 228 | if (f != NULL) { 229 | if (f->cursor != NULL) { 230 | f->cursor = TAILQ_NEXT(f->cursor, 231 | farm_cropq); 232 | } else { 233 | f->cursor = TAILQ_FIRST(&f->cropq); 234 | } 235 | } 236 | fprintf(stderr, "Farm %zu cursor %p\n", fix, f->cursor); 237 | break; 238 | case 'Z': 239 | if (f != NULL) 240 | f->cursor = NULL; 241 | break; 242 | 243 | /* UFO control sequences */ 244 | case 'A': 245 | if ((ufo != NULL) && (f != NULL)) { 246 | fprintf(stderr, "UFO abduct %p\n", f->cursor); 247 | ufo->ptrA = f->cursor; 248 | } 249 | break; 250 | case 'S': 251 | if (f != NULL) { 252 | /* Jess's Organic Farm-to-Vtable Capability */ 253 | fprintf(stderr, "Crop sign at farm %p\n", f); 254 | f->cursor = (void *)success; 255 | } 256 | break; 257 | case 's': 258 | if ((f != NULL) && (f->cursor != NULL)) { 259 | char buf[sizeof(void *)]; 260 | for (size_t i = 0; i < sizeof buf; i++) { 261 | buf[i] = getchar(); 262 | } 263 | fprintf(stderr, "Signing crop %p\n", f->cursor); 264 | memmove((char *)f->cursor, buf, sizeof(buf)); 265 | } 266 | break; 267 | case 'U': 268 | rm_ufo(); 269 | ufo = malloc(sizeof(union ufo)); 270 | assert(ufo != NULL); 271 | fprintf(stderr, "UFO at %p\n", ufo); 272 | break; 273 | case 'u': 274 | rm_ufo(); 275 | break; 276 | 277 | default: 278 | fprintf(stderr, "Did not understand %x; bail!\n", c); 279 | return 1; 280 | } 281 | } 282 | 283 | return 0; 284 | } 285 | -------------------------------------------------------------------------------- /src/missions/use-after-free-control-flow/README.md: -------------------------------------------------------------------------------- 1 | # Exploiting heap use-after-free to manipulate control flow 2 | 3 | **This mission requires a CheriBSD sysroot and image with temporal safety!** 4 | The executable can be built for both RISC-V and CHERI-RISC-V (and exploring 5 | both may be worthwhile), but the mission is for CHERI-RISC-V with heap temporal 6 | safety enforcement in place. 7 | 8 | This mission is a potted exercise inspired by real-world vulnerabilities where 9 | an active adversary can influence the contents of a server's heap and, often 10 | without completing authentication, walk the server's protocol state machine 11 | through erroneous states. Popular bugs facilitating these exploits include 12 | double free, use-after-reallocation, and type confusion (esp. of temporally 13 | aliased objects). For this mission, we have simplified the interface to be a 14 | little "command language" read over `stdin`, detailed below. 15 | 16 | The success criterion is executing the `success` function. To ease testing, 17 | this is the only case in which the program source claims to exit with a code of 18 | `42`. Ordinary termination is signaled with `0` and invalid input results in 19 | the program terminating with `1`. While the source does not overtly claim any 20 | other outcome, if control flow can be redirected, almost anything goes. 21 | 22 | ### Program Overview 23 | 24 | This is a (very) minimal simulation of a program that manages multiple 25 | sessions, objects within each session, and limited information flow between 26 | those sessions. Rather than network sockets, each session is represented by a 27 | `struct farm` and there are up to four such active at any moment. Within each 28 | `struct farm` is a circular collection of `struct crop`s, at most one of which 29 | may be selected by the `cursor` of its owning `struct farm`. If no crop is 30 | selected, the cursor is said to be "in the gap". The crop at the cursor, if 31 | any, can be asked to describe itself, using a function pointer within the 32 | `struct crop` itself. 33 | 34 | To make matters more exciting, a UFO may be summoned to perform particular 35 | kinds of mischief. The UFO may "abduct" a pointer into itself and can create 36 | crop signs within `struct crop` (with data) or `struct farm` (with a capability 37 | to the `success()` function). 38 | 39 | The gadgets offered by the UFO are somewhat limited (that is, they are not 40 | "write what where" or arbitrary control transfer) as one might expect to see in 41 | a real application. Nevertheless, they are sufficiently powerful to provide 42 | *myriad* exploitation vectors on RISC-V without CHERI, and even a few on 43 | CHERI-RISC-V. However, we believe that enforced heap temporal safety will 44 | ensure that any use of them is either overwritten by subsequent program 45 | operations before the gadgets' effects influence control flow or will cause the 46 | program to fail-stop with a default-fatal signal (e.g., `SIGSEGV` or 47 | `SIGPROT`). 48 | 49 | ### Building and running 50 | 51 | We suggest using the `ccc` tool provided with this book, building for 52 | `riscv64-purecap`. For experimentation, this program also builds for 53 | `riscv64`, without CHERI. On `riscv64-purecap` builds, heap temporal safety 54 | enforcement may be disabled at program load time by setting the environment 55 | variable `MALLOC_DISABLE_REVOCATION` to a non-empty value. (While setting this 56 | flag prior to program loading disqualifies such invocations from completing the 57 | mission, being able to disable revocation from within `main()` would be quite 58 | interesting indeed.) 59 | 60 | As said before, the program expects to read a command language from `stdin`. 61 | It will print "Ready" and then await input. Unlike real applications, this 62 | program is fairly chatty about its own operation to ease exploration. 63 | Nevertheless, it may be useful to run it within `gdb`, especially to 64 | differentiate causes of crashes. 65 | 66 | ### Command Language Directives 67 | 68 | * Whitespace is quietly ignored in most cases; this may simplify reading 69 | programs. 70 | 71 | * Digits `0` through `3` focus on the corresponding farm slots, making it the 72 | locus of subsequent commands until altered. 73 | 74 | * `F` allocates a new farm at the current slot. 75 | 76 | * `f` frees the current slot's farm (along with any crops in its collection) 77 | 78 | * `C` creates a new crop in the current farm and puts it at the left of the 79 | collection. The cursor is left in the gap or pointing at the crop it was 80 | before. If the cursor is not in the gap, the new crop will inherit the 81 | description of the selected crop; otherwise, the program chooses one of two 82 | varieties of cherry. 83 | 84 | * `L` and `R` move the current farm's cursor left and right, respectively. The 85 | farm's collection is circular with a gap; moving left (right) from the gap 86 | places the cursor at the rightmost (leftmost) element, and walking off either 87 | end returns the cursor to the gap. 88 | 89 | * `Z` moves the current farm's cursor to the gap. 90 | 91 | * `D` describes the crop at the current farm's cursor, if that cursor is not in 92 | the gap. 93 | 94 | * `c` removes the crop at the current farm's cursor from the collection and 95 | frees it. 96 | 97 | * `U` causes a UFO to arrive; `u` causes it to leave. There is at most one UFO 98 | at any moment. 99 | 100 | * `A` causes the UFO, if present, to abduct the current farm's cursor, 101 | presumably for further scrutiny. 102 | 103 | * `S` causes the UFO to make a crop sign on the current farm; `s` causes the 104 | UFO to read the next `sizeof(void*)` characters from `stdin` (whitespace is 105 | not ignored for this) and use that to sign the crop indicated by the current 106 | farm's cursor (a smaller crop sign, if you will). Writing crop signs of 107 | either variety is very destabilizing and likely to lead to crashes! 108 | 109 | * On CHERI-RISC-V builds running with heap temporal safety enforcement, `!` 110 | will force a revocation pass, destroying pointers to free objects and 111 | allowing reuse of memory and address space. 112 | 113 | ### Example Session 114 | 115 | Here is a short session which creates one `struct farm`, two `struct crop`s, 116 | exhibits cursor control and description of `struct crop`s, and then tears down 117 | the `struct farm`: 118 | ``` 119 | $ echo FCC RD ZLD f | ./temporal-mission 120 | Ready (CHERI-RISC-V) 121 | New farm (index 0) at 0x41201080 122 | New crop at 0x41201100 123 | New crop at 0x41201180 124 | Farm 0 cursor 0x41201180 125 | FYI: Current farm is 0x41201080 126 | FYI: cursor 0x41201180 127 | FYI: cursor->describe 0x102fd2 128 | Chelan at 0x41201180 129 | Farm 0 cursor 0x41201100 130 | FYI: Current farm is 0x41201080 131 | FYI: cursor 0x41201100 132 | FYI: cursor->describe 0x10300e 133 | Colt at 0x41201100 134 | Tear down farm (index 0) at 0x41201080 135 | ``` 136 | 137 | ### Source code 138 | 139 | **temporal-mission.c** 140 | ```C 141 | {{#include temporal-mission.c}} 142 | ``` 143 | -------------------------------------------------------------------------------- /src/exercises/subobject-bounds/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Explore Subobject Bounds 2 | 3 | ## Exercise a subobject buffer overflow 4 | 5 | This exercise demonstrates how subobject bounds can correct and array in a 6 | structure. 7 | 8 | 2. Expected output: 9 | ``` 10 | # ./buffer-overflow-subobject-riscv 11 | b.i = c 12 | b.i = b 13 | # ./buffer-overflow-subobject-cheri 14 | b.i = c 15 | b.i = b 16 | ``` 17 | 18 | 3. Example session: 19 | ``` 20 | (gdb) b fill_buf 21 | Breakpoint 1 at 0x1bae: file buffer-overflow-subobject.c, line 17. 22 | (gdb) r 23 | Starting program: /root/buffer-overflow-subobject-cheri 24 | b.i = c 25 | 26 | Breakpoint 1, fill_buf (buf=0x103f50 [rwRW,0x103f50-0x103fd4] "", len=128) 27 | at buffer-overflow-subobject.c:17 28 | 17 buf[i] = 'b'; 29 | ``` 30 | The bounds are `132` bytes corresponding to the size of the underlying object. 31 | 32 | 5. Expected output: 33 | ``` 34 | # ./buffer-overflow-subobject-cheri 35 | b.i = c 36 | In-address space security exception (core dumped) 37 | ``` 38 | 39 | 6. Example session: 40 | ``` 41 | (gdb) b fill_buf 42 | Breakpoint 1 at 0x1bae: file buffer-overflow-subobject.c, line 17. 43 | (gdb) r 44 | Starting program: /root/buffer-overflow-subobject-cheri 45 | b.i = c 46 | 47 | Breakpoint 1, fill_buf (buf=0x103f50 [rwRW,0x103f50-0x103fd0] "", len=128) at buffer-overflow-subobject.c:17 48 | 17 buf[i] = 'b'; 49 | ``` 50 | The pointer to the buffer is now bounded to the array rather than the object. 51 | 52 | Investigating further will reveal that the compiler has inserted a 53 | bounds-setting instruction prior to the call to `fill_buf` in `main`, that 54 | is, when the pointer to `b.buffer` is materialized. 55 | ``` 56 | (gdb) up 57 | #1 0x0000000000101c0c in main () at buffer-overflow-subobject.c:26 58 | 26 fill_buf(b.buffer, sizeof(b.buffer)); 59 | (gdb) disassemble 60 | Dump of assembler code for function main: 61 | 0x0000000000101bc0 <+0>: cincoffset csp,csp,-64 62 | ... 63 | 0x0000000000101bfc <+60>: csetbounds ca0,cs1,128 64 | 0x0000000000101c00 <+64>: li a1,128 65 | 0x0000000000101c04 <+68>: auipcc cra,0x0 66 | 0x0000000000101c08 <+72>: cjalr -92(cra) 67 | => 0x0000000000101c0c <+76>: clw a0,128(cs1) 68 | ``` 69 | 70 | ## Deliberately Using Larger Bounds 71 | 72 | 1. Example output: 73 | ``` 74 | Traversing list=0x104320 [rwRW,0x104320-0x104340] first=0x1040e0 [rwRW,0x1040d0-0x104100] lastnp=0x104150 [rwRW,0x104130-0x104160] 75 | Ilist cursor=0x1040e0 [rwRW,0x1040d0-0x104100] 76 | next=0x104140 [rwRW,0x104130-0x104160] 77 | prevnp=0x104330 [rwRW,0x104320-0x104340] 78 | val field at 0x1040d0 [rwRW,0x1040d0-0x104100] 79 | Ilist cursor=0x104140 [rwRW,0x104130-0x104160] 80 | next=0x104320 [rwRW,0x104320-0x104340] 81 | prevnp=0x1040f0 [rwRW,0x1040d0-0x104100] 82 | val field at 0x104130 [rwRW,0x104130-0x104160] 83 | Traversing list again, accessing superobject field... 84 | Ilist cursor=0x1040e0 [rwRW,0x1040d0-0x104100] value=1 (at 0x1040d0 [rwRW,0x1040d0-0x104100]) 85 | Ilist cursor=0x104140 [rwRW,0x104130-0x104160] value=3 (at 0x104130 [rwRW,0x104130-0x104160]) 86 | ``` 87 | 88 | 2. In turn: 89 | 90 | - All capabilities referencing the sentinel or its fields (including 91 | `&l->ile_next`) have length `0x20`, corresponding to `sizeof(struct 92 | ilist_elem`). 93 | 94 | - The next pointers in the sentinel, `0x1040d0 [rwRW,0x1040c0-0x1040f0]`, and 95 | in the first list element, `0x104130 [rwRW,0x104120-0x104150]`, have legth 96 | `0x30`, corresponding to `sizeof(struct obj)`. 97 | 98 | - The previous-next pointers in the sentinel, `0x104140 99 | [rwRW,0x104120-0x104150]` and in the last list element, `0x1040e0 100 | [rwRW,0x1040c0-0x1040f0]` also have length `0x30`. 101 | 102 | 3. Example output: 103 | ``` 104 | Traversing list=0x104350 [rwRW,0x104350-0x104370] first=0x104120 [rwRW,0x104120-0x104140] lastnp=0x104190 [rwRW,0x104190-0x1041a0] 105 | Ilist cursor=0x104120 [rwRW,0x104120-0x104140] 106 | next=0x104180 [rwRW,0x104180-0x1041a0] 107 | prevnp=0x104360 [rwRW,0x104360-0x104370] 108 | val field at 0x104110 [rwRW,0x104120-0x104140] 109 | Ilist cursor=0x104180 [rwRW,0x104180-0x1041a0] 110 | next=0x104350 [rwRW,0x104350-0x104370] 111 | prevnp=0x104130 [rwRW,0x104130-0x104140] 112 | val field at 0x104170 [rwRW,0x104180-0x1041a0] 113 | Traversing list again, accessing superobject field... 114 | In-address space security exception 115 | ``` 116 | 117 | Notice the line `val field at 0x104110 [rwRW,0x104120-0x104140]`. This is 118 | out of bounds! 119 | 120 | The compiler has taken our use of `&obj1.ilist` as an argument to 121 | `ilist_insert_after` as license to *narrow* the bounds to just the subobject. 122 | Indeed, the length of all `ile_next` pointers is now `0x20`. Further, all 123 | `ile_lastnp` pointers now have length `0x10`, the size of just the capability 124 | they point to! 125 | 126 | 4. The compiler will emit a pair of warnings about the uses of 127 | `ILIST_CONTAINER`: 128 | ``` 129 | ./subobject-list.c:75:20: error: static_assert failed due to requirement '__builtin_marked_no_subobject_bounds(struct obj) || __builtin_marked_no_subobject_bounds(struct ilist_elem)' "this type is unsafe for use in containerof() with sub-objectbounds. Please mark the member/type with __subobject_use_container_bounds" 130 | ``` 131 | 132 | 5. We can take the compiler's advice in at least two ways: 133 | 134 | 1. We could mark the `struct ilist_elem` type itself: 135 | ``` 136 | struct ilist_elem { 137 | struct ilist_elem **ile_prevnp; 138 | struct ilist_elem *ile_next; 139 | } __subobject_use_container_bounds; 140 | ``` 141 | 142 | When we run the program now, we will find that we are largely back in the 143 | case where no sub-object bounds were applied: pointers to the sentinel 144 | have length `0x20` and pointers to list elements have length `0x30`. 145 | However, the `&co->val` pointers are still bounded: 146 | 147 | ``` 148 | Traversing list=0x104300 [rwRW,0x104300-0x104320] first=0x1040d0 [rwRW,0x1040c0-0x1040f0] lastnp=0x104140 [rwRW,0x104120-0x104150] 149 | Ilist cursor=0x1040d0 [rwRW,0x1040c0-0x1040f0] 150 | next=0x104130 [rwRW,0x104120-0x104150] 151 | prevnp=0x104310 [rwRW,0x104300-0x104320] 152 | val field at 0x1040c0 [rwRW,0x1040c0-0x1040f0] 153 | Ilist cursor=0x104130 [rwRW,0x104120-0x104150] 154 | next=0x104300 [rwRW,0x104300-0x104320] 155 | prevnp=0x1040e0 [rwRW,0x1040c0-0x1040f0] 156 | val field at 0x104120 [rwRW,0x104120-0x104150] 157 | Traversing list again, accessing superobject field... 158 | Ilist cursor=0x1040d0 [rwRW,0x1040c0-0x1040f0] value=1 (at 0x1040c0 [rwRW,0x1040c0-0x1040c4]) 159 | Ilist cursor=0x104130 [rwRW,0x104120-0x104150] value=3 (at 0x104120 [rwRW,0x104120-0x104124]) 160 | ``` 161 | 162 | 2. We can mark the `ilist` field of `struct obj`: 163 | ``` 164 | struct obj { 165 | int val; 166 | struct ilist_elem ilist __subobject_use_container_bounds; 167 | }; 168 | ``` 169 | 170 | In this case, we find that the `ile_next` pointers are offset and not 171 | bounded, while the `ile_prevnp` pointers are tightly bounded: 172 | ``` 173 | Traversing list=0x104340 [rwRW,0x104340-0x104360] first=0x104110 [rwRW,0x104100-0x104130] lastnp=0x104180 [rwRW,0x104180-0x104190] 174 | Ilist cursor=0x104110 [rwRW,0x104100-0x104130] 175 | next=0x104170 [rwRW,0x104160-0x104190] 176 | prevnp=0x104350 [rwRW,0x104350-0x104360] 177 | val field at 0x104100 [rwRW,0x104100-0x104130] 178 | Ilist cursor=0x104170 [rwRW,0x104160-0x104190] 179 | next=0x104340 [rwRW,0x104340-0x104360] 180 | prevnp=0x104120 [rwRW,0x104120-0x104130] 181 | val field at 0x104160 [rwRW,0x104160-0x104190] 182 | Traversing list again, accessing superobject field... 183 | Ilist cursor=0x104110 [rwRW,0x104100-0x104130] value=1 (at 0x104100 [rwRW,0x104100-0x104104]) 184 | Ilist cursor=0x104170 [rwRW,0x104160-0x104190] value=3 (at 0x104160 [rwRW,0x104160-0x104164]) 185 | ``` 186 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/README.md: -------------------------------------------------------------------------------- 1 | # CheriABI Showcase 2 | 3 | This exercise demonstrates several aspects of CheriABI, which defines how 4 | CheriBSD processes are constructed, how function calls are made, how a process 5 | interacts with the kernel through system calls, and other such foundational 6 | details. 7 | 8 | ## The Kernel Voluntarily Honors Capability Bounds 9 | 10 | `kern-read-over.c` demonstrates a (potential) loss of spatial safety when 11 | pointers are passed from userspace to the kernel. The kernel, by convention, 12 | has full access to all of userspace memory. Even when CheriBSD is running 13 | CheriABI programs, this is true: the kernel holds a capability with full RWX 14 | access to all userspace addresses. Therefore, the kernel can act as a 15 | *confused deputy*, accessing memory with its legitimate authority but without 16 | intent. 17 | 18 | 1. Compile `kern-read-over.c` for both the baseline (`kern-read-over-baseline`) 19 | and CHERI-enabled (`kern-read-over-cheri`) architectures. 20 | 21 | 2. Run these programs and observe their outputs. 22 | 23 | 3. Focusing on the `read()` system call, what happens in the two versions of the 24 | program. When, in particular, does it look like the CHERI version notices 25 | something is amiss? 26 | 27 | 4. If you have done the [inter-stack-object buffer overflow 28 | exercise](../buffer-overflow-stack), contrast the behaviors of the two 29 | CHERI-enabled programs. 30 | 31 | ## The Process Memory Map 32 | 33 | In most UNIX programs, the rights to manipulate the virtual memory map are 34 | *ambient*: any piece of code can change the virtual memory permissions 35 | associated with a page, `munmap` pages, or even request a replacement mapping 36 | ("fixed `mmap`") almost anywhere in the address space. This risks allowing 37 | evasion of CHERI capabilities' protection properties, as CHERI capabilities are 38 | interpreted in combination with the virtual memory map. 39 | 40 | Therefore, the CheriBSD kernel avails itself of a *software permission* bit in 41 | CHERI capabilities. Such permissions are not architecturally interpreted but 42 | are still subject to architectural protection (and so, for example, a zero 43 | permission bit may not be set to one without simultaneously clearing the 44 | capability tag). In particular, CheriBSD defines `CHERI_PERM_SW_VMEM`, sets 45 | this permission bit when returning pointers to *new* allocations of address 46 | space, and requires that capabilities passed to address-space-manipulating 47 | functions bear this permission. Userspace components are free to clear this 48 | permission when delegating access to address space. 49 | 50 | 1. Compile `perm-vmem.c` for both the baseline (`perm-vmem-baseline`) and 51 | CHERI-enabled (`perm-vmem-cheri`) architectures. 52 | 53 | 2. Run these programs and observe their outputs. The `printf` format strings 54 | for capabilities, `%p` and `%#p`, elide some usually-excessive details, and 55 | `CHERI_PERM_SW_VMEM` is generally regarded as one such. `gdb`'s 56 | pretty-printing, similarly. However, we can programmatically extract the 57 | permissions field and display it. 58 | 59 | 3. Modify `perm-vmem.c` to verify that `madvise(MADV_FREE)` and 60 | `mmap(MAP_FIXED)` also are permitted for the capability returned directly 61 | from `mmap` but are not permitted for the heap-derived pointer. 62 | 63 | ## (Extra Credit!) Initial Process Construction 64 | 65 | We have largely focused on program behavior *after* it has been loaded and is 66 | running. Let us look in a little more detail at some aspects of the initial 67 | construction. While modern ELF loading is well beyond the scope of this 68 | document, and is perhaps best summarized as "here be dragons", we can 69 | nevertheless take a quick glance at some interesting features of CheriABI 70 | startup. 71 | 72 | 1. Compile `print-more.c` for both the baseline (`print-more-baseline`) and the 73 | CHERI-enabled (`print-more-cheri`) architectures. 74 | 75 | 2. Run both these programs and observe their outputs. As might be predicted, 76 | the CHERI version reports a wide variety of capabilities to different parts 77 | of the address space. Run both programs several times; what do you observe? 78 | 79 | Let us examine several interesting aspects of the reported capabilities. 80 | 81 | 3. Launch `gdb ./print-more-cheri` and have it start the program and stop before 82 | running any instructions, with `starti`. Where do we find ourselves? 83 | 84 | 4. Use `info inferiors` to obtain the child process identifier (PID) and 85 | `!procstat vm NNN` (replacing `NNN` with the child PID) to show the initial 86 | address space arranged by the kernel. 87 | 88 | Which of these initial mappings are targeted by the values reported for 89 | `&rodata_const`, `&relro_ptr`, `&rw_ptr` and `printf` in step 2? What are 90 | the permissions for these mappings? 91 | 92 | 5. Just because the page mappings exist, however, CHERI programs need to have 93 | capabilities installed to access them. Here at the very beginning of a 94 | process's life, we are in a good position to see the *root capabilities* 95 | that the kernel makes available. Use `info registers` to see the initial 96 | contents of the register file. 97 | 98 | 6. Let's begin our tour with `csp`, the capability stack pointer register. 99 | 100 | First, what may strike you as surprising (and why) about the stack pointer 101 | being replaced by a capability? 102 | 103 | Second, compare the address space map obtained above with the current `csp` 104 | value; what has the kernel arranged to "back" the region of address space 105 | within stack bounds? 106 | 107 | If you are familiar with [Stack Clash 108 | Vulnerabilities](https://blog.qualys.com/vulnerabilities-threat-research/2017/06/19/the-stack-clash), 109 | explain how the two aspects above work in tandem to mitigate this class of 110 | vulnerability. 111 | 112 | Third, contrast the relative order of `&argv[0]` and `&stack_local` as 113 | reported on the two different architectures in step 2 above. 114 | 115 | 7. Having access to the stack is all well and good, but surely there is more to 116 | a process than that. At the beginning of a CheriABI process's life, the 117 | capability in `ca0` (the first "argument register") points to the "auxiliary 118 | vector", an array of `Elf_Auxinfo` structures constructed by the kernel. 119 | 120 | `gdb` can ask the kernel for, and display, the information in the auxiliary 121 | vector with `info auxv`. However, the pretty-printer is not capability 122 | aware, so let's also directly spelunk the structure. Use some `gdb` 123 | scripting to print out the auxiliary vector in its entirety: 124 | ``` 125 | set $i = 0 126 | while(((Elf_Auxinfo *)$ca0)[$i].a_type != 0) 127 | p ((Elf_Auxinfo *)$ca0)[$i] 128 | set $i = $i + 1 129 | end 130 | ``` 131 | Use the more human-friendly `info auxv` to interpret the `a_type` values. 132 | 133 | In addition to the `AT_ARGV` value we have already (indirectly) seen above, 134 | there are many other capabilities to nearby parts of the address space, 135 | including the initial environment vector (`AT_ENVV`) and the executable path 136 | (`AT_EXECPATH`). 137 | 138 | More usefully, however, 139 | 140 | - `AT_PHDR` supplies a read/write capability to the loaded executable. 141 | 142 | - `AT_ENTRY` supplies a read/execute capability to the loaded executable, 143 | pointed at its entrypoint. 144 | 145 | - `AT_BASE` supplies a full read/write/execute capability to the program's 146 | "interpreter" (dynamic loader). The elevated permissions here allow the 147 | loader to (relatively) easily relocate *itself* early in execution. 148 | 149 | From which of these capabilities are the displayed values of `&rodata_const`, 150 | `&relro_ptr`, and `&rw_ptr` from step 2 sourced? What permissions have been 151 | shed in the derivation? How do these permissions differ from those of the 152 | underlying page mappings? 153 | 154 | 8. The displayed value for `printf` is tagged as being a `(sentry)`. 155 | Modify the program to attempt to display the result of computing either 156 | 157 | - `*(char *)(printf)` or 158 | - `(void*)((uintptr_t)printf + 1)`. 159 | 160 | Compile and run this modified version (or both). What happens? 161 | 162 | Sentry (short for "Sealed Entry") capabilities are a special form of 163 | capabilities: they are *immutable* and *inert*, conveying to the bearer no 164 | authority to the target, until they become the program counter, at which 165 | point they are unsealed into being an ordinary capability. Thus, we can 166 | neither read through nor mutate our handle to `printf`, yet we can jump to 167 | it. 168 | 169 | If you are familiar with [Return Oriented 170 | Programming](https://hovav.net/ucsd/dist/geometry.pdf) and [Jump Oriented 171 | Programming](https://www.csc2.ncsu.edu/faculty/xjiang4/pubs/ASIACCS11.pdf), 172 | you may wish to consider the cumulative challenge added by CHERI's 173 | architectural provenance requirement combined with pervasive use of sentry 174 | capabilities for dynamically resolved symbols. 175 | 176 | ## Source 177 | 178 | **kern-read-over.c** 179 | ```C 180 | {{#include kern-read-over.c}} 181 | ``` 182 | 183 | **perm-vmem.c** 184 | ```C 185 | {{#include perm-vmem.c}} 186 | ``` 187 | 188 | **print-more.c** 189 | ```C 190 | {{#include print-more.c}} 191 | ``` 192 | 193 | ## Courseware 194 | 195 | This exercise has [presentation materials](./cheriabi.pptx) available. 196 | -------------------------------------------------------------------------------- /src/exercises/adapt-c/answers.md: -------------------------------------------------------------------------------- 1 | # Answers - Adapt a C Program to CHERI C 2 | 3 | 2. Example output: 4 | ``` 5 | # ./tools/ccc riscv64 -o /build/cat-baseline ./src/exercises/adapt-c/cat/cat.c ./src/exercises/adapt-c/cat/methods.c 6 | Running: /output/sdk/bin/clang -target riscv64-unknown-freebsd -march=rv64gc -mabi=lp64d -mno-relax --sysroot=/output/sdk/sysroot-riscv64-purecap -g -O2 -fuse-ld=lld -Wall -Wcheri -o /build/cat-cheri ./src/exercises/adapt-c/cat/cat.c ./src/exercises/adapt-c/cat/methods.c 7 | # ./tools/ccc riscv64-purecap -o /build/cat-cheri ./src/exercises/adapt-c/cat/cat.c ./src/exercises/adapt-c/cat/methods.c 8 | Running: /output/sdk/bin/clang -target riscv64-unknown-freebsd -march=rv64gcxcheri -mabi=l64pc128d -mno-relax --sysroot=/output/sdk/sysroot-riscv64-purecap -g -O2 -fuse-ld=lld -Wall -Wcheri -o /build/cat-cheri ./src/exercises/adapt-c/cat/cat.c ./src/exercises/adapt-c/cat/methods.c 9 | ./src/exercises/adapt-c/cat/methods.c:70:43: warning: binary expression on capability types 'ptroff_t' (aka 'unsigned __intcap') and 'uintptr_t' (aka 'unsigned __intcap'); it is not clear which should be used as the source of provenance; currently provenance is inherited from the left-hand side [-Wcheri-provenance] 10 | return (write(fildes, (const void *)(off + (uintptr_t)buf), nbyte)); 11 | ~~~ ^ ~~~~~~~~~~~~~~ 12 | ./src/exercises/adapt-c/cat/methods.c:80:7: warning: cast from provenance-free integer type to pointer type will give pointer that can not be dereferenced [-Wcheri-capability-misuse] 13 | fp = (FILE *)file; 14 | ^ 15 | 2 warnings generated. 16 | ``` 17 | 18 | 19 | 3. Example output: 20 | ``` 21 | # ./cat-baseline /etc/hostid 22 | bb5fbb47-10ab-11ec-a609-f5a47707c223 23 | # ./cat-baseline -n /etc/hostid 24 | 1 bb5fbb47-10ab-11ec-a609-f5a47707c223 25 | # ./cat-cheri /etc/hostid 26 | cat-cheri: write(2) failed: Bad address 27 | # ./cat-cheri -n /etc/hostid 28 | In-address space security exception (core dumped) 29 | # 30 | ``` 31 | 32 | 33 | 4. When run without `gdb`, `cat-cheri` prints: 34 | ``` 35 | # ./cat-cheri /etc/hostid 36 | cat-cheri: write(2) failed: Bad address 37 | ``` 38 | Looking at the source code, we can see there is only one call to write(2): 39 | ``` 40 | # grep -R write src/exercises/adapt-c/cat 41 | src/exercises/adapt-c/cat/methods.c:write_off(int fildes, const void *buf, ptroff_t off, size_t nbyte) 42 | src/exercises/adapt-c/cat/methods.c: return (write(fildes, (const void *)(off + (uintptr_t)buf), nbyte)); 43 | src/exercises/adapt-c/cat/methods.c: if ((nw = write_off(wfd, buf, off, (size_t)nr)) < 0) 44 | src/exercises/adapt-c/cat/methods.c: err(1, "write(2) failed"); 45 | ``` 46 | The call is in the `write_off()` function and the message with the error is 47 | printed in case the call fails. Let's see what are arguments and result 48 | values for the system call by setting a breakpoint for `write()` in `gdb`: 49 | ``` 50 | # gdb ./cat-cheri 51 | (...) 52 | (gdb) b write 53 | Function "write" not defined. 54 | Make breakpoint pending on future shared library load? (y or [n]) y 55 | Breakpoint 1 (write) pending. 56 | (gdb) r /etc/hostid 57 | Starting program: /root/cat-cheri /etc/hostid 58 | 59 | Breakpoint 1, write (fd=, buf=, nbytes=) at /usr/home/john/work/cheri/git/cheribsd/lib/libc/sys/write.c:48 60 | 48 __libc_interposing[INTERPOS_write])(fd, buf, nbytes)); 61 | ``` 62 | Even though the debugger believes that the function arguments are 63 | optimized out, from the CHERI-RISC-V calling conventions we know that 64 | the arguments are in the ca0, ca1, and ca2 registers: 65 | ``` 66 | (gdb) info registers ca0 ca1 ca2 67 | ca0 0x1 0x1 68 | ca1 0x40802000 0x40802000 69 | ca2 0x25 0x25 70 | (gdb) disassemble 71 | Dump of assembler code for function write: 72 | => 0x000000004027fa98 <+0>: auipcc ca3,0xb7 73 | 0x000000004027fa9c <+4>: clc ca3,-424(ca3) 74 | 0x000000004027faa0 <+8>: clc ca5,496(ca3) 75 | 0x000000004027faa4 <+12>: cjr ca5 76 | End of assembler dump. 77 | ``` 78 | We can see that `write()` was called to write to `stdout` (`ca0`) 37 bytes 79 | (`ca2`) from a buffer with an untagged capability (`ca1`). The `write()` libc 80 | function does not include a trapping instruction but it jumps with `cjr`. 81 | Let's see where it jumps to: 82 | ``` 83 | (gdb) ni 4 84 | _write () at _write.S:4 85 | 4 _write.S: No such file or directory. 86 | (gdb) disassemble 87 | Dump of assembler code for function _write: 88 | => 0x0000000040282f40 <+0>: li t0,4 89 | 0x0000000040282f42 <+2>: ecall 90 | 0x0000000040282f46 <+6>: bnez t0,0x40282f4e <_write+14> 91 | 0x0000000040282f4a <+10>: cret 92 | 0x0000000040282f4e <+14>: auipcc ct1,0xffffd 93 | 0x0000000040282f52 <+18>: cincoffset ct1,ct1,-1166 94 | 0x0000000040282f56 <+22>: cjr ct1 95 | End of assembler dump. 96 | ``` 97 | `write()` jumped to `_write()`, a system call wrapper written in assembly, 98 | that uses the `ecall` instruction to make a system call. Let's see what is 99 | its result: 100 | ``` 101 | (gdb) ni 2 102 | 4 in _write.S 103 | (gdb) info registers ca0 ct0 104 | ca0 0xe 0xe 105 | ct0 0x1 0x1 106 | (gdb) 107 | ``` 108 | The `write()` system call failed as the kernel set `ct0` to `0x1` and 109 | returned errno `0xe` in `ca0`. Looking at `errno(2)` and `write(2)`, we can 110 | conclude that we passed an incorrect address to the buffer. It is likely 111 | here because the capability is just the address, without a tag. 112 | 113 | 114 | 5. When compiling `cat-cheri`, the compiler printed: 115 | ``` 116 | ./src/exercises/adapt-c/cat/methods.c:70:43: warning: binary expression on capability types 'ptroff_t' (aka 'unsigned __intcap') and 'uintptr_t' (aka 'unsigned __intcap'); it is not clear which should be used as the source of provenance; currently provenance is inherited from the left-hand side [-Wcheri-provenance] 117 | return (write(fildes, (const void *)(off + (uintptr_t)buf), nbyte)); 118 | ~~~ ^ ~~~~~~~~~~~~~~ 119 | ``` 120 | As the [CHERI C/C++ Programming Guide](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf) 121 | says in Section 4.2.3: 122 | > In the CHERI memory protection model, capabilities are derived from 123 | a single other capability. 124 | 125 | In our case, `off + (uintptr_t)buf` resulted in an untagged capability 126 | because `off` holds an integer value in an untagged capability and, as the 127 | compiler warns, it is used to create a resulting capability. 128 | In order to create a capability using the correct source capability, we can 129 | tell the compiler that `off` does not hold a valid address with a cast: 130 | ``` 131 | diff --git a/src/exercises/adapt-c/cat/methods.c b/src/exercises/adapt-c/cat/methods.c 132 | index bb78a75..6520735 100644 133 | --- a/src/exercises/adapt-c/cat/methods.c 134 | +++ b/src/exercises/adapt-c/cat/methods.c 135 | @@ -67,7 +67,7 @@ static ssize_t 136 | write_off(int fildes, const void *buf, ptroff_t off, size_t nbyte) 137 | { 138 | 139 | - return (write(fildes, (const void *)(off + (uintptr_t)buf), nbyte)); 140 | + return (write(fildes, (const void *)((size_t)off + (uintptr_t)buf), nbyte)); 141 | } 142 | 143 | static void 144 | ``` 145 | 146 | 6. Example output: 147 | ``` 148 | # gdb-run.sh ./cat-cheri -n /etc/hostid 149 | (...) 150 | Starting program: /root/cat-cheri -n /etc/hostid 151 | 152 | Program received signal SIGPROT, CHERI protection violation. 153 | Capability tag fault caused by register cs4. 154 | verbose_cat (file=) at cat/methods.c:87 155 | 87 for (prev = '\n'; (ch = getc(fp)) != EOF; prev = ch) { 156 | 157 | Thread 1 (LWP 100061 of process 2694): 158 | #0 verbose_cat (file=) at cat/methods.c:87 159 | #1 do_cat (file=, verbose=) at cat/methods.c:214 160 | #2 0x0000000000102dca in scanfiles (argv=0x3fbfdff7c0 [rwRW,0x3fbfdff7a0-0x3fbfdff7e0], verbose=) at cat/cat.c:172 161 | #3 0x0000000000102c52 in main (argc=3, argv=0x0) at cat/cat.c:128 162 | ``` 163 | `gdb` says that `cs4` triggered a CHERI exception: 164 | ``` 165 | (gdb) info register cs4 166 | cs4 0x403545d0 0x403545d0 167 | ``` 168 | `cs4` holds an untagged capability and the program tries to load a word using 169 | `cs4` which violates CHERI restrictions: 170 | ``` 171 | (gdb) disassemble $pcc,+4 172 | Dump of assembler code from 0x102f7e to 0x102f82: 173 | => 0x0000000000102f7e : clw a0,16(cs4) 174 | End of assembler dump. 175 | ``` 176 | Looking at the values of local variables, we can see that `cs4` holds the 177 | value of the `fp` variable: 178 | ``` 179 | (gdb) info locals 180 | fp = 0x403545d0 181 | gobble = 0 182 | line = 0 183 | prev = 10 184 | ch = 185 | wch = 186 | ``` 187 | It means that for some reason `fp` holds an invalid (NULL-derived) 188 | capability. 189 | 190 | 191 | 7. When compiling `cat-cheri`, the compiler printed: 192 | ``` 193 | ./src/exercises/adapt-c/cat/methods.c:80:7: warning: cast from provenance-free integer type to pointer type will give pointer that can not be dereferenced [-Wcheri-capability-misuse] 194 | fp = (FILE *)file; 195 | ^ 196 | ``` 197 | As the [CHERI C/C++ Programming Guide](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf) 198 | says in Section 4.2: 199 | > (...) only pointers implemented using valid capabilities can be dereferenced. 200 | 201 | and in Section 4.2.1: 202 | 203 | > `int`,`int32_t`,`long`,`int64_t`,... These pure integer types should be 204 | used to hold integer values that will never be cast to a pointer type without 205 | first combining them with another pointer value – e.g., by using them as an 206 | array offset. 207 | 208 | In our case, `long` is cast to a pointer type which results in a NULL-derived 209 | capability without a tag, with an address set to an integer value, and which 210 | cannot be dereferenced. We can fix this bug by using a data type that can 211 | hold both integer values and pointers - `uintptr_t`: 212 | ``` 213 | diff --git a/src/exercises/adapt-c/cat/cat.c b/src/exercises/adapt-c/cat/cat.c 214 | index 344e505..54cc864 100644 215 | --- a/src/exercises/adapt-c/cat/cat.c 216 | +++ b/src/exercises/adapt-c/cat/cat.c 217 | @@ -166,10 +166,10 @@ scanfiles(char *argv[], int verbose) 218 | rval = 1; 219 | } else if (verbose) { 220 | if (fd == STDIN_FILENO) 221 | - do_cat((long)stdin, verbose); 222 | + do_cat((uintptr_t)stdin, verbose); 223 | else { 224 | fp = fdopen(fd, "r"); 225 | - do_cat((long)fp, verbose); 226 | + do_cat((uintptr_t)fp, verbose); 227 | fclose(fp); 228 | } 229 | } else { 230 | diff --git a/src/exercises/adapt-c/cat/cat.h b/src/exercises/adapt-c/cat/cat.h 231 | index c88f930..047c0b7 100644 232 | --- a/src/exercises/adapt-c/cat/cat.h 233 | +++ b/src/exercises/adapt-c/cat/cat.h 234 | @@ -51,6 +51,6 @@ 235 | 236 | #define SUPPORTED_FLAGS "belnstuv" 237 | 238 | -void do_cat(long file, int verbose); 239 | +void do_cat(uintptr_t file, int verbose); 240 | 241 | #endif /* !_CAT_H_ */ 242 | diff --git a/src/exercises/adapt-c/cat/methods.c b/src/exercises/adapt-c/cat/methods.c 243 | index bb78a75..afe29d3 100644 244 | --- a/src/exercises/adapt-c/cat/methods.c 245 | +++ b/src/exercises/adapt-c/cat/methods.c 246 | @@ -71,7 +71,7 @@ write_off(int fildes, const void *buf, ptroff_t off, size_t nbyte) 247 | } 248 | 249 | static void 250 | -verbose_cat(long file) 251 | +verbose_cat(uintptr_t file) 252 | { 253 | FILE *fp; 254 | int ch, gobble, line, prev; 255 | @@ -166,7 +166,7 @@ ilseq: 256 | } 257 | 258 | static void 259 | -raw_cat(long file) 260 | +raw_cat(uintptr_t file) 261 | { 262 | long pagesize; 263 | int off, rfd, wfd; 264 | @@ -207,7 +207,7 @@ raw_cat(long file) 265 | } 266 | 267 | void 268 | -do_cat(long file, int verbose) 269 | +do_cat(uintptr_t file, int verbose) 270 | { 271 | 272 | if (verbose) { 273 | ``` 274 | 275 | 276 | 8. The first bug resulted in a system call error because there was no capability 277 | operation on an invalid capability. An operating system could not copy memory 278 | from the user address space because it checked if a user process passed an 279 | invalid capability and returned an error. 280 | 281 | The second bug resulted in a CHERI exception because an invalid capability 282 | was used to load a word from memory. 283 | 284 | 285 | For more information on the C/C++ programming languages, CHERI compiler warnings 286 | and errors, we recommend to read the 287 | [CHERI C/C++ Programming Guide](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf). 288 | -------------------------------------------------------------------------------- /src/exercises/cheriabi/answers.md: -------------------------------------------------------------------------------- 1 | # Answers 2 | 3 | ## The Process Memory Map 4 | 5 | 2. Example output from a baseline architecture: 6 | ``` 7 | Directly mapped page at p=0x84dc0000 8 | Punching hole in the heap at p=0x83b48000 9 | Done 10 | ``` 11 | 12 | And from a CHERI-enabled architecture: 13 | ``` 14 | Directly mapped page at p=0x40139000 [rwRW,0x40139000-0x4013a000] 15 | p.perms=0x7817d 16 | Punching hole in the heap at p=0x407d1000 [rwRW,0x407d1000-0x407d3000] 17 | p.perms=0x6817d 18 | munmap failed: Memory protection violation 19 | Done 20 | ``` 21 | 22 | 3. This amounts to adding calls to `madvise(p, 4096, MADV_FREE)` and 23 | `mmap(p, 4096, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE|MAP_ANON, -1, 0)` 24 | in the right places and verifying that the operations return appropriately. 25 | Additionally, you may wish to check `errno` in failure cases and the 26 | *contents* of memory after `mmap` to ensure that it has, indeed, behaved as 27 | expected in both cases. 28 | 29 | ## The Kernel as a Potentially Confused Deputy 30 | 31 | 2. Example output from a baseline architecture: 32 | ``` 33 | Write OK 34 | lower=0x80922400 upper=0x80922410 35 | Read 0x20 OK; lower[0]=0x10 upper[0]=0x20 36 | ``` 37 | 38 | And from a CHERI-enabled architecture: 39 | ``` 40 | Write OK 41 | lower=0x3fffdfff28 upper=0x3fffdfff38 42 | Bad read (Bad address); lower[0]=0x10 upper[0]=0x0 43 | ``` 44 | 45 | 3. On the baseline architecture, the kernel dutifully writes 0x20 bytes to the 46 | target address, regardless of the C object model. On the CHERI architecture, 47 | the kernel acts *intentionally* with the capability provided by userspace and 48 | so encounters a trap when copying bytes out to userspace. Because the kernel 49 | updates `lower[0]`, we can conjecture that the kernel is not performing 50 | explicit bounds checks but is rather operating under a trust-but-validate 51 | model, handling the architectural trap when it uses `copyout()` to copy bytes 52 | from its internal pipe buffer to the indicated userspace buffer. 53 | 54 | 4. Not all capability faults are fatal! While the [inter-stack-object overflow 55 | exercise](../buffer-overflow-stack) let the program die of the `SIGPROT` 56 | thrown its way, here, the kernel maps the architectural trap to a *failure 57 | return* rather than a fatal signal. 58 | 59 | ## (Extra Credit!) Initial Process Construction 60 | 61 | 2. Example output from a baseline architecture: 62 | ``` 63 | &argv[0]=0x806e0d08 64 | argv[0]=0x806e0fd0 65 | &stack_local=0x806e0c94 66 | &rodata_const=0x105c0 67 | &relro_ptr=0x105c8 68 | &rw_ptr=0x13e18 69 | printf=0x11c60 70 | ``` 71 | 72 | And from a CHERI-enabled architecture: 73 | ``` 74 | &argv[0]=0x3fbfdff8d0 [rwRW,0x3fbfdff8d0-0x3fbfdff8f0] 75 | argv[0]=0x3fbfdffe30 [rwRW,0x3fbfdffe30-0x3fbfdffe4a] 76 | &stack_local=0x3fffdfff6c [rwRW,0x3fffdfff6c-0x3fffdfff70] 77 | &rodata_const=0x1005c8 [rR,0x1005c8-0x1005cc] 78 | &relro_ptr=0x102f00 [rR,0x102f00-0x102f10] 79 | &rw_ptr=0x104070 [rwRW,0x104070-0x104080] 80 | printf=0x402608d8 [rxR,0x4013a000-0x40782000] (sentry) 81 | ``` 82 | 83 | Running the baseline version multiple times should produce different output 84 | thanks to Address Space Layout Randomization (ASLR), a popular probabilistic 85 | countermeasure against pointer forgery. Because CHERI offers deterministic 86 | protection against pointer forgery by its very nature, ASLR for CheriABI 87 | programs is turned off. 88 | 89 | 3. Example session: 90 | ``` 91 | (gdb) starti 92 | Starting program: /buildroot/print-more-cheri 93 | 94 | Program stopped. 95 | rtld_start () at /cheri/source/mainline/cheribsd/libexec/rtld-elf/riscv/rtld_start.S:62 96 | 62 /cheri/source/mainline/cheribsd/libexec/rtld-elf/riscv/rtld_start.S: No such file or directory. 97 | ``` 98 | 99 | We find ourselves nowhere within `print-more-cheri` but, rather, at the very 100 | beginning of the *dynamic loader* (`rtld`). 101 | 102 | 4. Example session for CHERI: 103 | ``` 104 | (gdb) info inferiors 105 | Num Description Executable 106 | * 1 process 1013 /buildroot/print-more-cheri 107 | (gdb) !procstat vm 1013 108 | PID START END PRT RES PRES REF SHD FLAG TP PATH 109 | 1013 0x100000 0x101000 r--R- 1 3 3 0 CN--- vn /buildroot/print-more-cheri 110 | 1013 0x101000 0x102000 r-xR- 1 3 3 0 CN--- vn /buildroot/print-more-cheri 111 | 1013 0x102000 0x104000 rw-RW 2 3 3 0 CN--- vn /buildroot/print-more-cheri 112 | 1013 0x104000 0x105000 rw-RW 1 1 1 0 ----- df 113 | 1013 0x40104000 0x4010f000 r--R- 11 344 32 0 CN--- vn /libexec/ld-elf.so.1 114 | 1013 0x4010f000 0x4012a000 r-xR- 27 0 1 0 C---- vn /libexec/ld-elf.so.1 115 | 1013 0x4012a000 0x4012b000 rw-RW 1 344 32 0 CN--- vn /libexec/ld-elf.so.1 116 | 1013 0x4012b000 0x4012d000 rw-RW 2 344 32 0 CN--- vn /libexec/ld-elf.so.1 117 | 1013 0x4012d000 0x4012f000 rw-RW 1 1 1 0 ----- df 118 | 1013 0x3fbfd80000 0x3fbfe00000 rw-RW 1 1 1 0 ----- df 119 | 1013 0x3fbfe00000 0x3fffde0000 ----- 0 0 0 0 G---- gd 120 | 1013 0x3fffde0000 0x3fffe00000 rw-RW 0 0 0 0 ---D- -- 121 | 1013 0x3ffffff000 0x4000000000 r-x-- 1 1 13 0 ----- ph 122 | ``` 123 | 124 | | pointer | mapping | permissions | 125 | | ------- | ------- | ----------- | 126 | | `&argv[0]` | `0x3fbfd80000` | `rw-RW` | 127 | | `argv` | " | " | 128 | | `stack_local` | `0x3fffde0000` | `rw-RW` | 129 | | `rodata_const` | `0x100000` | `r--R-` | 130 | | `relro_ptr` | `0x102000` | `rw-RW` | 131 | | `rw_ptr` | `0x104000` | `rw-RW` | 132 | | `printf` | not initially mapped | n/a | 133 | 134 | 5. Abridged output: 135 | ``` 136 | cra 0xd117200009e18201000000004010f040 0x4010f040 [rxR,0x40104000-0x4012f000] (sentry) 137 | csp 0xd17d000003ff2ffe0000003fffe00000 0x3fffe00000 [rwRW,0x3fbfe00000-0x3fffe00000] 138 | ca0 0xd17d00000785b9b40000003fbfdff9b0 0x3fbfdff9b0 [rwRW,0x3fbfdff9b0-0x3fbfdffe10] 139 | pcc 0x4010f040 0x4010f040 140 | ddc 0x0 0x0 141 | ``` 142 | 143 | 6. In baseline programs, the stack is bounded only by operating system measures 144 | -- the kernel will refuse to grow what it considers to be "the stack" beyond 145 | some limit. However, architecturally, there is no *a priori* limit to stack 146 | growth. In CHERI, by contrast, the stack is accessed via a capability, which 147 | must be constructed up front. 148 | 149 | For the CHERI program, the kernel has backed the entirety of stack memory 150 | with page mappings: 151 | ``` 152 | 1013 0x3fbfe00000 0x3fffde0000 ----- 0 0 0 0 G---- gd 153 | 1013 0x3fffde0000 0x3fffe00000 rw-RW 0 0 0 0 ---D- -- 154 | ``` 155 | The latter of these is marked as "growing down" (the `D` in the penultimate 156 | field) while the former is considered a "guard" mapping (the `G` flag and 157 | `gd` type), serving to prevent any other claimant to the address space. 158 | 159 | The initial bounds on the stack capability prevent half of Stack Clash: the 160 | stack capability cannot authorize access to a heap region, even if, say, 161 | indexing an on-stack array goes very far out of bounds. The primordial guard 162 | entry serves to prevent the second half: the heap cannot grow into the stack, 163 | because the kernel will not use that address space to satisfy `mmap` requests 164 | (and, moreover, no capability held by userspace, including the one in `csp`, 165 | bears `CHERI_PERM_SW_VMEM`, so the stack or its guard cannot be torn down). 166 | 167 | Traditionally, `argv` and its contents (as well as the `environ`ment vector 168 | and indeed the entire auxiliary vector) is placed above the initial stack 169 | pointer, so `&argv[0]` is above `&stack_local`. However, here we can see 170 | that CheriBSD chooses to locate all this initial data *below* the stack 171 | reservation, meaning that `&stack_local` is further up the address space than 172 | `&argv[0]`. This allows the kernel to ensure that parts of this initial 173 | state are immutable or that there exists exactly one capability to parts of 174 | the structure (allowing for easier reasoning about capability flow in 175 | userspace); these would not be true if this initial data were also reachable 176 | through the stack capability. 177 | 178 | 7. Example session: 179 | ``` 180 | (gdb) info auxv 181 | 3 AT_PHDR Program headers for program 0x100040 182 | 4 AT_PHENT Size of program header entry 56 183 | 5 AT_PHNUM Number of program headers 11 184 | 6 AT_PAGESZ System page size 4096 185 | 8 AT_FLAGS Flags 0x0 186 | 9 AT_ENTRY Entry point of program 0x101a30 187 | 7 AT_BASE Base address of interpreter 0x40104000 188 | 24 AT_EHDRFLAGS ELF header e_flags 0x30005 189 | 15 AT_EXECPATH Executable path 0x3fbfdfffa0 "/mnt/tmp/print-more-cheri" 190 | 18 AT_OSRELDATE OSRELDATE 1400051 191 | 16 AT_CANARY Canary for SSP 0x3fbfdfff60 192 | 17 AT_CANARYLEN Length of the SSP canary 64 193 | 19 AT_NCPUS Number of CPUs 1 194 | 20 AT_PAGESIZES Pagesizes 0x3fbfdfff40 195 | 21 AT_PAGESIZESLEN Number of pagesizes 24 196 | 22 AT_TIMEKEEP Pointer to timehands 0x3ffffff020 197 | 23 AT_STACKPROT Initial stack protection 0x3 198 | 25 AT_HWCAP Machine-dependent CPU capability hints 0x112d 199 | 27 AT_BSDFLAGS ELF BSD flags 0x0 200 | 28 AT_ARGC Argument count 1 201 | 29 AT_ARGV Argument vector 0x3fbfdff880 202 | 30 AT_ENVC Environment count 16 203 | 31 AT_ENVV Environment vector 0x3fbfdff8a0 204 | 32 AT_PS_STRINGS Process strings 0x3fbfdfffc0 205 | 0 AT_NULL End of vector 0x0 206 | (gdb) set $i = 0 207 | (gdb) while(((Elf_Auxinfo *)$ca0)[$i].a_type != 0) 208 | > p ((Elf_Auxinfo *)$ca0)[$i] 209 | > set $i = $i + 1 210 | > end 211 | $1 = {a_type = 3, a_un = {a_val = 1048640, a_ptr = 0x100040 [rwRW,0x100000-0x104260], a_fcn = 0x100040 [rwRW,0x100000-0x104260]}} 212 | $2 = {a_type = 4, a_un = {a_val = 56, a_ptr = 0x38, a_fcn = 0x38}} 213 | $3 = {a_type = 5, a_un = {a_val = 11, a_ptr = 0xb, a_fcn = 0xb}} 214 | $4 = {a_type = 6, a_un = {a_val = 4096, a_ptr = 0x1000, a_fcn = 0x1000}} 215 | $5 = {a_type = 8, a_un = {a_val = 0, a_ptr = 0x0, a_fcn = 0x0}} 216 | $6 = {a_type = 9, a_un = {a_val = 1055280, a_ptr = 0x101a30 <_start> [rxR,0x100000-0x104260], a_fcn = 0x101a30 <_start> [rxR,0x100000-0x104260]}} 217 | $7 = {a_type = 7, a_un = {a_val = 1074806784, a_ptr = 0x40104000 [rwxRW,0x40104000-0x4012f000], a_fcn = 0x40104000 [rwxRW,0x40104000-0x4012f000]}} 218 | $8 = {a_type = 24, a_un = {a_val = 196613, a_ptr = 0x30005, a_fcn = 0x30005}} 219 | $9 = {a_type = 15, a_un = {a_val = 273802067872, a_ptr = 0x3fbfdfffa0 [rwRW,0x3fbfdfffa0-0x3fbfdfffba], a_fcn = 0x3fbfdfffa0 [rwRW,0x3fbfdfffa0-0x3fbfdfffba]}} 220 | $10 = {a_type = 18, a_un = {a_val = 1400051, a_ptr = 0x155cf3, a_fcn = 0x155cf3}} 221 | $11 = {a_type = 16, a_un = {a_val = 273802067808, a_ptr = 0x3fbfdfff60 [rwRW,0x3fbfdfff60-0x3fbfdfffa0], a_fcn = 0x3fbfdfff60 [rwRW,0x3fbfdfff60-0x3fbfdfffa0]}} 222 | $12 = {a_type = 17, a_un = {a_val = 64, a_ptr = 0x40, a_fcn = 0x40}} 223 | $13 = {a_type = 19, a_un = {a_val = 1, a_ptr = 0x1, a_fcn = 0x1}} 224 | $14 = {a_type = 20, a_un = {a_val = 273802067776, a_ptr = 0x3fbfdfff40 [rwRW,0x3fbfdfff40-0x3fbfdfff58], a_fcn = 0x3fbfdfff40 [rwRW,0x3fbfdfff40-0x3fbfdfff58]}} 225 | $15 = {a_type = 21, a_un = {a_val = 24, a_ptr = 0x18, a_fcn = 0x18}} 226 | $16 = {a_type = 22, a_un = {a_val = 274877902880, a_ptr = 0x3ffffff020 [rwRW,0x3ffffff020-0x3ffffff190], a_fcn = 0x3ffffff020 [rwRW,0x3ffffff020-0x3ffffff190]}} 227 | $17 = {a_type = 23, a_un = {a_val = 3, a_ptr = 0x3, a_fcn = 0x3}} 228 | $18 = {a_type = 25, a_un = {a_val = 4397, a_ptr = 0x112d, a_fcn = 0x112d}} 229 | $19 = {a_type = 27, a_un = {a_val = 0, a_ptr = 0x0, a_fcn = 0x0}} 230 | $20 = {a_type = 28, a_un = {a_val = 1, a_ptr = 0x1, a_fcn = 0x1}} 231 | $21 = {a_type = 29, a_un = {a_val = 273802066048, a_ptr = 0x3fbfdff880 [rwRW,0x3fbfdff880-0x3fbfdff8a0], a_fcn = 0x3fbfdff880 [rwRW,0x3fbfdff880-0x3fbfdff8a0]}} 232 | $22 = {a_type = 30, a_un = {a_val = 16, a_ptr = 0x10, a_fcn = 0x10}} 233 | $23 = {a_type = 31, a_un = {a_val = 273802066080, a_ptr = 0x3fbfdff8a0 [rwRW,0x3fbfdff8a0-0x3fbfdff9b0], a_fcn = 0x3fbfdff8a0 [rwRW,0x3fbfdff8a0-0x3fbfdff9b0]}} 234 | $24 = {a_type = 32, a_un = {a_val = 273802067904, a_ptr = 0x3fbfdfffc0 [rwRW,0x3fbfdfffc0-0x3fbfe00000], a_fcn = 0x3fbfdfffc0 [rwRW,0x3fbfdfffc0-0x3fbfe00000]}} 235 | ``` 236 | 237 | `rodata_const` and `relro_ptr` could each be derived from either `AT_PHDR` 238 | (shedding write permission) or `AT_BASE` (shedding execute); despite that the 239 | pages are mapped read-write, the capability permissions will enforce that 240 | they cannot be used to modify the values here. If these are the only 241 | (non-TCB) capabilities to those locations, then the values must, indeed, be 242 | constant (outside bugs in the TCB). 243 | 244 | `rw_ptr` must, on the other hand, have come from `AT_PHDR`. 245 | 246 | 8. In either case, the program will trap and abort (`In-address space security 247 | exception`). 248 | 249 | Sentries are believed to complicate would-be ROP or JOP attacks without 250 | excessively complicating the architecture or system software. Without, it 251 | would be possible to locate and invoke gadgets within resolvable functions in 252 | any loaded object, as the function must have execute permission to its entire 253 | body, and so the capability used to reference the function would need to 254 | (transitively) confer such rights. Sentries let us refer to some code 255 | without the rights to jump to any part of it except the intended entry point. 256 | At the time of this writing, CHERI-RISC-V always, and Morello optionally, 257 | automatically constructs sentry capabilities when executing linked control 258 | transfers. 259 | 260 | At present, the default linkage model of CHERI means that PCC has bounds of 261 | an entire loaded `.text` segment (the executable or one of its loaded 262 | libraries), so ensuring the use of sentries when crossing segments restricts 263 | the ability to source gadgets to any that may exist within the segment 264 | vulnerable to ROP or JOP injection. 265 | 266 | The curious reader should seek out additional information about CHERI's 267 | "object type" mechanism and sealed capabilities, of which sentries are just 268 | one example. 269 | --------------------------------------------------------------------------------