From 817de379963640905bd8e8416998c42b3ed17e86 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Sat, 23 May 2026 12:15:30 -0700 Subject: [PATCH 1/8] CI: add GCC 16 on ARM job --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8726d8a3..20e2ddb9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -432,6 +432,32 @@ jobs: - run: sudo cmake --install build - run: cmake --build build --target check + ubuntu2404_gcc16_aarch64: + name: Ubuntu 24.04, GCC 16, aarch64 + runs-on: ubuntu-24.04-arm + env: + DEBIAN_FRONTEND: noninteractive + CC: gcc-16 + CXX: g++-16 + CFLAGS: -pedantic -Werror -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=address,undefined + CXXFLAGS: -pedantic -Werror -Wno-error=overloaded-virtual -g -fno-omit-frame-pointer -fsanitize=address,undefined -fno-sanitize-recover=address,undefined + UBSAN_OPTIONS: print_stacktrace=1 + steps: + - run: uname -rms + - run: python3 --version + - run: sudo add-apt-repository ppa:ubuntu-toolchain-r/test + - run: sudo apt-get update + - run: sudo apt-get install --no-install-recommends -y g++-16 libfl-dev libxml2-utils python3-pytest + - run: which gcc-16 + - run: which g++-16 + - run: echo "cloning ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" + - run: git clone --no-checkout -- ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} wd + - run: cd wd && git fetch -- origin ${{ github.event.pull_request.head.sha }} && git checkout FETCH_HEAD + - run: cmake -B build -S wd + - run: cmake --build build + - run: sudo cmake --install build + - run: cmake --build build --target check + macos26_homebrew_x86-64: name: macOS 26, Homebrew, x86-64 runs-on: macos-26-intel From 2e4a23beb46997a552c7ea0197870f9a3f623ded Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Sun, 31 May 2026 16:43:24 -0700 Subject: [PATCH 2/8] abbreviate some unused variables --- rumur/resources/header.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rumur/resources/header.c b/rumur/resources/header.c index ac645328..38e27338 100644 --- a/rumur/resources/header.c +++ b/rumur/resources/header.c @@ -1893,8 +1893,8 @@ handle_narrow(struct handle h, size_t offset, size_t width) { ASSERT(h.offset + offset + width <= h.offset + h.width && "narrowing a handle with values that actually expand it"); - size_t r __attribute__((unused)); - assert(!ADD(h.offset, offset, &r) && "narrowing handle overflows a size_t"); + assert(!ADD(h.offset, offset, &(size_t){0}) && + "narrowing handle overflows a size_t"); return (struct handle){ .base = h.base + (h.offset + offset) / CHAR_BIT, @@ -1923,8 +1923,8 @@ handle_index(const char *NONNULL context, const char *rule_name, expr, rule_name == NULL ? "" : " within ", rule_name == NULL ? "" : rule_name); - size_t r __attribute__((unused)); - assert(!ADD(root.offset, r2, &r) && "indexing handle overflows a size_t"); + assert(!ADD(root.offset, r2, &(size_t){0}) && + "indexing handle overflows a size_t"); return (struct handle){ .base = root.base + (root.offset + r2) / CHAR_BIT, From e475501230ba6ae7c2d97a0abb93a3616755d738 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 3/8] add an assertion on index count when generating clear OpenCode claims the calculation of `ic` can underflow, leading to generation of an infinite loop. This is a false positive, so lets try to make that more obvious to static analysis tools. Reported-by: OpenCode 1.16.2 --- rumur/src/generate-stmt.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rumur/src/generate-stmt.cc b/rumur/src/generate-stmt.cc index e77eb823..dd2dbad3 100644 --- a/rumur/src/generate-stmt.cc +++ b/rumur/src/generate-stmt.cc @@ -32,7 +32,9 @@ static void clear(std::ostream &out, const TypeExpr &t, if (auto a = dynamic_cast(type.get())) { // The number of elements in this array as a C code string - mpz_class ic = a->index_type->count() - 1; + const mpz_class ic_with_undefined = a->index_type->count(); + assert(ic_with_undefined > 0); + const mpz_class ic = ic_with_undefined - 1; const std::string ub = "((size_t)" + ic.get_str() + "ull)"; // The bit size of each array element as a C code string From d08bb56852738b31cef48c64baac560fff257110 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 4/8] abstract CLZ logic --- rumur/resources/header.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rumur/resources/header.c b/rumur/resources/header.c index 38e27338..a205f474 100644 --- a/rumur/resources/header.c +++ b/rumur/resources/header.c @@ -35,12 +35,14 @@ } while (0) #endif +/// count leading zeroes, that is not UB for 0 +#define CLZLL(value) \ + ((value) == 0 ? sizeof(unsigned long long) * CHAR_BIT \ + : __builtin_clzll(value)) + #define BITS_TO_BYTES(size) \ ((size) / CHAR_BIT + ((size) % CHAR_BIT == 0 ? 0 : 1)) -#define BITS_FOR(value) \ - ((value) == 0 \ - ? 0 \ - : (sizeof(unsigned long long) * CHAR_BIT - __builtin_clzll(value))) +#define BITS_FOR(value) (sizeof(unsigned long long) * CHAR_BIT - CLZLL(value)) /* The size of the compressed state data in bytes. */ enum { STATE_SIZE_BYTES = BITS_TO_BYTES(STATE_SIZE_BITS) }; From 7fa319e8a84e097aff69e2dce09007c3b676f9f4 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 5/8] remove magic number in 'INITIAL_SET_SIZE_EXPONENT' definition --- rumur/resources/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rumur/resources/header.c b/rumur/resources/header.c index a205f474..837df356 100644 --- a/rumur/resources/header.c +++ b/rumur/resources/header.c @@ -3297,7 +3297,7 @@ static __attribute__((const)) bool slot_neq(slot_t a, slot_t b) { enum { INITIAL_SET_SIZE_EXPONENT = - sizeof(unsigned long long) * 8 - 1 - + sizeof(unsigned long long) * CHAR_BIT - 1 - __builtin_clzll(SET_CAPACITY / sizeof(struct state *) / sizeof(struct state)) }; From e159d81582e71177c0f295460df41671181dbabd Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 6/8] fix: clamp 'INITIAL_SET_SIZE_EXPONENT' at 0 OpenCode notices that the argument to CLZ could evaluate to 0, leading to UB. The follow on problem here is that even if it were not, this still leads to an invalid large `INITIAL_SET_SIZE_EXPONENT`. The effect of this would be for `--set-capacity x` where `x` is a small number to generate code that would malfunction at runtime. Reported-by: OpenCode 1.16.2 --- rumur/resources/header.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rumur/resources/header.c b/rumur/resources/header.c index 837df356..9e3a441f 100644 --- a/rumur/resources/header.c +++ b/rumur/resources/header.c @@ -3297,9 +3297,12 @@ static __attribute__((const)) bool slot_neq(slot_t a, slot_t b) { enum { INITIAL_SET_SIZE_EXPONENT = - sizeof(unsigned long long) * CHAR_BIT - 1 - - __builtin_clzll(SET_CAPACITY / sizeof(struct state *) / + CLZLL(SET_CAPACITY / sizeof(struct state *) / sizeof(struct state)) != + sizeof(unsigned long long) * CHAR_BIT + ? sizeof(unsigned long long) * CHAR_BIT - 1 - + CLZLL(SET_CAPACITY / sizeof(struct state *) / sizeof(struct state)) + : 0 }; struct set { From 5798a6328aeafb9d01d3e749f4bbb2f3f12764aa Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 7/8] fix incorrect usage of 'select' Reported-by: OpenCode 1.16.2 --- rumur/src/process.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rumur/src/process.cc b/rumur/src/process.cc index cc5b69ba..a13222b0 100644 --- a/rumur/src/process.cc +++ b/rumur/src/process.cc @@ -204,7 +204,7 @@ int run(const std::vector &args, const std::string &input, } // wait for an event - if (select(nfds, &readfds, &writefds, nullptr, nullptr) < 0) { + if (select(nfds + 1, &readfds, &writefds, nullptr, nullptr) < 0) { // if our select call is correct, any “error” should be an interrupt assert(errno == EAGAIN || errno == EINTR); } From 5d3ae5fc97430b99a639e03d0a5bdb08e113a606 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 8 Jun 2026 19:02:36 -0700 Subject: [PATCH 8/8] assert set exponent always results in a valid shift OpenCode complains that the shift in this function could be out-of-range. This is a false positive, seemingly influenced by other incorrect inferences OpenCode makes about this file. But lets try to guide static analysis tools like this to something more accurate. Reported-by: OpenCode 1.16.2 --- rumur/resources/header.c | 1 + 1 file changed, 1 insertion(+) diff --git a/rumur/resources/header.c b/rumur/resources/header.c index 9e3a441f..7b4b6c31 100644 --- a/rumur/resources/header.c +++ b/rumur/resources/header.c @@ -3313,6 +3313,7 @@ struct set { /* Some utility functions for dealing with exponents. */ static size_t set_size(const struct set *NONNULL set) { + assert(set->size_exponent < sizeof(size_t) * CHAR_BIT); return ((size_t)1) << set->size_exponent; }