NetworkManager/shared/nm-glib-aux/nm-json-aux.c
Rasmus Thomsen d38b5d92ee
shared/nm-glib-aux: don't use RTLD_DEEPBIND on musl
[thaller@redhat.com: original patch modified]
2020-12-13 17:10:53 +01:00

287 lines
7.8 KiB
C

/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2017 - 2019 Red Hat, Inc.
*/
#include "nm-default.h"
#include "nm-json-aux.h"
#include <dlfcn.h>
/*****************************************************************************/
/* If RTLD_DEEPBIND isn't available just ignore it. This can cause problems
* with jansson, json-glib, and cjson symbols clashing (and as such crashing the
* program). But that needs to be fixed by the json libraries, and it is by adding
* symbol versioning in recent versions. */
#ifndef RTLD_DEEPBIND
#define RTLD_DEEPBIND 0
#endif
/*****************************************************************************/
static void
_gstr_append_string_len(GString *gstr, const char *str, gsize len)
{
g_string_append_c(gstr, '\"');
while (len > 0) {
gsize n;
const char *end;
gboolean valid;
nm_assert(len > 0);
valid = g_utf8_validate(str, len, &end);
nm_assert(end && end >= str && end <= &str[len]);
if (end > str) {
const char *s;
for (s = str; s < end; s++) {
nm_assert(s[0] != '\0');
if (s[0] < 0x20) {
const char *text;
switch (s[0]) {
case '\\':
text = "\\\\";
break;
case '\"':
text = "\\\"";
break;
case '\b':
text = "\\b";
break;
case '\f':
text = "\\f";
break;
case '\n':
text = "\\n";
break;
case '\r':
text = "\\r";
break;
case '\t':
text = "\\t";
break;
default:
g_string_append_printf(gstr, "\\u%04X", (guint) s[0]);
continue;
}
g_string_append(gstr, text);
continue;
}
if (NM_IN_SET(s[0], '\\', '\"'))
g_string_append_c(gstr, '\\');
g_string_append_c(gstr, s[0]);
}
} else
nm_assert(!valid);
if (valid) {
nm_assert(end == &str[len]);
break;
}
nm_assert(end < &str[len]);
if (end[0] == '\0') {
/* there is a NUL byte in the string. Technically this is valid UTF-8, so we
* encode it there. However, this will likely result in a truncated string when
* parsing. */
g_string_append(gstr, "\\u0000");
} else {
/* the character is not valid UTF-8. There is nothing we can do about it, because
* JSON can only contain UTF-8 and even the escape sequences can only escape Unicode
* codepoints (but not binary).
*
* The argument is not a string (in any known encoding), hence we cannot represent
* it as a JSON string (which are unicode strings).
*
* Print an underscore instead of the invalid char :) */
g_string_append_c(gstr, '_');
}
n = str - end;
nm_assert(n < len);
n++;
str += n;
len -= n;
}
g_string_append_c(gstr, '\"');
}
void
nm_json_gstr_append_string_len(GString *gstr, const char *str, gsize n)
{
g_return_if_fail(gstr);
_gstr_append_string_len(gstr, str, n);
}
void
nm_json_gstr_append_string(GString *gstr, const char *str)
{
g_return_if_fail(gstr);
if (!str)
g_string_append(gstr, "null");
else
_gstr_append_string_len(gstr, str, strlen(str));
}
void
nm_json_gstr_append_obj_name(GString *gstr, const char *key, char start_container)
{
g_return_if_fail(gstr);
g_return_if_fail(key);
nm_json_gstr_append_string(gstr, key);
if (start_container != '\0') {
nm_assert(NM_IN_SET(start_container, '[', '{'));
g_string_append_printf(gstr, ": %c ", start_container);
} else
g_string_append(gstr, ": ");
}
/*****************************************************************************/
typedef struct {
NMJsonVt vt;
void * dl_handle;
} NMJsonVtInternal;
static NMJsonVtInternal *
_nm_json_vt_internal_load(void)
{
NMJsonVtInternal *v;
const char * soname;
void * handle;
v = g_new0(NMJsonVtInternal, 1);
#if WITH_JANSSON && defined(JANSSON_SONAME)
G_STATIC_ASSERT_EXPR(NM_STRLEN(JANSSON_SONAME) > 0);
nm_assert(strlen(JANSSON_SONAME) > 0);
soname = JANSSON_SONAME;
#elif !WITH_JANSSON && !defined(JANSSON_SONAME)
soname = NULL;
#else
#error "WITH_JANSON and JANSSON_SONAME are defined inconsistently."
#endif
if (!soname)
return v;
handle = dlopen(soname,
RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE
#if !defined(ASAN_BUILD)
| RTLD_DEEPBIND
#endif
| 0);
if (!handle)
return v;
#define TRY_BIND_SYMBOL(symbol) \
G_STMT_START \
{ \
void *_sym = dlsym(handle, #symbol); \
\
if (!_sym) \
goto fail_symbol; \
v->vt.nm_##symbol = _sym; \
} \
G_STMT_END
TRY_BIND_SYMBOL(json_array);
TRY_BIND_SYMBOL(json_array_append_new);
TRY_BIND_SYMBOL(json_array_get);
TRY_BIND_SYMBOL(json_array_size);
TRY_BIND_SYMBOL(json_delete);
TRY_BIND_SYMBOL(json_dumps);
TRY_BIND_SYMBOL(json_false);
TRY_BIND_SYMBOL(json_integer);
TRY_BIND_SYMBOL(json_integer_value);
TRY_BIND_SYMBOL(json_loads);
TRY_BIND_SYMBOL(json_object);
TRY_BIND_SYMBOL(json_object_del);
TRY_BIND_SYMBOL(json_object_get);
TRY_BIND_SYMBOL(json_object_iter);
TRY_BIND_SYMBOL(json_object_iter_key);
TRY_BIND_SYMBOL(json_object_iter_next);
TRY_BIND_SYMBOL(json_object_iter_value);
TRY_BIND_SYMBOL(json_object_key_to_iter);
TRY_BIND_SYMBOL(json_object_set_new);
TRY_BIND_SYMBOL(json_object_size);
TRY_BIND_SYMBOL(json_string);
TRY_BIND_SYMBOL(json_string_value);
TRY_BIND_SYMBOL(json_true);
v->vt.loaded = TRUE;
v->dl_handle = handle;
return v;
fail_symbol:
dlclose(&handle);
*v = (NMJsonVtInternal){};
return v;
}
const NMJsonVt *_nm_json_vt_ptr = NULL;
const NMJsonVt *
_nm_json_vt_init(void)
{
NMJsonVtInternal *v;
again:
v = g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr);
if (G_UNLIKELY(!v)) {
v = _nm_json_vt_internal_load();
if (!g_atomic_pointer_compare_and_exchange((gpointer *) &_nm_json_vt_ptr, NULL, v)) {
if (v->dl_handle)
dlclose(v->dl_handle);
g_free(v);
goto again;
}
/* we transfer ownership. */
}
nm_assert(v && v == g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr));
return &v->vt;
}
const NMJsonVt *
nmtst_json_vt_reset(gboolean loaded)
{
NMJsonVtInternal *v_old;
NMJsonVtInternal *v;
v_old = g_atomic_pointer_get((gpointer *) &_nm_json_vt_ptr);
if (!loaded) {
/* load a fake instance for testing. */
v = g_new0(NMJsonVtInternal, 1);
} else
v = _nm_json_vt_internal_load();
if (!g_atomic_pointer_compare_and_exchange((gpointer *) &_nm_json_vt_ptr, v_old, v))
g_assert_not_reached();
if (v_old) {
if (v_old->dl_handle)
dlclose(v_old->dl_handle);
g_free((gpointer *) v_old);
}
return v->vt.loaded ? &v->vt : NULL;
}