When two different threads use json_loads to convert a string to a JSON object, they can disturb each other: one of the threads may run into an assertion failure and crash the program.
How to reproduce:
On a system with glibc, compile and run the following program foo.c:
#include <locale.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <jansson.h>
static void *
thread1_func (void *arg)
{
locale_t thread1_locale = newlocale (LC_ALL_MASK, "en_US.UTF-8", NULL);
uselocale (thread1_locale);
char input[] = "{\"a\":1.5}";
for (;;)
{
json_error_t e;
json_t *j = json_loads (input, 0, &e);
if (!json_is_object (j))
abort ();
json_t *k = json_object_get (j, "a");
if (!json_is_real (k))
abort ();
if (json_real_value (k) != 1.5)
{
fprintf (stderr, "thread1 disturbed by thread2, read %g\n", json_real_value (k)); fflush (stderr);
abort ();
}
json_decref (j);
}
/*NOTREACHED*/
}
static void *
thread2_func (void *arg)
{
locale_t thread2_locale = newlocale (LC_ALL_MASK, "fr_FR.UTF-8", NULL);
uselocale (thread2_locale);
char input[] = "{\"b\":2.5}";
for (;;)
{
json_error_t e;
json_t *j = json_loads (input, 0, &e);
if (!json_is_object (j))
abort ();
json_t *k = json_object_get (j, "b");
if (!json_is_real (k))
abort ();
if (json_real_value (k) != 2.5)
{
fprintf (stderr, "thread2 disturbed by thread1, read %g\n", json_real_value (k)); fflush (stderr);
abort ();
}
json_decref (j);
}
/*NOTREACHED*/
}
int
main (int argc, char *argv[])
{
/* Create the threads. */
pthread_t thread1, thread2;
pthread_create (&thread1, NULL, thread1_func, NULL);
pthread_create (&thread2, NULL, thread2_func, NULL);
/* Let them run for 1 second. */
{
struct timespec duration;
duration.tv_sec = (argc > 1 ? atoi (argv[1]) : 1);
duration.tv_nsec = 0;
nanosleep (&duration, NULL);
}
return 0;
}
Like this:
$ gcc -Wall foo.c /usr/lib/x86_64-linux-gnu/libjansson.a -lpthread
$ ./a.out
a.out: strconv.c:68: jsonp_strtod: Assertion `end == strbuffer->value + strbuffer->length' failed.
Aborted (core dumped)
Here is the gdb stack trace:
(gdb) where
#0 0x00007ffff7e079fc in pthread_kill () from /lib/x86_64-linux-gnu/libc.so.6
#1 0x00007ffff7db3476 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00007ffff7d997f3 in abort () from /lib/x86_64-linux-gnu/libc.so.6
#3 0x00007ffff7d9971b in ?? () from /lib/x86_64-linux-gnu/libc.so.6
#4 0x00007ffff7daae96 in __assert_fail () from /lib/x86_64-linux-gnu/libc.so.6
#5 0x0000555555557ae5 in jsonp_strtod ()
#6 0x0000555555556670 in lex_scan.isra ()
#7 0x0000555555556b14 in parse_value ()
#8 0x0000555555556e65 in parse_json ()
#9 0x0000555555556feb in json_loads ()
#10 0x000055555555564c in thread1_func ()
Note: When one of the pthread_create lines is commented out, such that only one thread is created, the program runs fine. Only when both pthread_create lines are enabled, does the program crash. This proves that there is an interaction between the threads.
Note: The test program fulfils the rules documented in https://jansson.readthedocs.io/en/latest/threadsafety.html :
- The
json_t objects are private to each of the threads.
- It does not invoke
setlocale.
When two different threads use
json_loadsto convert a string to a JSON object, they can disturb each other: one of the threads may run into an assertion failure and crash the program.How to reproduce:
On a system with glibc, compile and run the following program
foo.c:Like this:
Here is the gdb stack trace:
Note: When one of the
pthread_createlines is commented out, such that only one thread is created, the program runs fine. Only when bothpthread_createlines are enabled, does the program crash. This proves that there is an interaction between the threads.Note: The test program fulfils the rules documented in https://jansson.readthedocs.io/en/latest/threadsafety.html :
json_tobjects are private to each of the threads.setlocale.