From 3669d9406b117cc8e56087e8c75b9894d9e8267b Mon Sep 17 00:00:00 2001 From: Axy Date: Thu, 14 May 2026 15:46:36 +0200 Subject: [PATCH] Proper impl with no potential UB this time! --- Makefile | 9 ++++---- src/ccera.h | 5 +++-- src/defer.c | 34 ++++++------------------------ src/defer.h | 42 ++++++++++--------------------------- src/eval.c | 12 +++++------ src/framealloc.c | 31 +++++---------------------- src/framealloc.h | 32 +++++++++++++++------------- src/jmp.h | 10 ++++++++- src/panic.c | 17 ++++++--------- src/panic.h | 11 +++++----- src/return_patch.h | 25 ---------------------- src/stacktrack.c | 52 +++++++++++++++++++++++++++++++++------------- src/stacktrack.h | 41 ++++++++++++++++++++++++++++++++++++ test.c | 7 +++++-- 14 files changed, 156 insertions(+), 172 deletions(-) delete mode 100644 src/return_patch.h create mode 100644 src/stacktrack.h diff --git a/Makefile b/Makefile index 6f1899c..42b39b4 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ NAME=ccera.a SRCS=src/arc.c src/defer.c src/eval.c src/framealloc.c src/panic.c src/stacktrack.c src/value_lifetime.c src/value_list.c src/value_move.c src/value_new.c -HEADERS=src/align.h src/arc.h src/arith.h src/ccera.h src/defer.h src/framealloc.h src/jmp.h src/memutils.h src/panic.h src/return_patch.h src/value_get.h src/value.h src/value_new.h src/value_types.h +HEADERS=src/align.h src/arc.h src/arith.h src/ccera.h src/defer.h src/framealloc.h src/jmp.h src/memutils.h src/panic.h src/stacktrack.h src/value_get.h src/value.h src/value_new.h src/value_types.h BUILDDIR=.build @@ -17,13 +17,14 @@ CFLAGS=-Wall -Wextra -Werror -pthread -std=gnu23 CFLAGS += -finstrument-functions CFLAGS += -g CFLAGS += -O3 -CFLAGS += -flto -ffat-lto-objects +#CFLAGS += -flto -ffat-lto-objects CFLAGS += -fdiagnostics-color #CC=clang -CC=gcc +#CC=gcc +CC=cc -AR=ar +AR=gcc-ar MAKEFLAGS += --no-print-directory diff --git a/src/ccera.h b/src/ccera.h index 01226ed..8602e02 100644 --- a/src/ccera.h +++ b/src/ccera.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/29 16:08:57 by agilliar #+# #+# */ -/* Updated: 2026/05/13 21:17:24 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:58:03 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -20,6 +20,7 @@ # include "value.h" # include "arith.h" # include "arc.h" +# include "stacktrack.h" // The expression value is a tuple of an evaluatable and the argument to give it // @@ -39,6 +40,6 @@ // move is whether said value shall be moved out // // This function takes ownership -t_value expr_eval(t_value val); +t_value expr_eval(t_value expr); #endif diff --git a/src/defer.c b/src/defer.c index c0336ac..49778f9 100644 --- a/src/defer.c +++ b/src/defer.c @@ -6,11 +6,12 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/05 17:31:36 by agilliar #+# #+# */ -/* Updated: 2026/05/12 14:02:39 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:49:54 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "defer.h" +#include "panic.h" t_defer_frame *defer_store(void) { @@ -19,42 +20,19 @@ t_defer_frame *defer_store(void) return (&store); } -void defer_pop(bool err) +void defer_pop(void) { t_defer *curr; curr = defer_store()->frame; while (curr) { - if (err || (!curr->err)) + if (panicking() || (!curr->err)) curr->f(curr->dat); curr = curr->prev; } + if (defer_store()->ret_ptr) + defer_store()->ret_ptr(); if (defer_store()->prev) *defer_store() = *defer_store()->prev; } - -__attribute__((used)) -void *defer_pop_ret(void) -{ - void *ret; - - ret = defer_store()->ret_ptr; - defer_pop(false); - return (ret); -} - -__attribute__((naked)) -void defer_pop_shim(void) -{ - asm ( - "pushq %rax\n" - "pushq %rdx\n" - "callq defer_pop_ret\n" - "movq %rax, %rsi\n" - "popq %rdx\n" - "popq %rax\n" - "pushq %rsi\n" - "ret\n" - ); -} diff --git a/src/defer.h b/src/defer.h index c15bad1..54b77d2 100644 --- a/src/defer.h +++ b/src/defer.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:08:53 by agilliar #+# #+# */ -/* Updated: 2026/05/13 20:54:16 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:57:08 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -16,8 +16,8 @@ # include # include # include "framealloc.h" -# include "return_patch.h" # include "memutils.h" +# include "stacktrack.h" typedef struct s_defer { @@ -29,39 +29,34 @@ typedef struct s_defer typedef struct s_defer_frame { - void *ret_ptr; - void *frame_ptr; + void (*ret_ptr)(void); + size_t stacktrack_pos; struct s_defer_frame *prev; t_defer *frame; } t_defer_frame; t_defer_frame *defer_store(void); -void defer_pop(bool err); -void defer_pop_shim(void) - __attribute__((naked)); +void defer_pop(void); -__attribute__((always_inline)) +__attribute__((always_inline, no_instrument_function)) static inline void defer_patch(void) { t_defer_frame *prev; t_defer_frame *curr; - void *ret; - if (defer_store()->frame_ptr != __builtin_frame_address(0)) + if (defer_store()->stacktrack_pos != stacktrack_pos()) { - ret = __builtin_return_address(0); - return_patch(&defer_pop_shim); prev = cera_alloca_align(sizeof(t_defer_frame), alignof(t_defer_frame)); curr = defer_store(); *prev = *curr; - curr->ret_ptr = ret; + curr->ret_ptr = stacktrack_put(&defer_pop); curr->frame = NULL; curr->prev = prev; - defer_store()->frame_ptr = __builtin_frame_address(0); + defer_store()->stacktrack_pos = stacktrack_pos(); } } -__attribute__((always_inline)) +__attribute__((always_inline, no_instrument_function)) static inline void defer(void *f, void *dat) { t_defer *curr; @@ -75,7 +70,7 @@ static inline void defer(void *f, void *dat) defer_store()->frame = curr; } -__attribute__((always_inline)) +__attribute__((always_inline, no_instrument_function)) static inline void errdefer(void *f, void *dat) { t_defer *curr; @@ -89,19 +84,4 @@ static inline void errdefer(void *f, void *dat) defer_store()->frame = curr; } -// Defers the given function, alloca-ing size data from the pointer -// Not necessary for errdefer as the stack variables are still live -// on panic -// -__attribute__((always_inline)) -static inline void *deferc(void *f, void *dat, size_t size) -{ - void *b; - - b = cera_alloca(size); - cera_memcpy(b, dat, size); - defer(f, b); - return (b); -} - #endif diff --git a/src/eval.c b/src/eval.c index 90499ce..c73cf45 100644 --- a/src/eval.c +++ b/src/eval.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/04 01:21:34 by agilliar #+# #+# */ -/* Updated: 2026/05/13 22:42:06 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:58:19 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -18,6 +18,7 @@ t_value eval_step(t_value arg, t_value *exprs, uintptr_t *i) (void) arg; (void) exprs; (void) i; + return (value_new_nil()); } // The expression value is a tuple of an evaluatable and the argument to give it @@ -38,17 +39,16 @@ t_value eval_step(t_value arg, t_value *exprs, uintptr_t *i) // move is whether said value shall be moved out // // This function takes ownership -t_value expr_eval(t_value _expr) +t_value expr_eval(t_value expr) { - t_value *expr; t_value *eval; t_value arg; t_value *exprs; uintptr_t *i; - expr = deferc(value_drop, &_expr, sizeof(t_value)); - eval = value_list_getu(expr, 0); - arg = value_take_nil(value_list_getu(expr, 1)); + defer(value_drop, &expr); + eval = value_list_getu(&expr, 0); + arg = value_take_nil(value_list_getu(&expr, 1)); exprs = value_list_getu(eval, 0); i = value_uint(value_list_getu(eval, 1)); (void) exprs; diff --git a/src/framealloc.c b/src/framealloc.c index 4089208..c80b191 100644 --- a/src/framealloc.c +++ b/src/framealloc.c @@ -6,11 +6,12 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/11 19:22:02 by agilliar #+# #+# */ -/* Updated: 2026/05/12 14:01:24 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:58:34 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "framealloc.h" +#include "stacktrack.h" t_framealloc *framealloc_store(void) { @@ -19,35 +20,13 @@ t_framealloc *framealloc_store(void) return (&store); } +__attribute__((no_instrument_function)) void framealloc_pop(void) { t_framealloc *store; store = framealloc_store(); + if (store->frame.ret_ptr) + store->frame.ret_ptr(); store->frame = *store->frame.prev; } - -__attribute__((used)) -void *framealloc_pop_ret(void) -{ - void *ret_ptr; - - ret_ptr = framealloc_store()->frame.ret_ptr; - framealloc_pop(); - return (ret_ptr); -} - -__attribute__((naked)) -void framealloc_pop_shim(void) -{ - asm ( - "pushq %rax\n" - "pushq %rdx\n" - "callq framealloc_pop_ret\n" - "movq %rax, %rsi\n" - "popq %rdx\n" - "popq %rax\n" - "pushq %rsi\n" - "retq\n" - ); -} diff --git a/src/framealloc.h b/src/framealloc.h index a176e8f..b6d2d5b 100644 --- a/src/framealloc.h +++ b/src/framealloc.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/11 19:17:56 by agilliar #+# #+# */ -/* Updated: 2026/05/13 21:16:13 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 14:53:22 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,8 +14,8 @@ # define FRAMEALLOC_H # include -# include "return_patch.h" # include "align.h" +# include "stacktrack.h" # ifndef FRAMEALLOC_SIZE # define FRAMEALLOC_SIZE 32768 @@ -25,8 +25,8 @@ typedef struct s_framealloc_frame { size_t used; struct s_framealloc_frame *prev; - void *ret_ptr; - void *frame_ptr; + void (*ret_ptr)(void); + size_t stacktrack_pos; } t_framealloc_frame; typedef struct s_framealloc @@ -36,11 +36,11 @@ typedef struct s_framealloc } t_framealloc; t_framealloc *framealloc_store(void); -void framealloc_pop_shim(void) - __attribute__((naked)); -void framealloc_pop(void); +void framealloc_pop(void) + __attribute__((no_instrument_function)); -__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2))) +__attribute__((always_inline, no_instrument_function)) +__attribute__((malloc, alloc_size(1), alloc_align(2))) static inline void *cera_alloca_align_nopatch(size_t len, size_t align) { t_framealloc *store; @@ -54,37 +54,39 @@ static inline void *cera_alloca_align_nopatch(size_t len, size_t align) return (res); } +__attribute__((no_instrument_function)) __attribute__((always_inline)) static inline void cera_alloca_patch(void) { t_framealloc *store; t_framealloc_frame prev_frame; t_framealloc_frame *frame_p; - void *ret; + void (*ret)(void); store = framealloc_store(); - if (store->frame.frame_ptr != __builtin_frame_address(0)) + if (store->frame.stacktrack_pos != stacktrack_pos()) { - ret = __builtin_return_address(0); - return_patch(&framealloc_pop_shim); + ret = stacktrack_put(&framealloc_pop); prev_frame = store->frame; frame_p = cera_alloca_align_nopatch(sizeof(t_framealloc_frame), alignof(t_framealloc_frame)); *frame_p = prev_frame; store->frame.prev = frame_p; store->frame.ret_ptr = ret; - store->frame.frame_ptr = __builtin_frame_address(0); + store->frame.stacktrack_pos = stacktrack_pos(); } } -__attribute__((always_inline, malloc, alloc_size(1), alloc_align(2))) +__attribute__((always_inline, no_instrument_function)) +__attribute__((malloc, alloc_size(1), alloc_align(2))) static inline void *cera_alloca_align(size_t len, size_t align) { cera_alloca_patch(); return (cera_alloca_align_nopatch(len, align)); } -__attribute__((always_inline, malloc, alloc_size(1))) +__attribute__((always_inline, no_instrument_function)) +__attribute__((malloc, alloc_size(1))) static inline void *cera_alloca(size_t len) { return (cera_alloca_align(len, 8)); diff --git a/src/jmp.h b/src/jmp.h index 0d42250..a146376 100644 --- a/src/jmp.h +++ b/src/jmp.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:08:02 by agilliar #+# #+# */ -/* Updated: 2026/05/13 16:43:56 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 15:33:06 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -16,6 +16,14 @@ # include +# ifdef __clang__ + +typedef void *t_jmp[3]; + +# else + typedef size_t t_jmp[3]; +# endif + #endif diff --git a/src/panic.c b/src/panic.c index 699db6f..46748ec 100644 --- a/src/panic.c +++ b/src/panic.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/05 15:38:33 by agilliar #+# #+# */ -/* Updated: 2026/05/13 11:34:46 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 15:27:37 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -20,17 +20,14 @@ t_panic_info *panic_store(void) return (&store); } -__attribute__((returns_nonnull)) void *catch(void *f, void *dat) { void *res; t_panic_info prev; - cera_alloca_patch(); - defer_patch(); prev = *panic_store(); - panic_store()->frame_ptr = __builtin_frame_address(0); - if (__builtin_setjmp(&panic_store()->jmp)) + panic_store()->stacktrack_pos = stacktrack_pos(); + if (__builtin_setjmp((void *)&panic_store()->jmp)) { res = panic_store()->data; *panic_store() = prev; @@ -44,12 +41,10 @@ void *catch(void *f, void *dat) __attribute__((noreturn, nonnull(1), cold)) void panic(void *err) { - while (defer_store()->frame_ptr != panic_store()->frame_ptr) - defer_pop(true); - while (framealloc_store()->frame.frame_ptr != panic_store()->frame_ptr) - framealloc_pop(); + while (stacktrack_pos() != panic_store()->stacktrack_pos) + stacktrack_pop(); panic_store()->data = err; - __builtin_longjmp(&panic_store()->jmp, 1); + __builtin_longjmp((void *)&panic_store()->jmp, 1); } bool panicking(void) diff --git a/src/panic.h b/src/panic.h index 85f7d07..eee1a10 100644 --- a/src/panic.h +++ b/src/panic.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/10 22:11:10 by agilliar #+# #+# */ -/* Updated: 2026/05/13 11:34:25 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 15:09:11 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -17,17 +17,16 @@ typedef struct s_panic_info { - void *data; - t_jmp jmp; - void *frame_ptr; + void *data; + t_jmp jmp; + size_t stacktrack_pos; } t_panic_info; t_panic_info *panic_store(void); void panic(void *err) __attribute__((noreturn, nonnull(1))); -void *catch(void *f, void *dat) - __attribute__((returns_nonnull)); +void *catch(void *f, void *dat); bool panicking(void); #endif diff --git a/src/return_patch.h b/src/return_patch.h deleted file mode 100644 index a30ed14..0000000 --- a/src/return_patch.h +++ /dev/null @@ -1,25 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* return_patch.h :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: agilliar +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2026/05/11 20:08:23 by agilliar #+# #+# */ -/* Updated: 2026/05/12 08:54:17 by agilliar ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#ifndef RETURN_PATCH_H -# define RETURN_PATCH_H - -__attribute__((always_inline)) -static inline void return_patch(void *p) -{ - void **frame_addr; - - frame_addr = __builtin_frame_address(0); - frame_addr[1] = p; -} - -#endif diff --git a/src/stacktrack.c b/src/stacktrack.c index b948404..3310c83 100644 --- a/src/stacktrack.c +++ b/src/stacktrack.c @@ -6,46 +6,68 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/14 12:00:36 by agilliar #+# #+# */ -/* Updated: 2026/05/14 13:11:24 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 15:33:45 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include #include -#define RETFUNC_STACK_HEIGHT 1024 +#include "stacktrack.h" -void ***retfunc_store(void) +__attribute__((no_instrument_function)) +static t_stacktrack_store *stacktrack_store(void) { - static __thread void *stack[RETFUNC_STACK_HEIGHT]; - static __thread void **store = stack; + static __thread t_stacktrack_store store; return (&store); } +__attribute__((no_instrument_function)) +void *stacktrack_put(void *next) +{ + void *prev; + t_stacktrack_store *store; + + store = stacktrack_store(); + prev = store->stack[store->pos]; + store->stack[store->pos] = next; + return (prev); +} + +__attribute__((no_instrument_function)) +size_t stacktrack_pos(void) +{ + return (stacktrack_store()->pos); +} + __attribute__((no_instrument_function)) void __cyg_profile_func_enter(void *this_fn, void *call_site) { - void **store; + t_stacktrack_store *store; (void) this_fn; (void) call_site; - store = retfunc_store(); - (*store)++; - if (**store) + store = stacktrack_store(); + store->pos++; + if (store->stack[store->pos]) __builtin_unreachable(); } __attribute__((no_instrument_function)) void __cyg_profile_func_exit(void *this_fn, void *call_site) { - void **store; + t_stacktrack_store *store; + size_t pos; (void) this_fn; (void) call_site; - store = retfunc_store(); - if (**store) - ((void (*)(void))**store)(); - **store = NULL; - (*store)--; + store = stacktrack_store(); + pos = store->pos; + if (store->stack[pos]) + { + ((void (*)(void))store->stack[pos])(); + store->stack[pos] = NULL; + } + store->pos--; } diff --git a/src/stacktrack.h b/src/stacktrack.h new file mode 100644 index 0000000..427f78e --- /dev/null +++ b/src/stacktrack.h @@ -0,0 +1,41 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* stacktrack.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/14 13:42:09 by agilliar #+# #+# */ +/* Updated: 2026/05/14 15:04:54 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef STACKTRACK_H +# define STACKTRACK_H + +# define STACKTRACK_STACK_HEIGHT 1023 + +# include + +typedef struct s_stacktrack_store +{ + size_t pos; + void (*stack[STACKTRACK_STACK_HEIGHT])(void); +} t_stacktrack_store; + +size_t stacktrack_pos(void) + __attribute__((no_instrument_function)); + +void *stacktrack_put(void *next) + __attribute__((no_instrument_function)); + +void __cyg_profile_func_exit(void *this_fn, void *call_site) + __attribute__((no_instrument_function)); + +__attribute__((always_inline, no_instrument_function)) +static inline void stacktrack_pop(void) +{ + __cyg_profile_func_exit(NULL, NULL); +} + +#endif diff --git a/test.c b/test.c index d1b2984..fa9066c 100644 --- a/test.c +++ b/test.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 17:19:58 by agilliar #+# #+# */ -/* Updated: 2026/05/12 14:07:55 by agilliar ### ########.fr */ +/* Updated: 2026/05/14 15:45:14 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -21,7 +21,10 @@ void main2(char *orig) defer(free, s); char *s2 = malloc(strlen(orig) + 1); defer(free, s2); + strcpy(s2, orig); + printf("%s\n", s2); panic("error!"); + printf("%s\n", s2); } int main(void) @@ -30,7 +33,7 @@ int main(void) char *orig = "hello cera!"; char *s = malloc(strlen(orig) + 1); - cera_alloca_align(10, 30); + cera_alloca_align(10, 32); defer(free, s); msg = catch(main2, orig); if (msg) -- 2.53.0