Overview
This example uses two global objects (in .data) to demonstrate an overflow.
It is worth pondering how the bounds for pointers to globals come to be set!
-
Compile
buffer-overflow-global.cfor the baseline architecture to the binarybuffer-overflow-global-baselineand for the CHERI-aware architecture tobuffer-overflow-global-cheri.For this exercise, add
-G0to your compiler flags (this ensurescis not placed in the small data section away frombuffer). - Run both programs and observe the output.
- Using GDB on the core dump (or run the CHERI program under
gdb): Why has the CHERI program failed? - Modify
buffer-overflow-global.cto increase the buffer size from 128 bytes to 1Mbyte + 1 byte. - Recompile and re-run
buffer-overflow-global-cheri. Why does it no longer crash, even though the buffer overflow exists in the source code? Is the adjacent field still corrupted (i.e., has spatial safety been violated between allocations)? - Modify
buffer-overflow-global.cto restore the original buffer size of 128 bytes, and fix the bug by correcting accesses to the allocated array. - Recompile and run
buffer-overflow-global-cherito demonstrate that the program is now able to continue.
Source Files
buffer-overflow-global.c
/*
* SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016
* Copyright (c) 2020 SRI International
*/
#include <stdint.h>
#include <printf.h>
struct globals {
char buffer[128];
char c;
};
struct globals globals;
#define buffer globals.buffer
#define c globals.c
#pragma weak fill_buf
void
fill_buf(char *buf, size_t len)
{
for (size_t i = 0; i <= len; i++)
buf[i] = 'b';
}
#include "main-asserts.inc"
void
init(void)
{
(void)buffer;
main_asserts();
c = 'c';
printf("c = %c\n", c);
fill_buf(buffer, sizeof(buffer));
printf("c = %c\n", c);
}
void notified(){}
Support code
main-asserts.inc
/*
* SPDX-License-Identifier: BSD-2-Clause-DARPA-SSITH-ECATS-HR0011-18-C-0016
* Copyright (c) 2020 SRI International
*/
#include <sel4/assert.h>
#include <stddef.h>
#ifdef __CHERI_PURE_CAPABILITY__
#include <cheriintrin.h>
#endif
#ifndef nitems
#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
#endif
static void
main_asserts(void)
{
/*
* Ensure that overflowing `buffer` by 1 will hit `c`.
* In the pure-capabilty case, don't assert if the size of
* `buffer` requires padding.
*/
seL4_Assert((ptraddr_t)&buffer[nitems(buffer)] == (ptraddr_t)&c
#ifdef __CHERI_PURE_CAPABILITY__
|| sizeof(buffer) < cheri_representable_length(sizeof(buffer))
#endif
);
}