Fix jq segfault when used in threads
Resolves: rhbz#2176542
This commit is contained in:
parent
1e15065d35
commit
4dbfbc94c2
244
0003-fix-pthread-segfault.patch
Normal file
244
0003-fix-pthread-segfault.patch
Normal file
@ -0,0 +1,244 @@
|
||||
commit fead9669cb67badb22789d3ed1888779ed85c679
|
||||
Author: Tomas Halman <thalman@redhat.com>
|
||||
Date: Thu Mar 9 11:34:44 2023 +0100
|
||||
|
||||
Test that jq works in threads
|
||||
|
||||
This patch implements test that searches a key in simple
|
||||
json in pthread.
|
||||
|
||||
diff -up jq-1.6/src/jq_test.c.orig jq-1.6/src/jq_test.c
|
||||
--- jq-1.6/src/jq_test.c.orig 2023-03-09 14:02:16.980062057 +0100
|
||||
+++ jq-1.6/src/jq_test.c 2023-03-09 14:03:38.347017032 +0100
|
||||
@@ -2,12 +2,17 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
+#ifdef HAVE_PTHREAD
|
||||
+#include <pthread.h>
|
||||
+#endif
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
|
||||
static void jv_test();
|
||||
static void run_jq_tests(jv, int, FILE *, int, int);
|
||||
-
|
||||
+#ifdef HAVE_PTHREAD
|
||||
+static void run_jq_pthread_tests();
|
||||
+#endif
|
||||
|
||||
int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) {
|
||||
FILE *testdata = stdin;
|
||||
@@ -32,6 +37,9 @@ int jq_testsuite(jv libdirs, int verbose
|
||||
}
|
||||
}
|
||||
run_jq_tests(libdirs, verbose, testdata, skip, take);
|
||||
+#ifdef HAVE_PTHREAD
|
||||
+ run_jq_pthread_tests();
|
||||
+#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -105,7 +113,7 @@ static void run_jq_tests(jv lib_dirs, in
|
||||
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
|
||||
break;
|
||||
}
|
||||
-
|
||||
+
|
||||
must_fail = 0;
|
||||
check_msg = 0;
|
||||
|
||||
@@ -229,7 +237,7 @@ static void run_jq_tests(jv lib_dirs, in
|
||||
total_skipped = tests_to_skip - skip;
|
||||
}
|
||||
|
||||
- printf("%d of %d tests passed (%d malformed, %d skipped)\n",
|
||||
+ printf("%d of %d tests passed (%d malformed, %d skipped)\n",
|
||||
passed, tests, invalid, total_skipped);
|
||||
|
||||
if (skip > 0) {
|
||||
@@ -241,6 +249,88 @@ static void run_jq_tests(jv lib_dirs, in
|
||||
}
|
||||
|
||||
|
||||
+/// pthread regression test
|
||||
+#ifdef HAVE_PTHREAD
|
||||
+#define NUMBER_OF_THREADS 3
|
||||
+struct test_pthread_data {
|
||||
+ int result;
|
||||
+};
|
||||
+
|
||||
+static int test_pthread_jq_parse(jq_state *jq, struct jv_parser *parser)
|
||||
+{
|
||||
+ int rv = 0;
|
||||
+ jv value;
|
||||
+
|
||||
+ value = jv_parser_next(parser);
|
||||
+ while (jv_is_valid(value)) {
|
||||
+ jq_start(jq, value, 0);
|
||||
+ jv result = jq_next(jq);
|
||||
+
|
||||
+ while (jv_is_valid(result)) {
|
||||
+ jv_free(result);
|
||||
+ result = jq_next(jq);
|
||||
+ }
|
||||
+ jv_free(result);
|
||||
+ value = jv_parser_next(parser);
|
||||
+ }
|
||||
+ jv_free(value);
|
||||
+ return rv;
|
||||
+}
|
||||
+
|
||||
+static void *test_pthread_run(void *ptr) {
|
||||
+ int rv;
|
||||
+ jq_state *jq;
|
||||
+ const char *prg = ".data";
|
||||
+ const char *buf = "{ \"data\": 1 }";
|
||||
+ struct test_pthread_data *data = ptr;
|
||||
+
|
||||
+ jq = jq_init();
|
||||
+ if (jq_compile(jq, prg) == 0) {
|
||||
+ jq_teardown(&jq);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ struct jv_parser *parser = jv_parser_new(0);
|
||||
+ jv_parser_set_buf(parser, buf, strlen(buf), 0);
|
||||
+ rv = test_pthread_jq_parse(jq, parser);
|
||||
+
|
||||
+ data->result = rv;
|
||||
+
|
||||
+ jv_parser_free(parser);
|
||||
+ jq_teardown(&jq);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static void run_jq_pthread_tests() {
|
||||
+ pthread_t threads[NUMBER_OF_THREADS];
|
||||
+ struct test_pthread_data data[NUMBER_OF_THREADS];
|
||||
+ int createerror;
|
||||
+ int a;
|
||||
+
|
||||
+ memset(&threads, 0, sizeof(threads));
|
||||
+ memset(&data, 0, sizeof(data));
|
||||
+
|
||||
+ // Create all threads
|
||||
+ for (a = 0; a < NUMBER_OF_THREADS; ++a) {
|
||||
+ createerror = pthread_create(&threads[a], NULL, test_pthread_run, &data[a]);
|
||||
+ assert(createerror == 0);
|
||||
+ }
|
||||
+
|
||||
+ // wait for all threads
|
||||
+ for(a = 0; a < NUMBER_OF_THREADS; ++a) {
|
||||
+ if (threads[a] != 0) {
|
||||
+ pthread_join(threads[a], NULL);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // check results
|
||||
+ for(a = 0; a < NUMBER_OF_THREADS; ++a) {
|
||||
+ assert(data[a].result == 0);
|
||||
+ }
|
||||
+}
|
||||
+#endif // HAVE_PTHREAD
|
||||
+
|
||||
+
|
||||
static void jv_test() {
|
||||
/// JSON parser regression tests
|
||||
{
|
||||
|
||||
commit f05c5bb521f404592b137e02a1b8abd50da87ca3
|
||||
Author: Tomas Halman <thalman@redhat.com>
|
||||
Date: Wed Mar 1 20:07:28 2023 +0100
|
||||
|
||||
Fix segmentation fault when using jq in threads
|
||||
|
||||
In previous code nomem_handler in jv_alloc.c is called only once
|
||||
and therefore the structure is not initialized for second and
|
||||
following threads.
|
||||
|
||||
This leads to segmentation fault in multi-threading environment.
|
||||
|
||||
This patch moves initialization of nomem_handler out of the
|
||||
pthread_once call.
|
||||
|
||||
diff -up jq-1.6/src/jv_alloc.c.orig jq-1.6/src/jv_alloc.c
|
||||
--- jq-1.6/src/jv_alloc.c.orig 2018-11-02 02:49:29.000000000 +0100
|
||||
+++ jq-1.6/src/jv_alloc.c 2023-03-09 14:13:47.810177037 +0100
|
||||
@@ -45,9 +45,11 @@ static void memory_exhausted() {
|
||||
#ifdef HAVE_PTHREAD_KEY_CREATE
|
||||
#include <pthread.h>
|
||||
|
||||
-pthread_key_t nomem_handler_key;
|
||||
-pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
+static pthread_key_t nomem_handler_key;
|
||||
+static pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
+/* tsd_fini is called on application exit */
|
||||
+/* it clears the nomem_handler allocated in the main thread */
|
||||
static void tsd_fini(void) {
|
||||
struct nomem_handler *nomem_handler;
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
@@ -57,30 +59,45 @@ static void tsd_fini(void) {
|
||||
}
|
||||
}
|
||||
|
||||
+/* The tsd_fini_thread is a destructor set by calling */
|
||||
+/* pthread_key_create(&nomem_handler_key, tsd_fini_thread) */
|
||||
+/* It is called when thread ends */
|
||||
+static void tsd_fini_thread(void *nomem_handler) {
|
||||
+ free(nomem_handler);
|
||||
+}
|
||||
+
|
||||
static void tsd_init(void) {
|
||||
- if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
|
||||
- fprintf(stderr, "error: cannot create thread specific key");
|
||||
+ if (pthread_key_create(&nomem_handler_key, tsd_fini_thread) != 0) {
|
||||
+ fprintf(stderr, "jq: error: cannot create thread specific key");
|
||||
abort();
|
||||
}
|
||||
if (atexit(tsd_fini) != 0) {
|
||||
- fprintf(stderr, "error: cannot set an exit handler");
|
||||
+ fprintf(stderr, "jq: error: cannot set an exit handler");
|
||||
abort();
|
||||
}
|
||||
- struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));
|
||||
- if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
- fprintf(stderr, "error: cannot set thread specific data");
|
||||
- abort();
|
||||
+}
|
||||
+
|
||||
+static void tsd_init_nomem_handler(void)
|
||||
+{
|
||||
+ if (pthread_getspecific(nomem_handler_key) == NULL) {
|
||||
+ struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));
|
||||
+ if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
+ fprintf(stderr, "jq: error: cannot set thread specific data");
|
||||
+ abort();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
pthread_once(&mem_once, tsd_init); // cannot fail
|
||||
+ tsd_init_nomem_handler();
|
||||
+
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler == NULL) {
|
||||
handler(data);
|
||||
- fprintf(stderr, "error: cannot allocate memory\n");
|
||||
+ fprintf(stderr, "jq: error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
nomem_handler->handler = handler;
|
||||
@@ -91,6 +108,8 @@ static void memory_exhausted() {
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
pthread_once(&mem_once, tsd_init);
|
||||
+ tsd_init_nomem_handler();
|
||||
+
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler)
|
||||
nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety
|
7
jq.spec
7
jq.spec
@ -1,6 +1,6 @@
|
||||
Name: jq
|
||||
Version: 1.6
|
||||
Release: 14%{?dist}
|
||||
Release: 15%{?dist}
|
||||
Summary: Command-line JSON processor
|
||||
|
||||
License: MIT and ASL 2.0 and CC-BY and GPLv3
|
||||
@ -10,6 +10,7 @@ Source0: https://github.com/stedolan/jq/releases/download/%{name}-%{versi
|
||||
Patch0: jq-decimal-literal-number.patch
|
||||
Patch1: 0001-iterration-problem-for-non-decimal-string.patch
|
||||
Patch2: 0002-add-mantest.patch
|
||||
Patch3: 0003-fix-pthread-segfault.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: flex
|
||||
@ -100,6 +101,10 @@ make check
|
||||
|
||||
|
||||
%changelog
|
||||
* Thu Mar 9 2023 Tomas Halman <thalman@redhat.com> - 1.6-15
|
||||
- jq segfault when used in threads
|
||||
- Resolves: rhbz#2176542
|
||||
|
||||
* Fri Nov 4 2022 Tomas Halman <thalman@redhat.com> - 1.6-6
|
||||
- Add mantest to the gating
|
||||
- Related: rhbz#2049594
|
||||
|
Loading…
Reference in New Issue
Block a user