From 08f15adc6cd9de0db8244963177505cfdd634504 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Sat, 13 Sep 2025 15:50:28 +0200 Subject: lang/c: Add an example on flexible array checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miquel Sabaté Solà --- .gitignore | 3 +++ README.md | 1 + lang/c/Makefile | 56 ++++++++++++++++++++++++++++++++++++++++++++ lang/c/counted_by.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 lang/c/Makefile create mode 100644 lang/c/counted_by.c diff --git a/.gitignore b/.gitignore index 448af94..b892347 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ arch/riscv/machine/machine # kernel/dtm kernel/dtm/modules.order kernel/dtm/Module.symvers + +# lang/c +lang/c/counted_by \ No newline at end of file diff --git a/README.md b/README.md index 1804b5e..4c95800 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ A bunch of code that messes with low level stuff. That is: - `user/`: user space programs that either delve into RISC-V assembly or that interface the Linux Kernel on the RISC-V architecture. - `kernel/dtm`: simple kernel module showcasing the API from the `of` driver. +- `lang/c`: miscellanous code on the C programming language. A lot of this is not taken solely from my brain, and I will give credit whenever it's relevant to each piece of code. For the rest of the code, the GPLv3+ diff --git a/lang/c/Makefile b/lang/c/Makefile new file mode 100644 index 0000000..4979e7a --- /dev/null +++ b/lang/c/Makefile @@ -0,0 +1,56 @@ +## +# By default everything is silent. If you want to change this behavior, simply +# assign V=1 when calling make. + +V = +ifeq ($(strip $(V)),) + E = @echo + Q = @ +else + E = @\# + Q = +endif + +## +# Compile options. You can use CROSS_COMPILE just like on the Linux Kernel. + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +CCFLAGS = -Werror -Wpedantic -Wall -Wextra -Wcast-align -Wcast-qual -Winit-self \ + -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion \ + -Wswitch-default -Wundef -Wunreachable-code -Wmissing-noreturn \ + -D_FORTIFY_SOURCE=3 -fstrict-flex-arrays=3 -fsanitize=bounds-strict + # NOTE: last line: special flags for the examples :) +LDFLAGS = + + +# You can pass an optional `DEBUG` variable to manipulate the build type. +DEBUG = +ifeq ($(strip $(DEBUG)),) + CCFLAGS += -O3 +else + CCFLAGS += -g +endif + +## +# Paths. + +SRC = $(wildcard *.c) +EXES = $(SRC:.c=) + +## +# Targets + +.PHONY: all +all: clean $(EXES) + +.PHONY: build +build: $(EXES) + +.c: + $(E) " CC " $(*F) + $(Q) $(CC) $(CCFLAGS) $< -o $@ + +.PHONY: clean +clean: + $(Q) rm -f $(EXES) diff --git a/lang/c/counted_by.c b/lang/c/counted_by.c new file mode 100644 index 0000000..09d4699 --- /dev/null +++ b/lang/c/counted_by.c @@ -0,0 +1,67 @@ +#define _GNU_SOURCE + +#include +#include +#include + +struct foo { + uint64_t a; + uint64_t b; +}; + +struct bounded_flex_t { + uint64_t count; + + // NOTE: try to move this member somewhere else and you will get: + // counted_by.c:13:20: error: flexible array member not at end of struct + struct foo flex_array[] __attribute__((counted_by(count))); +}; + +void free_flex(struct bounded_flex_t **flex) +{ + struct bounded_flex_t *ptr = *flex; + + printf("Freeing a flexible array of %ld members.\n", ptr->count); + free(*flex); +} + +__attribute__((noreturn)) void die(const char *const str) +{ + fprintf(stderr, str); + exit(EXIT_FAILURE); +} + +int main(int argc, char *argv[]) +{ + uint64_t len = 1; + + if (argc == 2) { + len = (uint64_t) strtol(argv[1], NULL, 10); + if (!len) { + die("error: bad integer value!\n"); + } + } else if (argc > 2) { + die("error: only pass one argument maximum!\n"); + } + + // That's a mouthful :P + struct bounded_flex_t *flex __attribute__((cleanup(free_flex))) = malloc(sizeof(struct bounded_flex_t) + (len * sizeof(struct foo))); + if (!flex) { + die("error: could not allocate memory for it"); + } + + // NOTE: if this is not the first thing that we do, and we use 'len' for the + // for loop below, then we will get a runtime error from the array bound + // checker. Hence, pretty much like in Rust :) + flex->count = len; + + uint64_t accumulator = 0; + for (uint64_t i = 0; i < flex->count; i++) { + flex->flex_array[i] = (struct foo){ .a = accumulator, .b = accumulator + 1}; + accumulator++; + } + + for (uint64_t i = 0; i < flex->count; i++) { + printf("%ld: .a = %ld, .b = %ld\n", i, flex->flex_array[i].a, flex->flex_array[i].b); + } +} -- cgit v1.2.3