| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| Gravity v0.9.9 source code.tar.gz | 2026-10-11 | 973.3 kB | |
| Gravity v0.9.9 source code.zip | 2026-10-11 | 1.4 MB | |
| README.md | 2026-10-11 | 3.5 kB | |
| Totals: 3 Items | 2.4 MB | 0 | |
Security and bug-fix release. Three memory-safety issues reachable from any executed Gravity script are fixed, along with a code generation bug in the ternary operator. Each fix ships with a regression test.
Embedders that run untrusted scripts, or call JSON.parse on untrusted input, should upgrade.
Fixed
Runtime
- Heap out-of-bounds write in
List.sorted()andList.map()(critical) — both allocate the result withgravity_list_new(NULL, count), which reservescount + 8slots, and then overwrote the result's capacity with the capacity of the source list. A source list grown bypushdoubles its capacity, so the result claimed more room than it actually held, and later pushes — which only checkn < m— wrote script-controlled values past the allocation. The result now keeps its real capacity, asList.reversed()already did (GHSA-gq92-229r-8p96, [#458]) - Heap out-of-bounds read and infinite loop in String methods (high) —
utf8_charbytes()derives a sequence length of 1–4 from the leading byte alone, without checking that many bytes remain, and returns 0 for a continuation byte. String iteration,String.loop()andString.raw()copied that many bytes and read past the buffer on a truncated trailing sequence, andString.split("")never advanced on a leading continuation byte, growing its result until the process ran out of memory. A newutf8_charbytes_safe()clamps the length to the bytes that remain and never returns less than 1; all five call sites use it, so an invalid sequence degrades to a single raw byte (GHSA-m75h-734r-wc7j, [#459]) - Type confusion in the JSON object deserializer (
JSON.parse) (critical) —gravity_class_deserialize()andgravity_function_deserialize()read union members of nested fields without checking the JSON type. Because strings, arrays and objects share the same{length, ptr}layout, a string supplied forinameswas walked as an array of pointers (arbitrary read),$get/$setread object entries out of string bytes, andsuper/nivar/sivarcould dereference NULL or copy an aliased pointer as a C string. Every field is now checked against the type actually read, and the class-identifier check, previously anassert()compiled out underNDEBUG, is a real check in release builds too (GHSA-c3f8-98q2-cqvr, [#460])
Compiler
- Ternary operator returning the wrong value when a branch is a local variable — a branch made of a bare local identifier left its value in the local's own register and emitted no instruction to move it, while the expression took its result from the register of the
elsebranch. Inside a function,true ? foo : 1evaluated to the condition,true, andtrue ? 1 : footofoo. Both branches now store into a result register allocated before either runs (#455, [#456])
Changed
CHANGELOG.md: the former[Unreleased]section described changes that already shipped in 0.9.8; it is now part of the 0.9.8 entry, together with the CR+LF line-number,_WIN32andstaticlibentries that were previously only in the 0.9.8 release notes
Verification
356/356 unit tests and 11/11 loader tests pass, and CI is green on gcc and clang for Linux and macOS and on the address + undefined sanitizer job.
Thanks
To @swoolcock for reporting [#455] and contributing the fix in [#456], and to the reporters of the three security advisories.
Full Changelog: https://github.com/marcobambini/gravity/compare/0.9.8...0.9.9