Download Latest Version Gravity v0.9.9 source code.zip (1.4 MB) Google Add to Preferred Sources
Home / 0.9.9
Name Modified Size InfoDownloads / 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() and List.map() (critical) — both allocate the result with gravity_list_new(NULL, count), which reserves count + 8 slots, and then overwrote the result's capacity with the capacity of the source list. A source list grown by push doubles its capacity, so the result claimed more room than it actually held, and later pushes — which only check n < m — wrote script-controlled values past the allocation. The result now keeps its real capacity, as List.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() and String.raw() copied that many bytes and read past the buffer on a truncated trailing sequence, and String.split("") never advanced on a leading continuation byte, growing its result until the process ran out of memory. A new utf8_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() and gravity_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 for inames was walked as an array of pointers (arbitrary read), $get/$set read object entries out of string bytes, and super/nivar/sivar could 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 an assert() compiled out under NDEBUG, 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 else branch. Inside a function, true ? foo : 1 evaluated to the condition, true, and true ? 1 : foo to foo. 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, _WIN32 and staticlib entries 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

Source: README.md, updated 2026-10-11