Fixes the finding from CRAN’s gcc-UBSAN check of 0.1.2 (published
2026-09-12):
jansson/hashtable.c:217: index 1 out of bounds for type 'char [1]'
on the first object parsed. Both changes are in the bundled Jansson
sources, which compile only when no system Jansson is found.
struct hashtable_pair in the bundled
hashtable.h and struct Bigint in the bundled
dtoa.c declare their trailing, variably sized member as a
C99 flexible array member (char key[],
ULong x[]) instead of a one-element array. The one-element
form is the pre-C99 “struct hack”: every allocation was already sized
for the real length, so layout and behavior are unchanged, but indexing
past element 0 of a char [1] is out of bounds for the
declared type, and gcc’s -fsanitize=bounds-strict, which
CRAN’s UBSAN check uses, reports it. The dtoa.c instance
had not been reported yet because dtoa’s 64-bit fast path handles nearly
every double; shortest-form encoding of some whole-number doubles above
2^53 (29006543789128832 is one) falls back to its multi-word arithmetic
and trips the same check. Balloc()’s size arithmetic
follows the new sizeof.test_roundtrip.R now encodes whole-number doubles above
2^53 that take that fallback path, so sanitizer runs exercise it.tools/ubsan-check.sh reproduces CRAN’s gcc-UBSAN
configuration: the bundled Jansson compiled with
-fsanitize=undefined,bounds-strict, then
R CMD check, failing on any sanitizer report in the example
or test output. CI runs it in the Debian R-devel container next to the
existing tools/cran-check.sh gate.Resubmission after CRAN’s review of 0.1.1. DESCRIPTION only; no code changed.
Authors@R now lists every author and copyright holder
of the bundled Jansson sources, not just Jansson’s author: Petri
Lehtinen (ctb, cph), Basile Starynkevitch (ctb, cph:
memory.c), Graeme Smecher (ctb, cph:
pack_unpack.c), Sean Bright (ctb, cph:
version.c), David M. Gay (ctb) and Lucent Technologies
(cph) for dtoa.c, and Bob Jenkins (ctb) for the
public-domain lookup3.h. A Copyright field
points at the new inst/COPYRIGHTS, which records the
per-file statements and license terms.NA and NaN in the Description are no
longer single-quoted; CRAN reserves those quotes for software
names.Resubmission after CRAN’s incoming pretest. Every finding was in the bundled Jansson sources, which compile only when no system library is present: the configuration CRAN’s machines use, and the one our own checks were not exercising.
sprintf
anywhere. The decimal separator now comes from localeconv()
instead of printing 1.0 into a three-byte buffer, and the exponent in
jsonp_dtostr() is written digit by digit. Substituting
snprintf is not enough here: gcc folds an
snprintf whose output it can prove fits back into
sprintf, so the object still referenced it with no
sprintf in the source. Encoded output is unchanged,
verified byte-for-byte over 1039 doubles, 1009 of them in exponent
form.snprintf calls in the bundled
load.c bound their conversions with explicit precisions, so
gcc 16 can prove the trailing literal is never truncated, and
%p in the bundled value.c takes a
const void *.configure no longer uses command -v,
flagged as a possible bashism.tools/cran-check.sh reproduces CRAN’s Debian leg
(bundled Jansson, newest gcc, fails on WARNINGs). CI runs it, and every
check leg now fails on a WARNING rather than printing it and
passing.First CRAN release.
JANSSONR_VENDOR=1 (via --configure-vars)
forces the bundled copy. A system jansson older than 2.11 now selects
the bundled copy instead of failing the install; explicit
INCLUDE_DIR/LIB_DIR that do not yield a usable
jansson remain a hard error.src/jansson/ into src/config-win/ and the
new src/config-unix/ (a config next to the sources would
shadow the -I selection via
jansson_private.h’s quoted include).docs/ no longer reaches the
source tarball (.Rbuildignore).12345678901234567890) now reports
"integer_precision" like the 2^53 refusal, instead of
leaking jansson’s "numeric_overflow"; that code is reserved
for real-form overflow (1e999). Classified from the numeric
token at the error position in the input, never from jansson’s message
text. Lexical integer overflow carries source coordinates and an NA
path; the post-parse 2^53 refusal carries NA coordinates
and an RFC 6901 pointer.Makevars.win compiles Jansson 2.15.1 sources vendored under
src/jansson/ (MIT, Petri Lehtinen; cph added). Unix builds
still link the system library. OS_type: unix dropped.tools/valgrind.sh shows the installation log when the
temp install fails instead of discarding it.from_json() and
to_json(), an R-safe profile of RFC 8259 backed by the
system Jansson library (>= 2.11); needs R >= 4.4 (verified in a
container; 4.4 predates ANY_ATTRIB, covered by a version
shim).-0 encodes as -0.0 so every finite double,
signed zero included, round-trips bit-identically.