Releases: marcobambini/gravity
Release list
Gravity v0.9.8
Security and memory-safety release. Most of the crashes below were reported by external researchers fuzzing the compiler and the bytecode loader, and each fix ships with a regression test.
Fixed
Bytecode loader (gravity -x / gravity_vm_loadbuffer)
These are reachable from attacker-controlled serialized bytecode, so they matter to any embedder that loads a .g file it did not produce itself.
- NULL dereference in
gravity_vm_loadbuffer— a function object without anidentifierfield, such as{"x":{"type":"function"}}, reachedstrlen(NULL)and killed the process. The loader now validates the shape of a JSON executable before trusting it: the root and every entry must be objects, the identifier must appear exactly once and be a string, and unknown object types are rejected. Malformed input becomes a load error instead of a crash (#444) - Signed 64-bit integer overflow in
json_parse_ex— the integer and exponent accumulators multiplied by 10 per digit with no range check, so any literal past 19 significant digits overflowed. Signed overflow is undefined in C: the parser stored a wrapped value, and-fsanitize=undefinedbuilds trapped with SIGILL. Both accumulators are range-checked now (#447) - Pointer-arithmetic overflow in the JSON scan loop — the scanner incremented its cursor unconditionally, so input ending while still inside a string or comment advanced the pointer past one-past-the-end, which is undefined behaviour. The loop now stops at the end of the buffer whatever state the scanner is in (#448)
Compiler
- Heap out-of-bounds read in
parse_number_expression— the0x/0b/0oprefix check readvalue[1]without confirming the token had two bytes, so a file whose last token was a bare0read one byte past the buffer (#446) - Crash (SIGFPE) folding a floating-point remainder — the optimizer folded
%by truncating both operands toint64_t, so any divisor with0 < |divisor| < 1became an integer division by zero and killed the compiler on input as small as1 % 0.5. Float remainder is now folded withremainder(), matching the runtime, and mixed Int/Float remainders are left to the VM because REM dispatches on the class of the left operand. This also corrects a silent wrong answer:5.5 % 2.0folded to1where the VM evaluates-0.5(#443) - Undefined behaviour in Int arithmetic — Gravity Ints wrap on overflow, but the wrap was done on signed operands in the VM fast path, in the
operator_int_*methods and in the constant folder. NewGRAVITY_INT_ADD/SUB/MUL/NEG/DIV/REMhelpers do the arithmetic on the unsigned counterpart, so the values are unchanged but no longer undefined. They also coverGRAVITY_INT_MIN op -1, which on x86 faults inidivrather than merely wrapping (#443) - Wrong line numbers on sources saved with CR+LF endings —
is_newline()readPEEK_CURRENTto get the character following the one under examination, which only holds where the caller had already consumed it (the comment scanner andgravity_lexer_skip_line). Ingravity_lexer_nextthe character was still the one at the current offset, so the CR of a CR+LF pair never saw the LF next to it: the pair counted as two line breaks and every row the compiler reported drifted by one per line read so far. An error on row 5 of a file written on Windows was reported on row 9. The lookahead is passed in explicitly now, and the string scanner keeps every byte of the terminator inside the token so that a literal spanning CR+LF lines is not shortened (#389, from the patch in #401)
Runtime
- Heap buffer overflow in
list_storeat— storing past the end of a list grows the backing array throughmarray_resize, which leaves the array untouched when the reallocation fails. The guard tested the pointer for NULL, but a failedreallockeeps the old, smaller, non-NULL buffer in place, so it never fired: the count was then set to the requested index and the fill loop wrote past the end of the allocation. The capacity actually obtained is checked instead, and the fill loop is bounded by it. Reachable from a script —x[4444444444444444444] = 0is enough
Windows
- The Windows code paths were guarded by
WIN32, not_WIN32—gravity_utils.halready selectedwindows.hand itsDIRREFon_WIN32, whilegravity_utils.c,gravity_opt_file.cand the CLI testedWIN32, which no compiler defines on its own. The Visual Studio projects define it in exactly two of their twelve configurations, so every x64 build compiled the POSIX bodies —opendir/readdir— against aDIRREFthat is aHANDLE. MinGW and tcc land in the same place (#411)
Memory lifetime
- Optional classes never released —
Math,File,JSONandENVwere leaked by every embedder that created and destroyed a VM, becausegravity_core_freedropped a reference without the matching balance (#442) - Core reference leaked by every
gravity_compiler_run— the compiler took a reference to the core classes on each run and never gave it back, so the count never reached zero and the core was never torn down (#442) - Double free of the inline source buffer —
gravity -ihanded its heap-allocated wrapper source to the compiler withis_staticfalse, which passes ownership to the lexer; the lexer freed it and the CLI freed the same pointer again on the way out, aborting every inline run under a hardened allocator
Added
make staticlibbuildslibgravity.afrom the same objects asmake lib, so the archive carries the library without the CLI entry point (#427)test/loadbuffer/— malformed JSON executables that must each be rejected as a load error without crashing, plusjson_bounds.c(make jsontest), 60 checks driving the JSON scanner directly. Run withtest/loadbuffer/run_all.shtest/unittest/bugfix_crlf_lineno.gravity— a source stored with CR+LF endings on purpose, asserting the row and column the compiler reports- A GitHub Actions workflow building with gcc and clang on Linux and macOS, plus a job built with
-fsanitize=address,undefinedthat runs the unit tests, the fuzzing corpus and the loader tests through it
Changed
- The usage text now prints the real default output name,
gravity.g;README.mdandCLAUDE.mddocumented a stalegravity.json report_errorcarries the printf format attribute on gcc and clang in all three of its forms, so format/argument mismatches now fail the build rather than needing an external analyser. This replaces the CodeQL workflow, which still ran ongithub/codeql-action@v1, deprecated since January 2023, and reported green without being a current analysis. Its one open finding — asize_tpassed to%dingravity_codegen.c— is fixed
Verification
353/353 unit tests and 12/12 loader tests pass, and the unit tests plus the 742-input fuzzing corpus run clean under -fsanitize=address,undefined.
Thanks
To the reporters of #442, #443, #444, #446, #447 and #448 for the detailed write-ups and minimized reproducers, to @bardo84 for #389, and to @mwasplund (#401), @jockm (#427) and @tDwtp (#411) for the patches this release builds on.
Full Changelog: 0.9.7...0.9.8
Gravity v0.9.7
Gravity v0.9.6
Gravity v0.9.5
Gravity v0.9.5
Extensive bug fixes, memory safety improvements, and new documentation.
Compiler
- Fix off-by-one in lexer PEEK_NEXT/PEEK_NEXT2 boundary checks
- Fix memory leaks in parser (parse_file_expression, parse_variable_declaration, string interpolation)
- Fix wrong constant in semacheck2 upvalue limit error message
- Fix wrong variable name in for-loop register allocation in codegen
- Fix multiple early-return paths in visit_postfix_expr leaking arrays (replaced with goto cleanup)
- Fix register clobbering in LOCATION_CLASS_IVAR_OUTER assignment
- Fix constant folding and NEG optimization checking/writing wrong registers
- Fix OPCODE_SET_FOUR8bit macro missing opcode field
- Convert IR register allocation from bool[256] to compact 32-byte bitmask
- Remove unnecessary NOP emission for empty statements
Runtime / VM
- Fix boolean fast-path comparison incorrectly applied to ordered comparisons (LT, GT, LEQ, GEQ) — now restricted to EQ/NEQ only
- Fix crash in gravity_vm_setslot/gravity_vm_getslot when no frames exist
- Fix list_iterator_next out-of-bounds read
- Fix range_contains not handling reversed ranges
- Fix function_exec default-arg filling using wrong index formula
- Fix int_random truncation from gravity_int_t to int
- Fix string_count broken partial-match logic (rewrite using string_strnstr)
- Fix string_upper/string_lower off-by-one (iterated one byte past end)
- Fix string_loadat UTF-8 corruption when reversing multi-byte sequences
- Fix string_loop iterating byte-by-byte instead of by UTF-8 character
- Fix string_iterator/string_iterator_next missing bounds checks
- Fix fiber_abort wrong nargs check
- Fix system_input blindly stripping last char instead of checking for newline
- Fix convert_map2string/convert_list2string losing pointer on realloc failure
- Fix gravity_class_grow discarding existing ivar values when growing
Optional Modules
- Math: fix atan2f for float builds; use POW macro consistently; fix division-by-zero in logx when base=1; fix integer overflow in random; fix pointer bugs in round
- File: fix memory leaks in buildpath and directory scan; fix wrong nargs in open; fix leaked FILE* on failure; fix crash on negative read size; fix swapped fread/fwrite arguments
- JSON: rewrite string escaping to properly handle backslashes, quotes, and control characters
- ENV: add Windows compatibility for environ access
Shared / Data Structures
- Fix marray_push updating capacity before confirming realloc success
- Fix marray_resize/marray_resize0 to check realloc result
- Fix gravity_hash_memsize counting buckets as nodes
- Improve float hashing precision (%f → %.17g)
- Add overflow guard to gravity_function_cpool_add
Utilities
- Fix buffer overflow in debug DUMP_VM macros
- Fix memory leak in gravity_disassemble
- Fix escape buffer too small in JSON serializer
- Fix swapped PathCombineA arguments on Windows
- Fix file_name_frompath returning NULL when no separator
- Add NULL check in string_dup
- Fix signed arithmetic in number_from_bin
CLI
- Fix memory leaks in unittest_scan and inline execution
Documentation
- Add comprehensive ARCHITECTURE.md covering the full compilation pipeline, VM internals, value system, garbage collector, instruction set, and embedding API
- Update README.md with Building, Usage, and Project Structure sections; update line counts; enhance Features list
Tests
- Add 30 new unit tests covering all major bug fixes
Official 0.9.0 release
- Several leaks and bugs fixed
- New unit tests added
Official 0.8.5 release
Fixed a setter issue that affects some unit tests too
Official 0.8.3 release
Fixed an issue due to the introduction of lazy loading a superclass f…
Official 0.8.2 release
Added a way to lazy loading extern superclass at runtime
Official 0.8.1 release
Official 0.8.0 release
Improved error handling and detection