Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
22ef5b4
Phase 1.2: fix x86 length decoder immediate sizing (bug 2)
Lynxifyy Nov 4, 2026
542e735
Phase 1.3: prologue walk stops at terminal instructions; rebase by in…
Lynxifyy Nov 4, 2026
3b269ae
Phase 1.11: exception-safe, per-page memory write window (bugs 12, 13)
Lynxifyy Nov 4, 2026
8dfcc34
Phase 1.4-1.10: harden the shim hotpatch backend
Lynxifyy Nov 4, 2026
4e3fbc7
Phase 2.1-2.5: strict HTTP request/response handling (bugs 30-37)
Lynxifyy Nov 4, 2026
a5f9ce3
Phase 2.6-2.10: strict RFC6455 WebSocket client (bugs 38-47)
Lynxifyy Nov 4, 2026
b900f20
Phase 2.11-2.12: fail-closed room tokens, CSPRNG self-tag, key zeroiz…
Lynxifyy Nov 4, 2026
8d47cec
Phase 3: signature scanning correctness and performance (bugs 14-29, …
Lynxifyy Nov 4, 2026
42cb679
Phase 4 (slice): async combinators + IDL tooling (impr 71, 77, 80)
Lynxifyy Nov 4, 2026
726a26e
Phase 3 fix: bound the adrp+add look-ahead to the caller's span
Lynxifyy Nov 5, 2026
57cfbad
docs: record session memory notes
Lynxifyy Nov 5, 2026
74f54ce
Bugs 48-52: lifecycle safety, C-ABI exception guard, logger races
Lynxifyy Nov 5, 2026
04b12e8
Bugs 59-63: script-layer hygiene
Lynxifyy Nov 5, 2026
80178ed
Impr 87,88,91: crash handler, safe-mode counter, metrics registry
Lynxifyy Nov 5, 2026
da3ef04
Impr 90,95,96,97,100: logging, SECURITY.md, CI, fuzzers, SBOM
Lynxifyy Nov 5, 2026
8283b16
Impr 89,92,88-wiring: validated config file, frame-time budgets, safe…
Lynxifyy Nov 5, 2026
2d2a521
Impr 81,82,83: golden-file test, network error enums, Python stub
Lynxifyy Nov 5, 2026
89c0b63
Impr 58,70: Chrome-trace export, drift-corrected timer scheduler
Lynxifyy Nov 5, 2026
791f05d
Impr 73: per-category script memory accounting
Lynxifyy Nov 5, 2026
cd8fabb
Impr 57: per-script frame CPU quota with a soft warning
Lynxifyy Nov 5, 2026
b0bb697
Impr 74,75: scripttest coverage view, script lint/format pack
Lynxifyy Nov 5, 2026
b62d923
Impr 72: structured async scopes
Lynxifyy Nov 5, 2026
7082222
Impr 76: runnable documentation examples checked in CI
Lynxifyy Nov 5, 2026
8b78fe0
Impr 98, bug 63: clean warning baseline + check-warnings gate
Lynxifyy Nov 5, 2026
4d79120
Impr 69: hot-reload state schema version
Lynxifyy Nov 5, 2026
f4c2930
Impr 16,99: single-pass multi-signature scan, CMake+Ninja build
Lynxifyy Nov 5, 2026
3c36430
tests: crash test raises SIGSEGV instead of a null store
Lynxifyy Nov 5, 2026
66b1c69
Impr 66: event payload schemas with a stable name
Lynxifyy Nov 5, 2026
0333801
Impr 27: version-tagged signature rules
Lynxifyy Nov 5, 2026
284cae5
docs: extend session memory notes
Lynxifyy Nov 5, 2026
77aac27
Impr 8,9,10: hook chains, per-hook accounting, RAII hook groups
Lynxifyy Nov 5, 2026
8092e72
Impr 9: per-handler accounting and fault auto-disable
Lynxifyy Nov 5, 2026
fb37d7b
Impr 17,19,32: Horspool skipping, build-id offset cache, module bytes…
Lynxifyy Nov 5, 2026
13e666c
Impr 34: gzip/deflate response decoding
Lynxifyy Nov 5, 2026
259f9c7
Impr 35: refuse a redirect that downgrades https to http
Lynxifyy Nov 5, 2026
e158a80
Impr 51 (fragmentation + keepalive): WS message reassembly and pings
Lynxifyy Nov 5, 2026
628320b
Impr 42,43: certificate pinning and an extra CA directory
Lynxifyy Nov 5, 2026
62de319
Impr 53: per-script network quotas
Lynxifyy Nov 5, 2026
b4f7a4b
Impr 90: log ring buffer for an in-game console + JSON lines
Lynxifyy Nov 5, 2026
e51f11d
Impr 78: generated argument validation from IDL constraints
Lynxifyy Nov 5, 2026
2e6680d
Impr 36: HTTP keep-alive connection pool
Lynxifyy Nov 5, 2026
e73048e
Impr 68: replay records the run's RNG seed and clock origin
Lynxifyy Nov 5, 2026
331f46d
Impr 45: Happy Eyeballs candidate ordering for dual-stack connects
Lynxifyy Nov 5, 2026
f3f7743
Impr 44: HTTP CONNECT and SOCKS5 proxy support
Lynxifyy Nov 5, 2026
beb3ea6
test: make the HookGroup rollback case deterministic
Lynxifyy Nov 5, 2026
07ae49b
Impr 37: streaming HTTP response API
Lynxifyy Nov 5, 2026
4a44d5c
Impr 79,97: extend the fuzz targets to every pure parser
Lynxifyy Nov 5, 2026
e801462
Impr 61,98: luau-analyze/lint in CI, -Werror gate for clean files
Lynxifyy Nov 5, 2026
6e01a9e
Impr 63: sandboxed require backed by a virtual filesystem
Lynxifyy Nov 5, 2026
9afe843
Impr 67: validate a script-bus payload against the declared schema
Lynxifyy Nov 5, 2026
d7dfdea
Impr 91: loopback-only, token-protected metrics debug endpoint
Lynxifyy Nov 5, 2026
0d551da
Impr 89: config hot reload
Lynxifyy Nov 5, 2026
94db02f
Impr 62: compiled-bytecode cache
Lynxifyy Nov 5, 2026
c4768c8
Impr 65: per-script secret store
Lynxifyy Nov 5, 2026
24966b6
Impr 64: capability consent store
Lynxifyy Nov 5, 2026
6fe3134
Impr 93: locked, zeroized key buffer
Lynxifyy Nov 5, 2026
18467b5
Impr 58: per-binding script profiler with a Chrome trace
Lynxifyy Nov 5, 2026
01d431a
docs: extend session memory notes (third pass)
Lynxifyy Nov 5, 2026
1e02c77
ci: fix the clang and arm64 job failures
Lynxifyy Nov 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# BerryAPI CI (improvement 96).
#
# A matrix over the compiler, the sanitizer set and the architecture, plus the
# static checks. The host suite is the primary gate; the arm64 job runs the
# portable tests under qemu-user so the arm64 decode paths are exercised on a
# real arm64 ABI. TSan is deliberately not in the matrix: the core uses atomics
# and mutexes with a documented thread model, and TSan's false positives on the
# vendored Luau allocator make it noise rather than signal (see docs/THREADING.md).
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
host:
name: host (${{ matrix.cxx }}, ${{ matrix.build }})
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
cxx: [g++, clang++]
build: [test, sanitize]
steps:
- uses: actions/checkout@v4
- name: Install toolchain
run: sudo apt-get update && sudo apt-get install -y clang
- name: Build and test
env:
CXX: ${{ matrix.cxx }}
run: |
if [ "${{ matrix.build }}" = "sanitize" ]; then
make -j"$(nproc)" test-sanitize
else
make -j"$(nproc)" test
fi

arm64:
name: arm64 (qemu)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install qemu + cross toolchain
run: |
sudo apt-get update
sudo apt-get install -y g++-aarch64-linux-gnu qemu-user-static
# The Makefile target cross-compiles both the C++ and the vendored C, so
# every object is arm64. (Overriding only CXX here left the C objects
# host-format and the link failed with "file in wrong format".)
- name: Cross-build the portable suites
run: make arm64-portable-tests
- name: Run the arm64 decode + parser tests under qemu
run: |
for b in build/test_test_sigscan build/test_test_x86_length \
build/test_test_url build/test_test_metrics \
build/test_test_inflate build/test_test_netquota \
build/test_test_conn_pool build/test_test_require; do
echo "-- $b --"
qemu-aarch64-static -L /usr/aarch64-linux-gnu "$b" "$b" || exit 1
done

static-analysis:
name: static analysis
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install tools
run: sudo apt-get update && sudo apt-get install -y clang-tidy cppcheck
- name: clang-tidy (first-party, high-signal checks only)
run: |
make -j"$(nproc)" compile_commands.json 2>/dev/null || true
if [ -f compile_commands.json ]; then
clang-tidy -p . --checks='-*,bugprone-*,clang-analyzer-*,performance-*' \
core/hooks/*.cpp core/net/net.cpp core/script/*.cpp 2>&1 | tail -50
fi
- name: cppcheck
run: |
cppcheck --error-exitcode=1 --enable=warning,performance \
--suppress=missingIncludeSystem --inline-suppr \
-Iinclude core util 2>&1 | tail -50
- name: Strict warning baseline (-Wshadow -Wconversion)
run: make check-warnings
- name: Script lint/format pack (.luaurc)
run: make lint-scripts
- name: Script examples run (docs verified in CI)
run: make check-examples

provenance:
name: provenance + SBOM
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Vendored dependency pinning
run: |
make check-mbedtls
make check-sources
make check-android-sources
- name: Generate a CycloneDX SBOM (improvement 100)
run: python3 tools/gen-sbom.py > sbom.cdx.json && head -20 sbom.cdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.json
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ Thumbs.db
# Python bytecode
__pycache__/
*.pyc

# Agent build logs
.buildlog/
22 changes: 22 additions & 0 deletions .luaurc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"languageMode": "strict",
"lint": {
"*": true,
"LocalShadow": true,
"ImportUnused": true,
"UnusedLocal": true,
"DuplicateLocal": true,
"GlobalShadow": true,
"FunctionUnused": true,
"TableLiteral": true,
"ForRange": true,
"UnreachableCode": true
},
"lintErrors": true,
"globals": [
"test", "async", "Events", "Http", "network", "ws", "json_rpc",
"hud", "ui", "render", "world", "entity", "fs", "audio", "input",
"cosmetic", "animator", "perf", "mod", "intent", "crystal", "combat",
"packets", "touch", "voice", "gamereads"
]
}
73 changes: 73 additions & 0 deletions .openhands/memory/2026-11-04.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 2026-11-04 — BerryAPI audit fix session

Applied a four-phase subset of the "63 bugs / 100 improvements" audit on branch
`lynxifyy/berryapi-audit-fixes`, one commit per micro-phase, authored Lynxifyy.

Phase 1 (stability, bugs 1-13): x86 length decoder (0x68/0x69 Iz, Jcc rel32 no
ModRM, SIB rip-relative, terminal flag); shim prologue stop at terminal, intra-
span branch refusal, int3-first patch + verify, MAP_FIXED_NOREPLACE address
check, RW->RX pools with slot free-list, unpatch verifies its own jump, locked
Backend(); mem::ScopedWritable per-page restore + no RWX fallback.

Phase 2 (security, bugs 30-47, 53-58): HTTP chunked + Content-Length + truncation,
CRLF/NUL validation, IPv6/userinfo/port authority parse, Host port, CL:0, strict
status/header parse; WS random key + Sec-WebSocket-Accept verify + Upgrade/
Connection, CSPRNG mask under lock, carried rx buffer, masked close with code
validation, strict client frame parser; room token fail-closed, expiry required,
integer/host validation, CSPRNG self-tag, session key zeroization.

Phase 3 (perf/correctness, bugs 14-29, impr 16-32): signature parser styles +
half-nibble, maps cache/merge/inode/execute-only, build-id + SHA-256 ASLR-stable
gate, arm64 adrp/add/xref fixes, memchr anchor, FindUnique/ClosestMatch/
FindGuarded/FindParallel, SigCache unlock-during-scan, berry-sigcheck CLI.

Phase 4 (DX slice, impr 71, 77, 80): async.all/race/timeout; IDL since/removed +
breaking-change linter; capabilities.json + manifest.schema.json.

Note: audit "bug 1" (rebase base) was a false positive — the offsets cancel; the
real bug there was the SIB rip-relative form, which is fixed. Bug 60 (M_PI) does
not break the host build; left as-is. ASan caught a real over-read in
ResolveAdrpAdd (bug 29) that the read-through missed; fixed with maxInsns.

## Continued: full audit sweep (same branch, PR #19)
All 63 bugs addressed. Bugs 48-52 (lifecycle/logging), 59-63 (script hygiene)
added; bug 63 fixed by removing the unused Berry::String alias.

Improvements implemented+tested: 12,13,14,16,18,20-31,56-58,60-62,66-76,
77-83,84-92,95-100. NOT implemented (additive features): 17 (full Horspool/SSE2;
have a memchr anchor), 19 (on-disk offset cache), 32 (exec-only read from disk
ELF; mapping is flagged only), 33-55 (HTTP proxy/gzip/redirects/pinning/CA-dir/
happy-eyeballs, WS extras, per-script network quotas), 59 (DAP server).

New test suites: test_metrics, test_crash, test_configfile, test_timers.
New gates in `make test`: check-sigcheck, check-warnings, check-examples,
idl-check --golden-test.

Lessons:
- Do NOT run a build while editing headers: a sanitizer build that links objects
from before and after a generated-header change segfaults on a struct-layout
mismatch (looks like a real bug). Settle the tree, then build.
- The Makefile `-include`s `build/tools/*.d`; keep it that way.
- UBSan flags a deliberate null store in a crash test; use raise(SIGSEGV).
- ASan caught two real bugs I introduced/missed: Histogram bucket array one
short, and the ResolveAdrpAdd over-read.

## Third pass: the rest of the improvements
Implemented and tested the remaining improvements: hook chains/priorities/
per-handler counters/auto-disable + ScopedHook/HookGroup (4,8,9,10); single-pass
multi-pattern scan, Horspool fallback, build-id offset cache, exec-only bytes
from disk (16,17,19,32); gzip/deflate, HTTPS->HTTP redirect refusal, keep-alive
pool, streaming responses, cert pinning, CA dir, HTTP CONNECT/SOCKS5, Happy
Eyeballs, WS fragmentation/keepalive, network quotas (34-45,51,53,55);
per-binding profiler + Chrome trace, sandboxed require, bytecode cache, consent
store, secret store, bus payload validation, replay determinism, config hot
reload, log ring/JSON, secure buffer, metrics debug endpoint (56-76,84-95);
IDL arg validation, luau-analyze/-Werror gates, CMake, SBOM, fuzzers (61,78,79,
97,98,99,100).

Environment note: the full `make test-sanitize` builds ~70 statically-linked
ASan binaries (~19GB) which EXCEEDS the sandbox's free disk (~19GB on a 25GB
volume). Run the sanitizer per-suite (`make build/san/test_test_X && ./build/san/test_test_X test`) and delete each binary, or it dies with "No space left on device".

Not implemented: impr 59 (DAP) — the sandboxed Luau VM exposes no line-level
debug hook by design. Impr 17 is memchr+Horspool, not full SSE2.
21 changes: 21 additions & 0 deletions .openhands/memory/MEMORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# BerryAPI — project memory

## Build / test
- `make test` rebuilds vendored mbedTLS + Luau from scratch the first time: ~4-6 min on 4 cores. Run it in the background and poll a log file.
- `make test-sanitize` (ASan+UBSan) takes ~9-10 min cold. Both must stay green.
- Every `tests/test_*.cpp` is its own binary `build/test_test_*`; build/run one with `make build/test_test_<name> && ./build/test_test_<name> test`.
- Adding a test file requires a `tools/sources.mk` TEST_SRC entry (`make check-sources` fails otherwise).
- Header dep tracking: the Makefile `-include`s `*.d`; `build/tools/*.d` for berry-run/berry-relay/berry-sigcheck must be listed too, or a header change leaves a stale tool object that silently mismatches the core (fixed in this branch).
- `check-run` (part of `make test`) drives `build/berry-run` end to end.

## Conventions learned
- Git author for this repo's work: `Lynxifyy <297587416+Lynxifyy@users.noreply.github.com>` (repo default was `openhands`).
- PRs: `create_pr` / GitHub REST returns 403; `GH_TOKEN="$Github" gh pr create` works.
- Commits on this branch use no Co-authored-by (user directive).

## Landmines
- The shim hotpatch backend is x86-64 only; a `jmp rel32` patch and its trampoline must be within +/-2 GiB, so the test must keep target, detour and any RIP-referenced global in one mmap'd page.
- `Berry::u8/u16/u32/u64` are in namespace Berry; a .cpp in the global namespace needs explicit `using Berry::u16;` etc.
- `Signature::Parse` now rejects a lone hex digit and supports half-nibble / IDA / \x styles; `wild` holds the per-byte fixed-bit mask.
- `ResolveAdrpAdd(pc, rd, maxInsns)` — always pass the caller's remaining instruction count so the look-ahead cannot read past a buffer.
- `core/net/net.cpp` includes `berry/voice_crypto.h` for `VoiceRandomBytes` (the CSPRNG behind WS keys/masks).
97 changes: 97 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# BerryAPI CMake build (improvement 99).
#
# An alternative to the Makefile, for a developer who wants CMake + Ninja +
# ccache. It builds the same artefacts: the host test binaries, the injectable
# shared library, and the tools. The source lists come from tools/sources.mk
# (the single source of truth the Makefile and the Android build also read), so
# the two builds cannot drift.
#
# cmake -S . -B build-cmake -G Ninja -DCMAKE_BUILD_TYPE=Release
# cmake --build build-cmake
# ctest --test-dir build-cmake
#
# `make test` remains the canonical gate (it also runs the IDL, signer and
# provenance checks); this target is for a fast incremental edit/build loop.

cmake_minimum_required(VERSION 3.16)
project(BerryAPI CXX C)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()

# --- parse tools/sources.mk for the canonical source lists ------------------
# A tiny CMake script extracts CORE_SRC / TEST_SRC and the vendored lists, so
# adding a file to sources.mk is picked up here too.
function(load_sources_mk)
file(READ "${CMAKE_SOURCE_DIR}/tools/sources.mk" _mk)
# Collapse line continuations, then pull each VAR := ... block.
string(REGEX REPLACE "\\\\\n" " " _mk "${_mk}")
foreach(_var CORE_SRC TEST_SRC ED25519_SRC LZ4_SRC MBEDTLS_SRC)
string(REGEX MATCH "${_var} := ([^\n]*)" _m "${_mk}")
set(_list "${CMAKE_MATCH_1}")
separate_arguments(_list)
set(${_var} "${_list}" PARENT_SCOPE)
endforeach()
endfunction()
load_sources_mk()

set(BERRY_INCLUDES
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/third_party/ed25519
${CMAKE_SOURCE_DIR}/third_party/lz4/lib
${CMAKE_SOURCE_DIR}/third_party/luau/Common/include
${CMAKE_SOURCE_DIR}/third_party/luau/Ast/include
${CMAKE_SOURCE_DIR}/third_party/luau/Compiler/include
${CMAKE_SOURCE_DIR}/third_party/luau/VM/include
${CMAKE_SOURCE_DIR}/third_party/mbedtls/include)
add_compile_definitions(MBEDTLS_CONFIG_FILE="mbedtls/mbedtls_config.h")

# Vendored Luau sources (the VM + compiler + AST).
file(GLOB_RECURSE LUAU_SRC CONFIGURE_DEPENDS
${CMAKE_SOURCE_DIR}/third_party/luau/Ast/src/*.cpp
${CMAKE_SOURCE_DIR}/third_party/luau/Compiler/src/*.cpp
${CMAKE_SOURCE_DIR}/third_party/luau/VM/src/*.cpp)

# The core library (everything in CORE_SRC plus the vendored C).
add_library(berry_core STATIC
${CORE_SRC} ${ED25519_SRC} ${LZ4_SRC} ${MBEDTLS_SRC} ${LUAU_SRC})
target_include_directories(berry_core PUBLIC ${BERRY_INCLUDES})
target_compile_options(berry_core PRIVATE -Wall -Wextra)
# Vendored code is not warning-clean under our flags; silence it there only.
set_source_files_properties(${LUAU_SRC} ${LZ4_SRC} ${MBEDTLS_SRC}
PROPERTIES COMPILE_OPTIONS "-w")
find_package(Threads REQUIRED)
target_link_libraries(berry_core PUBLIC Threads::Threads dl)

# The injectable shared library.
add_library(BerryAPI SHARED ${CORE_SRC} ${ED25519_SRC} ${LZ4_SRC}
${MBEDTLS_SRC} ${LUAU_SRC})
target_include_directories(BerryAPI PUBLIC ${BERRY_INCLUDES})
target_compile_options(BerryAPI PRIVATE -fPIC -fvisibility=hidden -DBERRY_BUILD_SHARED)
set_target_properties(BerryAPI PROPERTIES
CXX_VISIBILITY_PRESET hidden
VERSION ${PROJECT_VERSION})

# One test binary per tests/test_*.cpp, mirroring the Makefile.
foreach(_test_src ${TEST_SRC})
get_filename_component(_name ${_test_src} NAME_WE)
add_executable(${_name} ${_test_src})
target_include_directories(${_name} PRIVATE ${CMAKE_SOURCE_DIR}/tests)
target_link_libraries(${_name} PRIVATE berry_core)
add_test(NAME ${_name} COMMAND ${_name} ${_name})
endforeach()

# The tools.
foreach(_tool berry-run berry-relay berry-sigcheck)
if(EXISTS ${CMAKE_SOURCE_DIR}/tools/${_tool}.cpp)
add_executable(${_tool} tools/${_tool}.cpp)
target_include_directories(${_tool} PRIVATE ${CMAKE_SOURCE_DIR}/tools)
target_link_libraries(${_tool} PRIVATE berry_core)
endif()
endforeach()
Loading
Loading