FreeBSD Port: dns/bind920 JSON statistics channel leads to increasing memory usage
Felix J. Ogris
fjo-lists at ogris.de
Fri Jul 31 18:32:33 UTC 2026
Hi,
json-c 0.19 wraps the crucial call of _json_object_put_maybe_free() in an assertion. See json_object.c line 442. This gets removed in a release build due to the -DNDEBUG compiler option. It's been fixed in the master branch. Trivial backport patch for 0.19 as in FreeBSD ports attached.
BR,
Felix
Am 30.07.26 um 22:23 schrieb Felix J. Ogris:
> Hi,
>
> BIND 9.20.26 linked against json-c 0.19 on FreeBSD 15.1 consumes more and more memory if you query its JSON statistics channel, around 500k to 1 meg per query. With json-c 0.18 or if you query the XML statistics channel, memory usage remains almost constant. Linux is unaffected.
>
> named -V:
> BIND 9.20.26 (Stable Release) <id:>
> running on FreeBSD amd64 15.1-RELEASE-p2 FreeBSD 15.1-RELEASE-p2 releng/15.1-n283596-aadd58dddcbc GENERIC (FreeBSD 15.1-RELEASE-p2)
> built by make with '--enable-dnsrps' '--localstatedir=/var' '--sysconfdir=/usr/local/etc/namedb' '--with-openssl=/usr' '--with-readline=libedit' '--disable-tracing' '--disable-dnstap' '--disable-fixed-rrset' '--disable-geoip' '--without-maxminddb' '--without-gssapi' '--with-libidn2=/usr/local' '--disable-largefile' '--without-lmdb' '--disable-querytrace' '--with-json-c' '--with-libxml2' '--enable-tcp-fastopen' '--prefix=/usr/local' '--mandir=/usr/local/share/man' '--disable-silent-rules' '--infodir=/usr/local/share/info/' '--build=amd64-portbld-freebsd15.1' 'build_alias=amd64-portbld-freebsd15.1' 'CC=cc' 'CFLAGS=-O2 -pipe -DLIBICONV_PLUG -fstack-protector-strong -isystem /usr/local/include -fno-strict-aliasing ' 'LDFLAGS= -L/usr/local/lib -ljson-c ' 'LIBS=-L/usr/local/lib' 'CPPFLAGS=-DLIBICONV_PLUG -isystem /usr/local/include' 'CPP=cpp' 'PKG_CONFIG=pkgconf' 'PKG_CONFIG_LIBDIR=/usr/ports/dns/bind920/work/.pkgconfig:/usr/local/libdata/pkgconfig:/usr/local/share/pkgconfig:/
> usr/libdata/pkgconfig'
> compiled by CLANG FreeBSD Clang 19.1.7 (https://github.com/llvm/llvm-project.git llvmorg-19.1.7-0-gcd708029e0b2)
> compiled with OpenSSL version: OpenSSL 3.5.6 7 Apr 2026
> linked to OpenSSL version: OpenSSL 3.5.6 7 Apr 2026
> compiled with libuv version: 1.52.1
> linked to libuv version: 1.52.1
> compiled with liburcu version: 0.15.6
> compiled with system jemalloc version: 2024073001
> compiled with libnghttp2 version: 1.69.0
> linked to libnghttp2 version: 1.69.0
> compiled with libxml2 version: 2.15.3
> linked to libxml2 version: 21503
> compiled with json-c version: 0.19
> linked to json-c version: 0.19
> compiled with zlib version: 1.3.2
> linked to zlib version: 1.3.2
> threads support is enabled
>
> My /etc/malloc.conf is symlinked to "retain:false" to let jemalloc release unused memory immediately.
>
> How to trigger this behaviour on an otherwise idle system:
>
> $ date
> Thu Jul 30 21:54:35 CEST 2026
> $ ps axuww | grep -E "^USER|sbin/named" | grep -v grep
> USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
> bind 28389 0.0 0.5 61260 30556 - Is 21:53 0:00.16 /usr/local/sbin/named -u bind -c /usr/local/etc/namedb/named.conf
> $ for i in `jot 1000`; do fetch -q -o /dev/null http://127.0.0.1:8053/json; done
> $ date
> Thu Jul 30 21:55:41 CEST 2026
> $ ps axuww | grep -E "^USER|sbin/named" | grep -v grep
> USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
> bind 28389 23.2 9.5 631780 592084 - Ss 21:53 0:08.30 /usr/local/sbin/named -u bind -c /usr/local/etc/namedb/named.conf
> $
>
> Even after a while memory isn't released:
>
> $ date
> Thu Jul 30 22:21:12 CEST 2026
> $ ps axuww | grep -E "^USER|sbin/named" | grep -v grep
> USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
> bind 28389 0.0 9.7 646656 606956 - Is 21:53 0:08.80 /usr/local/sbin/named -u bind -c /usr/local/etc/namedb/named.conf
> $
>
> When comparing json-c 0.18 to 0.19, I noticed a rewrite of json_object_put() which calls _json_object_put_maybe_free(). I assume this somehow leads to a memory leak just on FreeBSD like unpatched liburcu < 0.15.6.
>
> BR,
> Felix
-------------- next part --------------
--- json_object.c.orig 2026-06-27 15:39:00.000000000 +0200
+++ json_object.c 2026-07-31 20:11:19.459962000 +0200
@@ -434,12 +434,15 @@
// All slots are cleared, now pop back up to the parent
{
+ int rc;
json_object *parent = jso->_delete_parent;
// jso is a child that's already been detached from its parent
// so we need to actually free it now
assert(jso->_ref_count == 0);
jso->_ref_count++; // We're the exclusive owner of jso, non-atomic add is ok.
- assert(_json_object_put_maybe_free(jso, 1) == 0);
+ rc = _json_object_put_maybe_free(jso, 1);
+ assert(rc == 0);
+ (void)rc;
jso = parent;
// iteration will be reset at the top of the loop
}
More information about the bind-users
mailing list