NetBSD Problem Report #59230
From brad@anduin.eldar.org Fri Mar 28 17:30:57 2025
Return-Path: <brad@anduin.eldar.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id BCB091A9239
for <gnats-bugs@gnats.NetBSD.org>; Fri, 28 Mar 2025 17:30:57 +0000 (UTC)
Message-Id: <202503281730.52SHUrW5016805@anduin.eldar.org>
Date: Fri, 28 Mar 2025 13:30:53 -0400 (EDT)
From: brad@anduin.eldar.org
Reply-To: brad@anduin.eldar.org
To: gnats-bugs@NetBSD.org
Subject: Bug in libmj, might effect netpgp
X-Send-Pr-Version: 3.95
>Number: 59230
>Category: lib
>Synopsis: Bug in libmj, might effect netpgp
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Fri Mar 28 17:35:00 +0000 2025
>Originator: brad@anduin.eldar.org
>Release: NetBSD 10.99.12
>Organization:
eldar.org
>Environment:
System: NetBSD bradtest.eot11.eldar.org 10.99.12 NetBSD 10.99.12 (GENERIC_TEST) #0: Sun Mar 9 09:02:09 EDT 2025 brad@samwise.nat.eldar.org:/usr/src/sys/arch/amd64/compile/GENERIC_TEST amd64
Architecture: x86_64
Machine: amd64
>Description:
libmj, a minimal JSON library, comes from the netpgp package which is
in base. It appears that libmj is a first class library with a man
page that describes it use. It was imported quite some time ago.While
using libmj for something other than netpgp, I noticed a bug.
If there is a JSON array inside of a JSON object, using the libmj
sense of "array" and "object", the resulting JSON superatom will not
print correctly with mj_asprintf(), but will with mj_snprintf().
Since mj_asprintf() is just a small wrapper that recursively finds the
size of the atom passed to it, callocs up some space with the
resulting size and then calls mj_snprintf(), the bug is probably in
the function mj_string_size(), which is used to calculate the JSON
atom sizes. It appears that mj_string_size() might not account for
some white space present in arrays.
A test program is included below that demostrates the problem, and a
possible patch.
>How-To-Repeat:
Call the following program z.c, and compile it like this:
cc -DDO_MJ_ASPRINTF -DDO_MJ_SNPRINTF -o z z.c -lmj
When executed, the program will generate this output:
{ "OUTER_OBJECT":{ "OBJECT_0":[ { "INNER_ARRAY_0":[ 0, 0 ]
, "INNER_ARRAY_1":[ 0, 0 ]
}
, { "INNER_ARRAY_0":[ 0, 0 ]
, "INNER_ARRAY_1":[ 1, 1 ]
}
]
, "OBJECT_1":[ { "INNER_ARRAY_0":[ 1, 1 ]
, "INNER_ARRAY_1":[ 1, 1 ]
}
, { "INNER_ARRAY_0":[ 1, 1 ]
, "INNER_ARRAY_1":[ 2, 2 ]
}
]
}
, "INCLUDE_INT":10191, "INCLUDE_INT_AGAIN
-----
{ "OUTER_OBJECT":{ "OBJECT_0":[ { "INNER_ARRAY_0":[ 0, 0 ]
, "INNER_ARRAY_1":[ 0, 0 ]
}
, { "INNER_ARRAY_0":[ 0, 0 ]
, "INNER_ARRAY_1":[ 1, 1 ]
}
]
, "OBJECT_1":[ { "INNER_ARRAY_0":[ 1, 1 ]
, "INNER_ARRAY_1":[ 1, 1 ]
}
, { "INNER_ARRAY_0":[ 1, 1 ]
, "INNER_ARRAY_1":[ 2, 2 ]
}
]
}
, "INCLUDE_INT":10191, "INCLUDE_INT_AGAIN":20382 }
Note that the first print of the main JSON atom clips off a number of
characters and resultes in invalid JSON. In fact, a character will be
lost for every array present. The second one has everything and is
correct.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mj.h>
void
main()
{
mj_t array;
mj_t obj;
mj_t inner_obj;
mj_t outer_obj;
char buf[10000];
char obuf[10000];
char *s;
int cc;
int anint = 10191;
(void) memset(&obj, 0x0, sizeof(obj));
(void) memset(&outer_obj, 0x0, sizeof(outer_obj));
mj_create(&obj, "object");
mj_create(&outer_obj, "object");
for(int k=0;k < 2;k++) {
(void) memset(&array, 0x0, sizeof(array));
mj_create(&array, "array");
for(int64_t j=0;j < 2;j++) {
(void) memset(&inner_obj, 0x0, sizeof(inner_obj));
mj_create(&inner_obj, "object");
for(int64_t i=0;i < 2;i++) {
sprintf(buf,"INNER_ARRAY_%d",i);
mj_t inner_array;
(void) memset(&inner_array, 0x0, sizeof(inner_array));
mj_create(&inner_array, "array");
for(int64_t l=0;l < 2;l++) {
mj_append(&inner_array,"integer", (i * j) + k);
}
mj_append_field(&inner_obj, buf, "array", &inner_array);
}
mj_append(&array,"object",&inner_obj);
mj_delete(&inner_obj);
}
sprintf(buf,"OBJECT_%d",k);
mj_append_field(&outer_obj, buf, "array", &array);
mj_delete(&array);
}
mj_append_field(&obj, "OUTER_OBJECT", "object", &outer_obj);
mj_append_field(&obj, "INCLUDE_INT", "integer", (int64_t)anint);
mj_append_field(&obj, "INCLUDE_INT_AGAIN", "integer", (int64_t)anint * 2);
#ifdef DO_MJ_ASPRINTF
cc = mj_asprint(&s, &obj, MJ_JSON_ENCODE);
printf("%s",s);
printf("\n\n");
#endif
printf("-----\n");
#ifdef DO_MJ_SNPRINTF
mj_snprint(obuf,sizeof(obuf),&obj,MJ_JSON_ENCODE);
printf("%s",obuf);
printf("\n\n");
#endif
}
>Fix:
This patch allows the test program to work as expected, but I don't
really know if it is the correct solution. The status and
dispensation of netpgp, as a imported package, is also unknown. While
a number of other atom combinations were tested, I did not test just
every possible way one might generate JSON. I mostly had a need to
put JSON arrays inside of JSON objects hopefully using a library that
was in the base system.
--- crypto/external/bsd/netpgp/dist/src/libmj/mj.c.ORIG3 2022-08-27 15:25:42.278799705 -0400
+++ crypto/external/bsd/netpgp/dist/src/libmj/mj.c 2025-03-28 12:42:23.590223734 -0400
@@ -502,7 +502,7 @@
cc += (i < atom->c - 1) ? 2 : 1;
}
/* end ']' */
- return cc + 1;
+ return cc + 2;
case MJ_OBJECT:
/* start '{ ' */
for (cc = 2, i = 0 ; i < atom->c ; i += 2) {
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2025
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.