Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libjansson for openSUSE:Factory checked in at 2026-07-02 22:13:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libjansson (Old) and /work/SRC/openSUSE:Factory/.libjansson.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libjansson" Thu Jul 2 22:13:50 2026 rev:17 rq:1363118 version:2.15.1 Changes: -------- --- /work/SRC/openSUSE:Factory/libjansson/libjansson.changes 2026-02-07 15:33:16.144980723 +0100 +++ /work/SRC/openSUSE:Factory/.libjansson.new.1982/libjansson.changes 2026-07-02 22:19:07.232805503 +0200 @@ -1,0 +2,11 @@ +Thu Jul 2 06:43:20 UTC 2026 - Paolo Stivanin <[email protected]> + +- Update to 2.15.1: + * Include the object key or array index in unpack type mismatch error + messages (@cwalther in #731) + * Reject negative string length in the json_pack s# and +# formats + (@akheron in #740) + * Limit recursion depth in dump, equal and deep copy to prevent stack + overflow (@akheron in #741) + +------------------------------------------------------------------- Old: ---- jansson-2.15.0.tar.bz2 jansson-2.15.0.tar.bz2.asc New: ---- jansson-2.15.1.tar.bz2 jansson-2.15.1.tar.bz2.asc ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libjansson.spec ++++++ --- /var/tmp/diff_new_pack.KqJFIF/_old 2026-07-02 22:19:07.892828177 +0200 +++ /var/tmp/diff_new_pack.KqJFIF/_new 2026-07-02 22:19:07.896828315 +0200 @@ -18,7 +18,7 @@ %define lname libjansson4 Name: libjansson -Version: 2.15.0 +Version: 2.15.1 Release: 0 Summary: C library for encoding, decoding and manipulating JSON data License: MIT ++++++ jansson-2.15.0.tar.bz2 -> jansson-2.15.1.tar.bz2 ++++++ ++++ 8268 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/CHANGES new/jansson-2.15.1/CHANGES --- old/jansson-2.15.0/CHANGES 2026-01-24 21:54:33.000000000 +0100 +++ new/jansson-2.15.1/CHANGES 2026-07-01 20:35:59.000000000 +0200 @@ -1,3 +1,27 @@ +Version 2.15.1 +============== + +Released 2026-06-30 + +* Fixes: + + - Include the object key or array index in unpack type mismatch error + messages (@cwalther in #731) + - Reject negative string length in the `json_pack` `s#` and `+#` formats + (@akheron in #740) + - Limit recursion depth in dump, equal and deep copy to prevent stack + overflow (@akheron in #741) + +* Build: + + - Only export symbols starting with `json_` and `jansson_` from the CMake + build (@shyjun in #705) + - Include `jansson_private_config.h` only if `HAVE_CONFIG_H` is enabled + (@jaeyoonjung in #704) + - Add a configurable pkg-config install path to the CMake build + (@akallabeth in #721) + - Only use `--default-symver` when the linker supports it (@kraj in #744) + Version 2.15.0 ============== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/CMakeLists.txt new/jansson-2.15.1/CMakeLists.txt --- old/jansson-2.15.0/CMakeLists.txt 2026-01-24 21:54:33.000000000 +0100 +++ new/jansson-2.15.1/CMakeLists.txt 2026-07-01 20:35:59.000000000 +0200 @@ -36,10 +36,10 @@ # set (JANSSON_VERSION "2.3.1") # set (JANSSON_SOVERSION 2) -set(JANSSON_DISPLAY_VERSION "2.15.0") +set(JANSSON_DISPLAY_VERSION "2.15.1") # This is what is required to match the same numbers as automake's -set(JANSSON_VERSION "4.15.0") +set(JANSSON_VERSION "4.15.1") set(JANSSON_SOVERSION 4) # for CheckFunctionKeywords @@ -322,6 +322,9 @@ # some linkers may only support --version-script file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/jansson.sym" "JANSSON_${JANSSON_SOVERSION} { global: + json_*; + jansson_*; + local: *; }; ") @@ -601,6 +604,9 @@ set(JANSSON_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") +set(DEF_INSTALL_PKGCONF_DIR lib/pkgconfig) +set(JANSSON_INSTALL_PKGCONF_DIR ${DEF_INSTALL_PKGCONF_DIR} CACHE PATH "Installation directory for pkg-config files") + # Create pkg-conf file. # (We use the same files as ./configure does, so we # have to defined the same variables used there). @@ -658,7 +664,7 @@ # Install the pkg-config. install(FILES ${CMAKE_CURRENT_BINARY_DIR}/jansson.pc - DESTINATION lib/pkgconfig) + DESTINATION ${JANSSON_INSTALL_PKGCONF_DIR}) # Install the configs. install(FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/configure.ac new/jansson-2.15.1/configure.ac --- old/jansson-2.15.0/configure.ac 2026-01-24 21:54:32.000000000 +0100 +++ new/jansson-2.15.1/configure.ac 2026-07-01 20:35:59.000000000 +0200 @@ -1,5 +1,5 @@ AC_PREREQ([2.60]) -AC_INIT([jansson], [2.15.0], [https://github.com/akheron/jansson/issues]) +AC_INIT([jansson], [2.15.1], [https://github.com/akheron/jansson/issues]) AC_CONFIG_AUX_DIR([.]) AM_INIT_AUTOMAKE([1.10 foreign]) @@ -135,7 +135,16 @@ # Enable symbol versioning on GNU libc JSON_SYMVER_LDFLAGS= -AC_CHECK_DECL([__GLIBC__], [JSON_SYMVER_LDFLAGS=-Wl,--default-symver]) +AC_CHECK_DECL([__GLIBC__], [ + saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -Wl,--default-symver" + AC_MSG_CHECKING([whether the linker accepts -Wl,--default-symver]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [AC_MSG_RESULT([yes]) + JSON_SYMVER_LDFLAGS=-Wl,--default-symver], + [AC_MSG_RESULT([no])]) + LDFLAGS="$saved_LDFLAGS" +]) AC_SUBST([JSON_SYMVER_LDFLAGS]) AC_ARG_ENABLE([dtoa], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/doc/apiref.rst new/jansson-2.15.1/doc/apiref.rst --- old/jansson-2.15.0/doc/apiref.rst 2026-01-24 21:50:09.000000000 +0100 +++ new/jansson-2.15.1/doc/apiref.rst 2026-07-01 20:34:38.000000000 +0200 @@ -1535,7 +1535,9 @@ .. versionadded:: 2.11 ``s#`` (string) [const char \*, int] - Convert a UTF-8 buffer of a given length to a JSON string. + Convert a UTF-8 buffer of a given length to a JSON string. The + length must not be negative; a negative length results in a + ``json_error_invalid_argument`` error. .. versionadded:: 2.5 @@ -1865,13 +1867,15 @@ if their types are equal. (Because these values are singletons, their equality can actually be tested with ``==``.) +* Values nested more deeply than ``JSON_PARSER_MAX_DEPTH`` are not + recursed into, and thus cosidered unequal, to avoid stack exhaustion. + .. function:: int json_equal(json_t *value1, json_t *value2) Returns 1 if *value1* and *value2* are equal, as defined above. Returns 0 if they are unequal or one or both of the pointers are *NULL*. - Copying ======= @@ -1899,7 +1903,9 @@ .. refcounting:: new - Returns a deep copy of *value*, or *NULL* on error. + Returns a deep copy of *value*, or *NULL* on error. Copying fails if + *value* is nested more deeply than ``JSON_PARSER_MAX_DEPTH``, to avoid + stack exhaustion. .. _apiref-custom-memory-allocation: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/doc/conf.py new/jansson-2.15.1/doc/conf.py --- old/jansson-2.15.0/doc/conf.py 2026-01-24 21:54:32.000000000 +0100 +++ new/jansson-2.15.1/doc/conf.py 2026-07-01 20:35:59.000000000 +0200 @@ -48,7 +48,7 @@ # built documents. # # The short X.Y version. -version = '2.15.0' +version = '2.15.1' # The full version, including alpha/beta/rc tags. release = version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/scripts/clang-format-check.log new/jansson-2.15.1/scripts/clang-format-check.log --- old/jansson-2.15.0/scripts/clang-format-check.log 1970-01-01 01:00:00.000000000 +0100 +++ new/jansson-2.15.1/scripts/clang-format-check.log 2026-06-13 14:06:37.000000000 +0200 @@ -0,0 +1,46 @@ +Checking android/jansson_config.h +Checking doc/github_commits.c +Checking examples/simple_parse.c +Checking src/dtoa.c +Checking src/dump.c +Checking src/error.c +Checking src/hashtable.c +Checking src/hashtable.h +Checking src/hashtable_seed.c +Checking src/jansson.h +Checking src/jansson_private.h +Checking src/load.c +Checking src/lookup3.h +Checking src/memory.c +Checking src/pack_unpack.c +Checking src/strbuffer.c +Checking src/strbuffer.h +Checking src/strconv.c +Checking src/utf.c +Checking src/utf.h +Checking src/value.c +Checking src/version.c +Checking test/bin/json_process.c +Checking test/ossfuzz/testinput.h +Checking test/suites/api/test_array.c +Checking test/suites/api/test_chaos.c +Checking test/suites/api/test_chaos_realloc.c +Checking test/suites/api/test_copy.c +Checking test/suites/api/test_dump.c +Checking test/suites/api/test_dump_callback.c +Checking test/suites/api/test_equal.c +Checking test/suites/api/test_fixed_size.c +Checking test/suites/api/test_load.c +Checking test/suites/api/test_load_callback.c +Checking test/suites/api/test_loadb.c +Checking test/suites/api/test_memory_funcs.c +Checking test/suites/api/test_memory_funcs_realloc.c +Checking test/suites/api/test_number.c +Checking test/suites/api/test_object.c +Checking test/suites/api/test_pack.c +Checking test/suites/api/test_simple.c +Checking test/suites/api/test_sprintf.c +Checking test/suites/api/test_unpack.c +Checking test/suites/api/test_version.c +Checking test/suites/api/util.h +PASS scripts/clang-format-check (exit status: 0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/scripts/clang-format-check.trs new/jansson-2.15.1/scripts/clang-format-check.trs --- old/jansson-2.15.0/scripts/clang-format-check.trs 1970-01-01 01:00:00.000000000 +0100 +++ new/jansson-2.15.1/scripts/clang-format-check.trs 2026-06-13 14:06:37.000000000 +0200 @@ -0,0 +1,4 @@ +:test-result: PASS +:global-test-result: PASS +:recheck: no +:copy-in-global-log: no diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/Makefile.am new/jansson-2.15.1/src/Makefile.am --- old/jansson-2.15.0/src/Makefile.am 2026-01-24 21:54:33.000000000 +0100 +++ new/jansson-2.15.1/src/Makefile.am 2026-07-01 20:35:59.000000000 +0200 @@ -30,6 +30,6 @@ libjansson_la_LDFLAGS = \ -no-undefined \ -export-symbols-regex '^json_|^jansson_' \ - -version-info 19:0:15 \ + -version-info 19:1:15 \ @JSON_SYMVER_LDFLAGS@ \ @JSON_BSYMBOLIC_LDFLAGS@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/dump.c new/jansson-2.15.1/src/dump.c --- old/jansson-2.15.0/src/dump.c 2026-01-22 07:37:04.000000000 +0100 +++ new/jansson-2.15.1/src/dump.c 2026-07-01 20:34:38.000000000 +0200 @@ -221,6 +221,9 @@ if (!json) return -1; + if (depth >= JSON_PARSER_MAX_DEPTH) + return -1; + switch (json_typeof(json)) { case JSON_NULL: return dump("null", 4, data); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/jansson.h new/jansson-2.15.1/src/jansson.h --- old/jansson-2.15.0/src/jansson.h 2026-01-24 21:54:32.000000000 +0100 +++ new/jansson-2.15.1/src/jansson.h 2026-07-01 20:35:59.000000000 +0200 @@ -22,10 +22,10 @@ #define JANSSON_MAJOR_VERSION 2 #define JANSSON_MINOR_VERSION 15 -#define JANSSON_MICRO_VERSION 0 +#define JANSSON_MICRO_VERSION 1 /* Micro version is omitted if it's 0 */ -#define JANSSON_VERSION "2.15.0" +#define JANSSON_VERSION "2.15.1" /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/jansson_private.h new/jansson-2.15.1/src/jansson_private.h --- old/jansson-2.15.0/src/jansson_private.h 2026-01-22 07:37:04.000000000 +0100 +++ new/jansson-2.15.1/src/jansson_private.h 2026-07-01 20:34:38.000000000 +0200 @@ -10,7 +10,9 @@ #include "hashtable.h" #include "jansson.h" +#ifdef HAVE_CONFIG_H #include "jansson_private_config.h" +#endif #include "strbuffer.h" #include <stddef.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/pack_unpack.c new/jansson-2.15.1/src/pack_unpack.c --- old/jansson-2.15.0/src/pack_unpack.c 2026-01-22 07:37:04.000000000 +0100 +++ new/jansson-2.15.1/src/pack_unpack.c 2026-07-01 20:34:38.000000000 +0200 @@ -173,7 +173,15 @@ next_token(s); if (token(s) == '#') { - length = va_arg(*ap, int); + int len = va_arg(*ap, int); + if (len < 0) { + set_error(s, "<args>", json_error_invalid_argument, + "Invalid length for %s: %d", purpose, len); + s->has_error = 1; + length = 0; + } else { + length = (size_t)len; + } } else if (token(s) == '%') { length = va_arg(*ap, size_t); } else { @@ -460,9 +468,10 @@ } } -static int unpack(scanner_t *s, json_t *root, va_list *ap); +static int unpack(scanner_t *s, json_t *root, va_list *ap, const char *key); -static int unpack_object(scanner_t *s, json_t *root, va_list *ap) { +static int unpack_object(scanner_t *s, json_t *root, va_list *ap, + const char *parent_key) { int ret = -1; int strict = 0; int gotopt = 0; @@ -480,8 +489,9 @@ } if (root && !json_is_object(root)) { - set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s", - type_name(root)); + set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s%s%s", + type_name(root), parent_key ? " for key " : "", + parent_key ? parent_key : ""); goto out; } next_token(s); @@ -543,7 +553,7 @@ } } - if (unpack(s, value, ap)) + if (unpack(s, value, ap, key)) goto out; hashtable_set(&key_set, key, key_len, json_null()); @@ -597,13 +607,14 @@ return ret; } -static int unpack_array(scanner_t *s, json_t *root, va_list *ap) { +static int unpack_array(scanner_t *s, json_t *root, va_list *ap, const char *parent_key) { size_t i = 0; int strict = 0; if (root && !json_is_array(root)) { - set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", - type_name(root)); + set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s%s%s", + type_name(root), parent_key ? " for key " : "", + parent_key ? parent_key : ""); return -1; } next_token(s); @@ -648,7 +659,7 @@ } } - if (unpack(s, value, ap)) + if (unpack(s, value, ap, NULL)) return -1; next_token(s); @@ -668,18 +679,19 @@ return 0; } -static int unpack(scanner_t *s, json_t *root, va_list *ap) { +static int unpack(scanner_t *s, json_t *root, va_list *ap, const char *key) { switch (token(s)) { case '{': - return unpack_object(s, root, ap); + return unpack_object(s, root, ap, key); case '[': - return unpack_array(s, root, ap); + return unpack_array(s, root, ap, key); case 's': if (root && !json_is_string(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected string, got %s", type_name(root)); + "Expected string, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -716,7 +728,8 @@ case 'i': if (root && !json_is_integer(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected integer, got %s", type_name(root)); + "Expected integer, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -731,7 +744,8 @@ case 'I': if (root && !json_is_integer(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected integer, got %s", type_name(root)); + "Expected integer, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -746,7 +760,8 @@ case 'b': if (root && !json_is_boolean(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected true or false, got %s", type_name(root)); + "Expected true or false, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -761,7 +776,8 @@ case 'f': if (root && !json_is_real(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected real, got %s", type_name(root)); + "Expected real, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -776,7 +792,8 @@ case 'F': if (root && !json_is_number(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected real or integer, got %s", type_name(root)); + "Expected real or integer, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } @@ -806,7 +823,8 @@ /* Never assign, just validate */ if (root && !json_is_null(root)) { set_error(s, "<validation>", json_error_wrong_type, - "Expected null, got %s", type_name(root)); + "Expected null, got %s%s%s", type_name(root), + key ? " for key " : "", key ? key : ""); return -1; } return 0; @@ -898,7 +916,7 @@ next_token(&s); va_copy(ap_copy, ap); - if (unpack(&s, root, &ap_copy)) { + if (unpack(&s, root, &ap_copy, NULL)) { va_end(ap_copy); return -1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/src/value.c new/jansson-2.15.1/src/value.c --- old/jansson-2.15.0/src/value.c 2026-01-22 07:37:04.000000000 +0100 +++ new/jansson-2.15.1/src/value.c 2026-07-01 20:34:38.000000000 +0200 @@ -37,7 +37,8 @@ static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); } #endif -json_t *do_deep_copy(const json_t *json, hashtable_t *parents); +static int do_equal(const json_t *json1, const json_t *json2, int depth); +json_t *do_deep_copy(const json_t *json, hashtable_t *parents, int depth); static JSON_INLINE void json_init(json_t *json, json_type type) { json->type = type; @@ -352,7 +353,7 @@ return hashtable_key_to_iter(key); } -static int json_object_equal(const json_t *object1, const json_t *object2) { +static int json_object_equal(const json_t *object1, const json_t *object2, int depth) { const char *key; size_t key_len; const json_t *value1, *value2; @@ -363,7 +364,7 @@ json_object_keylen_foreach((json_t *)object1, key, key_len, value1) { value2 = json_object_getn(object2, key, key_len); - if (!json_equal(value1, value2)) + if (!do_equal(value1, value2, depth + 1)) return 0; } @@ -387,7 +388,8 @@ return result; } -static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents) { +static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents, + int depth) { json_t *result; void *iter; char loop_key[LOOP_KEY_LEN]; @@ -412,7 +414,7 @@ value = json_object_iter_value(iter); if (json_object_setn_new_nocheck(result, key, key_len, - do_deep_copy(value, parents))) { + do_deep_copy(value, parents, depth + 1))) { json_decref(result); result = NULL; break; @@ -638,7 +640,7 @@ return 0; } -static int json_array_equal(const json_t *array1, const json_t *array2) { +static int json_array_equal(const json_t *array1, const json_t *array2, int depth) { size_t i, size; size = json_array_size(array1); @@ -651,7 +653,7 @@ value1 = json_array_get(array1, i); value2 = json_array_get(array2, i); - if (!json_equal(value1, value2)) + if (!do_equal(value1, value2, depth + 1)) return 0; } @@ -672,7 +674,8 @@ return result; } -static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) { +static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents, + int depth) { json_t *result; size_t i; char loop_key[LOOP_KEY_LEN]; @@ -686,8 +689,8 @@ goto out; for (i = 0; i < json_array_size(array); i++) { - if (json_array_append_new(result, - do_deep_copy(json_array_get(array, i), parents))) { + if (json_array_append_new( + result, do_deep_copy(json_array_get(array, i), parents, depth + 1))) { json_decref(result); result = NULL; break; @@ -1017,6 +1020,10 @@ /*** equality ***/ int json_equal(const json_t *json1, const json_t *json2) { + return do_equal(json1, json2, 0); +} + +static int do_equal(const json_t *json1, const json_t *json2, int depth) { if (!json1 || !json2) return 0; @@ -1027,11 +1034,14 @@ if (json1 == json2) return 1; + if (depth >= JSON_PARSER_MAX_DEPTH) + return 0; + switch (json_typeof(json1)) { case JSON_OBJECT: - return json_object_equal(json1, json2); + return json_object_equal(json1, json2, depth); case JSON_ARRAY: - return json_array_equal(json1, json2); + return json_array_equal(json1, json2, depth); case JSON_STRING: return json_string_equal(json1, json2); case JSON_INTEGER: @@ -1075,21 +1085,24 @@ if (hashtable_init(&parents_set)) return NULL; - res = do_deep_copy(json, &parents_set); + res = do_deep_copy(json, &parents_set, 0); hashtable_close(&parents_set); return res; } -json_t *do_deep_copy(const json_t *json, hashtable_t *parents) { +json_t *do_deep_copy(const json_t *json, hashtable_t *parents, int depth) { if (!json) return NULL; + if (depth >= JSON_PARSER_MAX_DEPTH) + return NULL; + switch (json_typeof(json)) { case JSON_OBJECT: - return json_object_deep_copy(json, parents); + return json_object_deep_copy(json, parents, depth); case JSON_ARRAY: - return json_array_deep_copy(json, parents); + return json_array_deep_copy(json, parents, depth); /* for the rest of the types, deep copying doesn't differ from shallow copying */ case JSON_STRING: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/bin/json_process.c new/jansson-2.15.1/test/bin/json_process.c --- old/jansson-2.15.0/test/bin/json_process.c 2024-03-31 21:03:18.000000000 +0200 +++ new/jansson-2.15.1/test/bin/json_process.c 2026-07-01 20:34:38.000000000 +0200 @@ -19,6 +19,10 @@ #include <locale.h> #endif +#if defined(_MSC_VER) && (_MSC_VER < 1900) && !defined(snprintf) +#define snprintf _snprintf +#endif + #if _WIN32 #include <fcntl.h> /* for _O_BINARY */ #include <io.h> /* for _setmode() */ @@ -119,13 +123,14 @@ int ret; FILE *file; - sprintf(filename, "%s%c%s", path, dir_sep, fname); + snprintf(filename, sizeof(filename), "%s%c%s", path, dir_sep, fname); file = fopen(filename, "rb"); if (!file) { + size_t len = strlen(filename); if (conf.strip) - strcat(filename, ".strip"); + strncat(filename, ".strip", sizeof(filename) - len - 1); else - strcat(filename, ".normal"); + strncat(filename, ".normal", sizeof(filename) - len - 1); file = fopen(filename, "rb"); } if (!file) { @@ -159,13 +164,13 @@ json_t *json; json_error_t error; - sprintf(filename, "%s%cinput", test_path, dir_sep); + snprintf(filename, sizeof(filename), "%s%cinput", test_path, dir_sep); if (!(infile = fopen(filename, "rb"))) { fprintf(stderr, "Could not open \"%s\"\n", filename); return 2; } - sprintf(filename, "%s%cenv", test_path, dir_sep); + snprintf(filename, sizeof(filename), "%s%cenv", test_path, dir_sep); conffile = fopen(filename, "rb"); if (conffile) { read_conf(conffile); @@ -215,8 +220,8 @@ fclose(infile); if (!json) { - sprintf(errstr, "%d %d %d\n%s\n", error.line, error.column, error.position, - error.text); + snprintf(errstr, sizeof(errstr), "%d %d %d\n%s\n", error.line, error.column, + error.position, error.text); ret = cmpfile(errstr, test_path, "error"); return ret; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/ossfuzz/Makefile.am new/jansson-2.15.1/test/ossfuzz/Makefile.am --- old/jansson-2.15.0/test/ossfuzz/Makefile.am 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/ossfuzz/Makefile.am 2026-07-01 20:34:38.000000000 +0200 @@ -18,7 +18,9 @@ if USE_OSSFUZZERS noinst_PROGRAMS += \ - json_load_dump_fuzzer + json_load_dump_fuzzer \ + json_pack_unpack_fuzzer \ + json_equal_copy_fuzzer noinst_LIBRARIES += \ libstandaloneengine.a @@ -28,5 +30,13 @@ json_load_dump_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) json_load_dump_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static +json_pack_unpack_fuzzer_SOURCES = json_pack_unpack_fuzzer.cc testinput.h +json_pack_unpack_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +json_pack_unpack_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static + +json_equal_copy_fuzzer_SOURCES = json_equal_copy_fuzzer.cc testinput.h +json_equal_copy_fuzzer_CXXFLAGS = $(AM_CXXFLAGS) $(FUZZ_FLAG) +json_equal_copy_fuzzer_LDFLAGS = $(AM_LDFLAGS) -static + libstandaloneengine_a_SOURCES = standaloneengine.cc libstandaloneengine_a_CXXFLAGS = $(AM_CXXFLAGS) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/ossfuzz/json_equal_copy_fuzzer.cc new/jansson-2.15.1/test/ossfuzz/json_equal_copy_fuzzer.cc --- old/jansson-2.15.0/test/ossfuzz/json_equal_copy_fuzzer.cc 1970-01-01 01:00:00.000000000 +0100 +++ new/jansson-2.15.1/test/ossfuzz/json_equal_copy_fuzzer.cc 2026-07-01 20:34:38.000000000 +0200 @@ -0,0 +1,111 @@ +/* + * json_equal_copy_fuzzer.cc + * + * Fuzz target for jansson equality and copying APIs. + * The existing json_load_dump_fuzzer covers json_loads/json_dumps only. + * This harness exercises the following uncovered paths: + * + * json_loadb() - length-bounded load (no NUL required) + * json_equal() - deep equality comparison + * json_copy() - shallow copy + * json_deep_copy() - recursive deep copy + * json_object_update() - merge two objects + * json_object_update_existing() - update only existing keys + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "jansson.h" + +static uint8_t u8(const uint8_t **d, size_t *s) { + if (*s == 0) + return 0; + uint8_t v = **d; + (*d)++; + (*s)--; + return v; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 2) + return 0; + + uint8_t op = u8(&data, &size); + uint8_t flag_byte = u8(&data, &size); + + size_t load_flags = 0; + if (flag_byte & 0x01) + load_flags |= JSON_REJECT_DUPLICATES; + if (flag_byte & 0x02) + load_flags |= JSON_DECODE_ANY; + if (flag_byte & 0x04) + load_flags |= JSON_DISABLE_EOF_CHECK; + if (flag_byte & 0x08) + load_flags |= JSON_DECODE_INT_AS_REAL; + if (flag_byte & 0x10) + load_flags |= JSON_ALLOW_NUL; + + /* Parse with json_loadb — does not require NUL termination */ + json_error_t err; + json_t *root = json_loadb((const char *)data, size, load_flags, &err); + if (!root) + return 0; + + switch (op % 5) { + case 0: { + /* Deep copy then compare for equality */ + json_t *copy = json_deep_copy(root); + if (copy) { + json_equal(root, copy); + json_decref(copy); + } + break; + } + case 1: { + /* Shallow copy */ + json_t *shallow = json_copy(root); + if (shallow) { + json_equal(root, shallow); + json_decref(shallow); + } + break; + } + case 2: { + /* Compare root with itself */ + json_equal(root, root); + break; + } + case 3: { + /* json_object_update: merge root into a new empty object */ + if (json_is_object(root)) { + json_t *target = json_object(); + if (target) { + json_object_update(target, root); + json_decref(target); + } + } + break; + } + case 4: { + /* json_object_update_existing: only update keys present in target */ + if (json_is_object(root)) { + json_t *target = json_deep_copy(root); + if (target) { + json_t *patch = json_deep_copy(root); + if (patch) { + json_object_update_existing(target, patch); + json_decref(patch); + } + json_decref(target); + } + } + break; + } + } + + json_decref(root); + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/ossfuzz/json_pack_unpack_fuzzer.cc new/jansson-2.15.1/test/ossfuzz/json_pack_unpack_fuzzer.cc --- old/jansson-2.15.0/test/ossfuzz/json_pack_unpack_fuzzer.cc 1970-01-01 01:00:00.000000000 +0100 +++ new/jansson-2.15.1/test/ossfuzz/json_pack_unpack_fuzzer.cc 2026-07-01 20:34:38.000000000 +0200 @@ -0,0 +1,87 @@ +/* + * json_pack_unpack_fuzzer.cc + * + * Fuzz harness for jansson json_unpack() and json_deep_copy() paths. + * json_unpack() processes a format string + JSON value and extracts + * typed fields — it involves non-trivial string/type dispatch that + * is not exercised by the load/dump fuzzer. + * + * OSS-Fuzz build: compiled via test/ossfuzz/ossfuzz.sh. + */ +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <stdlib.h> + +#include "jansson.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + if (size < 2) + return 0; + + /* Use first byte to select format variant, rest as JSON input */ + uint8_t variant = data[0] % 8; + const char *json_str = (const char *)(data + 1); + size_t json_len = size - 1; + + json_error_t error; + json_t *root = json_loadb(json_str, json_len, + JSON_DECODE_ANY | JSON_ALLOW_NUL, &error); + if (root == NULL) + return 0; + + /* Exercise json_deep_copy on whatever we parsed */ + json_t *copy = json_deep_copy(root); + if (copy != NULL) + json_decref(copy); + + /* Exercise json_dumps with various flags */ + const int flag_sets[] = { + 0, + JSON_COMPACT, + JSON_ENSURE_ASCII, + JSON_SORT_KEYS, + JSON_COMPACT | JSON_ENSURE_ASCII, + }; + int flags = flag_sets[variant % 5]; + char *dumped = json_dumps(root, flags); + if (dumped != NULL) { + /* Re-load the dumped output for round-trip check */ + json_t *reparsed = json_loads(dumped, 0, NULL); + if (reparsed != NULL) + json_decref(reparsed); + free(dumped); + } + + /* Exercise json_unpack with simple format strings if root is an object */ + if (json_is_object(root)) { + json_t *val = NULL; + const char *key = NULL; + /* Unpack first key-value pair */ + void *iter = json_object_iter(root); + if (iter != NULL) { + key = json_object_iter_key(iter); + val = json_object_iter_value(iter); + (void)key; + (void)val; + } + + /* json_unpack with "o" format — extract object */ + json_t *out_obj = NULL; + json_unpack(root, "o", &out_obj); + + /* json_unpack with "{s?o}" — optional key lookup */ + json_t *field = NULL; + json_unpack(root, "{s?o}", "data", &field); + } + + /* Exercise json_unpack on array */ + if (json_is_array(root) && json_array_size(root) > 0) { + json_t *first = NULL; + json_unpack(root, "[o!]", &first); + } + + json_decref(root); + return 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/suites/api/test_copy.c new/jansson-2.15.1/test/suites/api/test_copy.c --- old/jansson-2.15.0/test/suites/api/test_copy.c 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/suites/api/test_copy.c 2026-07-01 20:34:38.000000000 +0200 @@ -364,6 +364,25 @@ json_decref(json); } +static void test_deep_copy_max_depth(void) { + json_t *json; + json_t *copy; + int i; + + json = json_array(); + for (i = 1; i < JSON_PARSER_MAX_DEPTH + 100; i++) { + json_t *outer = json_array(); + json_array_append_new(outer, json); + json = outer; + } + + copy = json_deep_copy(json); + if (copy) + fail("json_deep_copy didn't fail for a too deeply nested structure!"); + + json_decref(json); +} + static void run_tests() { test_copy_simple(); test_deep_copy_simple(); @@ -372,4 +391,5 @@ test_copy_object(); test_deep_copy_object(); test_deep_copy_circular_references(); + test_deep_copy_max_depth(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/suites/api/test_dump.c new/jansson-2.15.1/test/suites/api/test_dump.c --- old/jansson-2.15.0/test/suites/api/test_dump.c 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/suites/api/test_dump.c 2026-07-01 20:34:38.000000000 +0200 @@ -297,6 +297,39 @@ } } +static json_t *build_nested_array(int depth) { + json_t *json = json_array(); + int i; + + for (i = 1; i < depth; i++) { + json_t *outer = json_array(); + json_array_append_new(outer, json); + json = outer; + } + + return json; +} + +static void max_depth() { + json_t *json; + char *result; + + /* A structure nested exactly to the limit must still serialize. */ + json = build_nested_array(JSON_PARSER_MAX_DEPTH); + result = json_dumps(json, 0); + if (!result) + fail("json_dumps failed for a structure within the depth limit"); + free(result); + json_decref(json); + + /* A structure nested deeper than the limit must fail cleanly rather + than overflow the stack. */ + json = build_nested_array(JSON_PARSER_MAX_DEPTH + 100); + if (json_dumps(json, 0) != NULL) + fail("json_dumps didn't fail for a too deeply nested structure"); + json_decref(json); +} + static void run_tests() { encode_null(); encode_twice(); @@ -308,4 +341,5 @@ dumpb(); dumpfd(); embed(); + max_depth(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/suites/api/test_equal.c new/jansson-2.15.1/test/suites/api/test_equal.c --- old/jansson-2.15.0/test/suites/api/test_equal.c 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/suites/api/test_equal.c 2026-07-01 20:34:38.000000000 +0200 @@ -194,9 +194,34 @@ json_decref(value3); } +static void test_equal_max_depth() { + json_t *value1, *value2; + int i; + + value1 = json_array(); + value2 = json_array(); + for (i = 1; i < JSON_PARSER_MAX_DEPTH + 100; i++) { + json_t *outer1 = json_array(); + json_t *outer2 = json_array(); + json_array_append_new(outer1, value1); + json_array_append_new(outer2, value2); + value1 = outer1; + value2 = outer2; + } + + /* The structures are structurally equal, but exceed the depth limit; + json_equal must bail out rather than overflow the stack. */ + if (json_equal(value1, value2)) + fail("json_equal didn't bail out for a too deeply nested structure"); + + json_decref(value1); + json_decref(value2); +} + static void run_tests() { test_equal_simple(); test_equal_array(); test_equal_object(); test_equal_complex(); + test_equal_max_depth(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/suites/api/test_pack.c new/jansson-2.15.1/test/suites/api/test_pack.c --- old/jansson-2.15.0/test/suites/api/test_pack.c 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/suites/api/test_pack.c 2026-07-01 20:34:38.000000000 +0200 @@ -167,6 +167,12 @@ fail("json_pack string and length (int) refcount failed"); json_decref(value); + /* negative length (int) is rejected */ + if (json_pack_ex(&error, 0, "s#", "test", -2)) + fail("json_pack failed to catch negative length"); + check_error(json_error_invalid_argument, "Invalid length for string: -2", "<args>", 1, + 2, 2); + /* string and length (size_t), non-NUL terminated string */ value = json_pack("s%", buffer, (size_t)4); if (!json_is_string(value) || strcmp("test", json_string_value(value))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/jansson-2.15.0/test/suites/api/test_unpack.c new/jansson-2.15.1/test/suites/api/test_unpack.c --- old/jansson-2.15.0/test/suites/api/test_unpack.c 2022-08-09 20:56:05.000000000 +0200 +++ new/jansson-2.15.1/test/suites/api/test_unpack.c 2026-07-01 20:34:38.000000000 +0200 @@ -263,6 +263,57 @@ json_decref(j); json_decref(j2); + /* invalid types in objects */ + j = json_pack("{si}", "bar", 42); + j2 = json_pack("{ss}", "bar", "foo"); + if (!json_unpack_ex(j, &error, 0, "{ss}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected string, got integer for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j, &error, 0, "{sn}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected null, got integer for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j, &error, 0, "{sb}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected true or false, got integer for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j2, &error, 0, "{si}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected integer, got string for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j2, &error, 0, "{sI}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected integer, got string for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j, &error, 0, "{sf}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected real, got integer for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j2, &error, 0, "{sF}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected real or integer, got string for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j, &error, 0, "{s[i]}", "bar")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected array, got integer for key bar", + "<validation>", 1, 3, 3); + + if (!json_unpack_ex(j, &error, 0, "{s{si}}", "bar", "foo")) + fail("json_unpack failed to catch invalid type in object"); + check_error(json_error_wrong_type, "Expected object, got integer for key bar", + "<validation>", 1, 3, 3); + + json_decref(j); + json_decref(j2); + /* Array index out of range */ j = json_pack("[i]", 1); if (!json_unpack_ex(j, &error, 0, "[ii]", &i1, &i2))
