From dd428301eb6f02542015121d7b08d9997f137e50 Mon Sep 17 00:00:00 2001 From: Sebastien Bacher Date: Tue, 12 Mar 2019 15:13:33 +0100 Subject: New upstream version 1.15.91 --- shared/nm-utils/nm-c-list.h | 36 +++ shared/nm-utils/nm-dedup-multi.c | 55 +--- shared/nm-utils/nm-errno.c | 177 +++++++++++-- shared/nm-utils/nm-errno.h | 138 ++++++---- shared/nm-utils/nm-glib.h | 8 +- shared/nm-utils/nm-hash-utils.c | 2 +- shared/nm-utils/nm-hash-utils.h | 3 + shared/nm-utils/nm-io-utils.c | 75 +++--- shared/nm-utils/nm-jansson.h | 13 +- shared/nm-utils/nm-logging-fwd.h | 8 +- shared/nm-utils/nm-macros-internal.h | 107 +++++++- shared/nm-utils/nm-secret-utils.c | 27 ++ shared/nm-utils/nm-secret-utils.h | 27 ++ shared/nm-utils/nm-shared-utils.c | 376 +++++++++++++++++++++++++++- shared/nm-utils/nm-shared-utils.h | 92 +++++-- shared/nm-utils/nm-test-utils.h | 8 +- shared/nm-utils/tests/test-shared-general.c | 46 ++++ 17 files changed, 1009 insertions(+), 189 deletions(-) (limited to 'shared/nm-utils') diff --git a/shared/nm-utils/nm-c-list.h b/shared/nm-utils/nm-c-list.h index b43d1441..5c73f574 100644 --- a/shared/nm-utils/nm-c-list.h +++ b/shared/nm-utils/nm-c-list.h @@ -78,4 +78,40 @@ nm_c_list_elem_free_all (CList *head, GDestroyNotify free_fcn) } } +/*****************************************************************************/ + +static inline gboolean +nm_c_list_move_before (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->prev != elem) { + c_list_unlink_stale (elem); + c_list_link_before (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_tail(lst, elem) nm_c_list_move_before (lst, elem) + +static inline gboolean +nm_c_list_move_after (CList *lst, CList *elem) +{ + nm_assert (lst); + nm_assert (elem); + nm_assert (c_list_contains (lst, elem)); + + if ( lst != elem + && lst->next != elem) { + c_list_unlink_stale (elem); + c_list_link_after (lst, elem); + return TRUE; + } + return FALSE; +} +#define nm_c_list_move_front(lst, elem) nm_c_list_move_after (lst, elem) + #endif /* __NM_C_LIST_H__ */ diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index 852c207c..5bdc3e3c 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -24,6 +24,7 @@ #include "nm-dedup-multi.h" #include "nm-hash-utils.h" +#include "nm-c-list.h" /*****************************************************************************/ @@ -260,44 +261,27 @@ _add (NMDedupMultiIndex *self, nm_dedup_multi_entry_set_dirty (entry, FALSE); nm_assert (!head_existing || entry->head == head_existing); - - if (entry_order) { - nm_assert (entry_order->head == entry->head); - nm_assert (c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); - nm_assert (c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); - } + nm_assert (!entry_order || entry_order->head == entry->head); + nm_assert (!entry_order || c_list_contains (&entry->lst_entries, &entry_order->lst_entries)); + nm_assert (!entry_order || c_list_contains (&entry_order->lst_entries, &entry->lst_entries)); switch (mode) { case NM_DEDUP_MULTI_IDX_MODE_PREPEND_FORCE: if (entry_order) { - if ( entry_order != entry - && entry->lst_entries.next != &entry_order->lst_entries) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, &entry->lst_entries)) changed = TRUE; - } } else { - if (entry->lst_entries.prev != &entry->head->lst_entries_head) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + if (nm_c_list_move_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) changed = TRUE; - } } break; case NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE: if (entry_order) { - if ( entry_order != entry - && entry->lst_entries.prev != &entry_order->lst_entries) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, &entry->lst_entries)) changed = TRUE; - } } else { - if (entry->lst_entries.next != &entry->head->lst_entries_head) { - c_list_unlink_stale (&entry->lst_entries); - c_list_link_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + if (nm_c_list_move_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries)) changed = TRUE; - } } break; case NM_DEDUP_MULTI_IDX_MODE_PREPEND: @@ -1022,33 +1006,20 @@ nm_dedup_multi_entry_reorder (const NMDedupMultiEntry *entry, if (!entry_order) { const NMDedupMultiHeadEntry *head_entry = entry->head; - nm_assert (c_list_contains (&head_entry->lst_entries_head, &entry->lst_entries)); if (order_after) { - if (head_entry->lst_entries_head.prev != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries); + if (nm_c_list_move_tail ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) return TRUE; - } } else { - if (head_entry->lst_entries_head.next != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries); + if (nm_c_list_move_front ((CList *) &head_entry->lst_entries_head, (CList *) &entry->lst_entries)) return TRUE; - } } - } else if (entry != entry_order) { + } else { if (order_after) { - if (entry_order->lst_entries.next != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries); + if (nm_c_list_move_after ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) return TRUE; - } } else { - if (entry_order->lst_entries.prev != &entry->lst_entries) { - c_list_unlink_stale ((CList *) &entry->lst_entries); - c_list_link_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries); + if (nm_c_list_move_before ((CList *) &entry_order->lst_entries, (CList *) &entry->lst_entries)) return TRUE; - } } } diff --git a/shared/nm-utils/nm-errno.c b/shared/nm-utils/nm-errno.c index c87f0b48..30eb9a8e 100644 --- a/shared/nm-utils/nm-errno.c +++ b/shared/nm-utils/nm-errno.c @@ -22,33 +22,61 @@ #include "nm-errno.h" +#include + /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, int, +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_geterror, +#if 0 + enum _NMErrno, +#else + int, +#endif NM_UTILS_LOOKUP_DEFAULT (NULL), - NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), - NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), - - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), - NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_SUCCESS, "NME_ERRNO_SUCCESS"), + NM_UTILS_LOOKUP_STR_ITEM (NME_ERRNO_OUT_OF_RANGE, "NME_ERRNO_OUT_OF_RANGE"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_UNSPEC, "NME_UNSPEC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_BUG, "NME_BUG"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NATIVE_ERRNO, "NME_NATIVE_ERRNO"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_SEQ_MISMATCH, "NME_NL_SEQ_MISMATCH"), + NM_UTILS_LOOKUP_STR_ITEM (NME_NL_NOADDR, "NME_NL_NOADDR"), + + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_FOUND, "not-found"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_EXISTS, "exists"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_WRONG_TYPE, "wrong-type"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NOT_SLAVE, "not-slave"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NO_FIRMWARE, "no-firmware"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_NETLINK, "netlink"), + NM_UTILS_LOOKUP_STR_ITEM (NME_PL_CANT_SET_MTU, "cant-set-mtu"), + + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_MININT), + NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_ERRNO_RESERVED_LAST_PLUS_1), ); +/** + * nm_strerror(): + * @nmerr: the NetworkManager specific errno to be converted + * to string. + * + * NetworkManager specific error numbers reserve a range in "errno.h" with + * our own defines. For numbers that don't fall into this range, the numbers + * are identical to the common error numbers. + * + * Idential to strerror(), g_strerror(), nm_strerror_native() for error numbers + * that are not in the reserved range of NetworkManager specific errors. + * + * Returns: (transfer none): the string representation of the error number. + */ const char * nm_strerror (int nmerr) { @@ -61,5 +89,110 @@ nm_strerror (int nmerr) if (s) return s; } - return g_strerror (nmerr); + return nm_strerror_native (nmerr); +} + +/*****************************************************************************/ + +/** + * nm_strerror_native_r: + * @errsv: the errno to convert to string. + * @buf: the output buffer where to write the string to. + * @buf_size: the length of buffer. + * + * This is like strerror_r(), with one difference: depending on the + * locale, the returned string is guaranteed to be valid UTF-8. + * Also, there is some confusion as to whether to use glibc's + * strerror_r() or the POXIX/XSI variant. This is abstracted + * by the function. + * + * Note that the returned buffer may also be a statically allocated + * buffer, and not the input buffer @buf. Consequently, the returned + * string may be longer than @buf_size. + * + * Returns: (transfer none): a NUL terminated error message. This is either a static + * string (that is never freed), or the provided @buf argumnt. + */ +const char * +nm_strerror_native_r (int errsv, char *buf, gsize buf_size) +{ + char *buf2; + + nm_assert (buf); + nm_assert (buf_size > 0); + +#if (_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE + /* XSI-compliant */ + { + int errno_saved = errno; + + if (strerror_r (errsv, buf, buf_size) != 0) { + g_snprintf (buf, buf_size, "Unspecified errno %d", errsv); + errno = errno_saved; + } + buf2 = buf; + } +#else + /* GNU-specific */ + buf2 = strerror_r (errsv, buf, buf_size); +#endif + + /* like g_strerror(), ensure that the error message is UTF-8. */ + if ( !g_get_charset (NULL) + && !g_utf8_validate (buf2, -1, NULL)) { + gs_free char *msg = NULL; + + msg = g_locale_to_utf8 (buf2, -1, NULL, NULL, NULL); + if (msg) { + g_strlcpy (buf, msg, buf_size); + buf2 = buf; + } + } + + return buf2; +} + +/** + * nm_strerror_native: + * @errsv: the errno integer from + * + * Like strerror(), but strerror() is not thread-safe and not guaranteed + * to be UTF-8. + * + * g_strerror() is a thread-safe variant of strerror(), however it caches + * all returned strings in a dictionary. That means, using this on untrusted + * error numbers can result in this cache to grow without limits. + * + * Instead, return a tread-local buffer. This way, it's thread-safe. + * + * There is a downside to this: subsequent calls of nm_strerror_native() + * overwrite the error message. + * + * Returns: (transfer none): the text representation of the error number. + */ +const char * +nm_strerror_native (int errsv) +{ + static _nm_thread_local char *buf_static = NULL; + char *buf; + + buf = buf_static; + if (G_UNLIKELY (!buf)) { + int errno_saved = errno; + pthread_key_t key; + + buf = g_malloc (NM_STRERROR_BUFSIZE); + buf_static = buf; + + if ( pthread_key_create (&key, g_free) != 0 + || pthread_setspecific (key, buf) != 0) { + /* Failure. We will leak the buffer when the thread exits. + * + * Nothing we can do about it really. For Debug builds we fail with an assertion. */ + nm_assert_not_reached (); + } + errno = errno_saved; + } + + return nm_strerror_native_r (errsv, buf, NM_STRERROR_BUFSIZE); } diff --git a/shared/nm-utils/nm-errno.h b/shared/nm-utils/nm-errno.h index c3008f1f..d77735a7 100644 --- a/shared/nm-utils/nm-errno.h +++ b/shared/nm-utils/nm-errno.h @@ -25,11 +25,23 @@ /*****************************************************************************/ -enum { +enum _NMErrno { _NM_ERRNO_MININT = G_MININT, _NM_ERRNO_MAXINT = G_MAXINT, _NM_ERRNO_RESERVED_FIRST = 100000, + + /* when we cannot represent a number as positive number, we resort to this + * number. Basically, the values G_MININT, -NME_ERRNO_SUCCESS, NME_ERRNO_SUCCESS + * and G_MAXINT all map to the same value. */ + NME_ERRNO_OUT_OF_RANGE = G_MAXINT, + + /* Indicate that the original errno was zero. Zero denotes *no error*, but we know something + * went wrong and we want to report some error. This is a placeholder to mean, something + * was wrong, but errno was zero. */ + NME_ERRNO_SUCCESS = G_MAXINT - 1, + + /* an unspecified error. */ NME_UNSPEC = _NM_ERRNO_RESERVED_FIRST, @@ -68,68 +80,106 @@ enum { /*****************************************************************************/ +/* When we receive an errno from a system function, we can safely assume + * that the error number is not negative. We rely on that, and possibly just + * "return -errsv;" to signal an error. We also rely on that, because libc + * is our trusted base: meaning, if it cannot even succeed at setting errno + * according to specification, all bets are off. + * + * This macro returns the input argument, and asserts that the error variable + * is positive. + * + * In a sense, the macro is related to nm_errno_native() function, but the difference + * is that this macro asserts that @errsv is positive, while nm_errno_native() coerces + * negative values to be non-negative. */ +#define NM_ERRNO_NATIVE(errsv) \ + ({ \ + const int _errsv_x = (errsv); \ + \ + nm_assert (_errsv_x > 0); \ + _errsv_x; \ + }) + +/* Normalize native errno. + * + * Our API may return native error codes () as negative values. This function + * takes such an errno, and normalizes it to their positive value. + * + * The special values G_MININT and zero are coerced to NME_ERRNO_OUT_OF_RANGE and NME_ERRNO_SUCCESS + * respectively. + * Other values are coerced to their inverse. + * Other positive values are returned unchanged. + * + * Basically, this normalizes errsv to be positive (taking care of two pathological cases). + */ static inline int nm_errno_native (int errsv) { - /* several API returns negative errno values as errors. Normalize - * negative values to positive values. - * - * As a special case, map G_MININT to G_MAXINT. If you care about the - * distinction, then check for G_MININT before. - * - * Basically, this normalizes a plain errno to be non-negative. */ - return errsv >= 0 - ? errsv - : ((errsv == G_MININT) ? G_MAXINT : -errsv); + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + return errsv >= 0 ? errsv : -errsv; + } } +/* Normalizes an nm-error to be positive. + * + * Various API returns negative error codes, and this function converts the negative + * value to its positive. + * + * Note that @nmerr is on the domain of NetworkManager specific error numbers, + * which is not the same as the native error numbers (errsv from ). But + * as far as normalizing goes, nm_errno() does exactly the same remapping as + * nm_errno_native(). */ static inline int nm_errno (int nmerr) { - /* Normalizes an nm-error to be positive. Various API returns negative - * error codes, and this function converts the negative value to its - * positive. - * - * It's very similar to nm_errno_native(), but not exactly. The difference is that - * nm_errno_native() is for plain errno, while nm_errno() is for nm-error numbers. - * Yes, nm-error number are ~almost~ the same as errno, except that a particular - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) is reserved. The difference - * between the two functions is only how G_MININT is mapped. - * - * See also nm_errno_from_native() below. */ - return nmerr >= 0 - ? nmerr - : ((nmerr == G_MININT) ? NME_BUG : -nmerr); + return nm_errno_native (nmerr); } +/* this maps a native errno to a (always non-negative) nm-error number. + * + * Note that nm-error numbers are embedded into the range of regular + * errno. The only difference is, that nm-error numbers reserve a + * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their + * own purpose. + * + * That means, converting an errno to nm-error number means in + * most cases just returning itself. + * Only pathological cases need special handling: + * + * - 0 is mapped to NME_ERRNO_SUCCESS; + * - G_MININT is mapped to NME_ERRNO_OUT_OF_RANGE; + * - values in the range of (+/-) [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] + * are mapped to NME_NATIVE_ERRNO + * - all other values are their (positive) absolute value. + */ static inline int nm_errno_from_native (int errsv) { - /* this maps a native errno to a (always non-negative) nm-error number. - * - * Note that nm-error numbers are embedded into the range of regular - * errno. The only difference is, that nm-error numbers reserve a - * range (_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST) for their - * own purpose. - * - * That means, converting an errno to nm-error number means in - * most cases just returning itself (negative values are normalized - * to be positive). Only values G_MININT and [_NM_ERRNO_RESERVED_FIRST, _NM_ERRNO_RESERVED_LAST] - * are coerced to the special value NME_NATIVE_ERRNO, as they cannot - * otherwise be represented in nm-error number domain. */ - if (errsv < 0) { - return G_UNLIKELY (errsv == G_MININT) + switch (errsv) { + case 0: return NME_ERRNO_SUCCESS; + case G_MININT: return NME_ERRNO_OUT_OF_RANGE; + default: + if (errsv < 0) + errsv = -errsv; + return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST + && errsv <= _NM_ERRNO_RESERVED_LAST) ? NME_NATIVE_ERRNO - : -errsv; + : errsv; } - return G_UNLIKELY ( errsv >= _NM_ERRNO_RESERVED_FIRST - && errsv <= _NM_ERRNO_RESERVED_LAST) - ? NME_NATIVE_ERRNO - : errsv; } const char *nm_strerror (int nmerr); /*****************************************************************************/ +#define NM_STRERROR_BUFSIZE 1024 + +const char *nm_strerror_native_r (int errsv, char *buf, gsize buf_size); +const char *nm_strerror_native (int errsv); + +/*****************************************************************************/ + #endif /* __NM_ERRNO_H__ */ diff --git a/shared/nm-utils/nm-glib.h b/shared/nm-utils/nm-glib.h index b7534edd..e941e067 100644 --- a/shared/nm-utils/nm-glib.h +++ b/shared/nm-utils/nm-glib.h @@ -424,11 +424,13 @@ g_steal_pointer (gpointer pp) return ref; } +#endif -/* type safety */ -#define g_steal_pointer(pp) \ - (0 ? (*(pp)) : (g_steal_pointer) (pp)) +#ifdef g_steal_pointer +#undef g_steal_pointer #endif +#define g_steal_pointer(pp) \ + ((typeof (*(pp))) g_steal_pointer (pp)) /*****************************************************************************/ diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c index 80387c71..6e728e6b 100644 --- a/shared/nm-utils/nm-hash-utils.c +++ b/shared/nm-utils/nm-hash-utils.c @@ -71,7 +71,7 @@ again: * the first guint has only the entropy that nm_utils_random_bytes() * generated for the first 4 bytes and relies on a good random generator. * - * The first int is especially intersting for nm_hash_static() below, and we + * The first int is especially interesting for nm_hash_static() below, and we * want to have it all the entropy of t_arr. */ c_siphash_init (&siph_state, t_arr.v8); c_siphash_append (&siph_state, (const guint8 *) &t_arr, sizeof (t_arr)); diff --git a/shared/nm-utils/nm-hash-utils.h b/shared/nm-utils/nm-hash-utils.h index cf71a7e9..1a1e44f5 100644 --- a/shared/nm-utils/nm-hash-utils.h +++ b/shared/nm-utils/nm-hash-utils.h @@ -122,6 +122,9 @@ nm_hash_update (NMHashState *state, const void *ptr, gsize n) nm_hash_update ((state), &_val, sizeof (_val)); \ } G_STMT_END +#define nm_hash_update_valp(state, val) \ + nm_hash_update ((state), (val), sizeof (*(val))) \ + static inline void nm_hash_update_bool (NMHashState *state, bool val) { diff --git a/shared/nm-utils/nm-io-utils.c b/shared/nm-utils/nm-io-utils.c index 06f756c4..51312748 100644 --- a/shared/nm-utils/nm-io-utils.c +++ b/shared/nm-utils/nm-io-utils.c @@ -29,6 +29,7 @@ #include "nm-shared-utils.h" #include "nm-secret-utils.h" +#include "nm-errno.h" /*****************************************************************************/ @@ -36,14 +37,12 @@ _nm_printf (3, 4) static int _get_contents_error (GError **error, int errsv, const char *format, ...) { - if (errsv < 0) - errsv = -errsv; - else if (!errsv) - errsv = errno; + nm_assert (NM_ERRNO_NATIVE (errsv)); if (error) { - char *msg; + gs_free char *msg = NULL; va_list args; + char bstrerr[NM_STRERROR_BUFSIZE]; va_start (args, format); msg = g_strdup_vprintf (format, args); @@ -52,11 +51,17 @@ _get_contents_error (GError **error, int errsv, const char *format, ...) G_FILE_ERROR, g_file_error_from_errno (errsv), "%s: %s", - msg, g_strerror (errsv)); - g_free (msg); + msg, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); } return -errsv; } +#define _get_contents_error_errno(error, ...) \ + ({ \ + int _errsv = (errno); \ + \ + _get_contents_error (error, _errsv, __VA_ARGS__); \ + }) static char * _mem_realloc (char *old, gboolean do_bzero_mem, gsize cur_len, gsize new_len) @@ -127,13 +132,14 @@ nm_utils_fd_get_contents (int fd, struct stat stat_buf; gs_free char *str = NULL; const bool do_bzero_mem = NM_FLAGS_HAS (flags, NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET); + int errsv; g_return_val_if_fail (fd >= 0, -EINVAL); g_return_val_if_fail (contents, -EINVAL); g_return_val_if_fail (!error || !*error, -EINVAL); if (fstat (fd, &stat_buf) < 0) - return _get_contents_error (error, 0, "failure during fstat"); + return _get_contents_error_errno (error, "failure during fstat"); if (!max_length) { /* default to a very large size, but not extreme */ @@ -156,7 +162,7 @@ nm_utils_fd_get_contents (int fd, if (n_read < 0) { if (do_bzero_mem) nm_explicit_bzero (str, n_stat); - return _get_contents_error (error, n_read, "error reading %zu bytes from file descriptor", n_stat); + return _get_contents_error (error, -n_read, "error reading %zu bytes from file descriptor", n_stat); } str[n_read] = '\0'; @@ -176,19 +182,19 @@ nm_utils_fd_get_contents (int fd, else { fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0); if (fd2 < 0) - return _get_contents_error (error, 0, "error during dup"); + return _get_contents_error_errno (error, "error during dup"); } if (!(f = fdopen (fd2, "r"))) { + errsv = errno; nm_close (fd2); - return _get_contents_error (error, 0, "failure during fdopen"); + return _get_contents_error (error, errsv, "failure during fdopen"); } n_have = 0; n_alloc = 0; while (!feof (f)) { - int errsv; gsize n_read; n_read = fread (buf, 1, sizeof (buf), f); @@ -262,8 +268,8 @@ nm_utils_fd_get_contents (int fd, * @flags: %NMUtilsFileGetContentsFlags for reading the file. * @contents: the output buffer with the file read. It is always * NUL terminated. The buffer is at most @max_length long, including - * the NUL byte. That is, it reads only files up to a length of - * @max_length - 1 bytes. + * the NUL byte. That is, it reads only files up to a length of + * @max_length - 1 bytes. * @length: optional output argument of the read file size. * * A reimplementation of g_file_get_contents() with a few differences: @@ -284,6 +290,7 @@ nm_utils_file_get_contents (int dirfd, { int fd; int errsv; + char bstrerr[NM_STRERROR_BUFSIZE]; g_return_val_if_fail (filename && filename[0], -EINVAL); @@ -297,8 +304,8 @@ nm_utils_file_get_contents (int dirfd, g_file_error_from_errno (errsv), "Failed to open file \"%s\" with openat: %s", filename, - g_strerror (errsv)); - return -errsv; + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); } } else { fd = open (filename, O_RDONLY | O_CLOEXEC); @@ -310,8 +317,8 @@ nm_utils_file_get_contents (int dirfd, g_file_error_from_errno (errsv), "Failed to open file \"%s\": %s", filename, - g_strerror (errsv)); - return -errsv; + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return -NM_ERRNO_NATIVE (errsv); } } return nm_utils_fd_get_contents (fd, @@ -341,6 +348,7 @@ nm_utils_file_set_contents (const char *filename, int errsv; gssize s; int fd; + char bstrerr[NM_STRERROR_BUFSIZE]; g_return_val_if_fail (filename, FALSE); g_return_val_if_fail (contents || !length, FALSE); @@ -359,7 +367,7 @@ nm_utils_file_set_contents (const char *filename, g_file_error_from_errno (errsv), "failed to create file %s: %s", tmp_name, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } @@ -378,7 +386,7 @@ nm_utils_file_set_contents (const char *filename, g_file_error_from_errno (errsv), "failed to write to file %s: %s", tmp_name, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } @@ -395,20 +403,21 @@ nm_utils_file_set_contents (const char *filename, * guarantee the data is written to the disk before the metadata.) */ if ( lstat (filename, &statbuf) == 0 - && statbuf.st_size > 0 - && fsync (fd) != 0) { - errsv = errno; + && statbuf.st_size > 0) { + if (fsync (fd) != 0) { + errsv = errno; - nm_close (fd); - unlink (tmp_name); + nm_close (fd); + unlink (tmp_name); - g_set_error (error, - G_FILE_ERROR, - g_file_error_from_errno (errsv), - "failed to fsync %s: %s", - tmp_name, - g_strerror (errsv)); - return FALSE; + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errsv), + "failed to fsync %s: %s", + tmp_name, + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); + return FALSE; + } } nm_close (fd); @@ -422,7 +431,7 @@ nm_utils_file_set_contents (const char *filename, "failed to rename %s to %s: %s", tmp_name, filename, - g_strerror (errsv)); + nm_strerror_native_r (errsv, bstrerr, sizeof (bstrerr))); return FALSE; } diff --git a/shared/nm-utils/nm-jansson.h b/shared/nm-utils/nm-jansson.h index b00c75c6..5a73231f 100644 --- a/shared/nm-utils/nm-jansson.h +++ b/shared/nm-utils/nm-jansson.h @@ -34,13 +34,16 @@ /* Added in Jansson v2.8 */ #ifndef json_object_foreach_safe #define json_object_foreach_safe(object, n, key, value) \ - for(key = json_object_iter_key(json_object_iter(object)), \ - n = json_object_iter_next(object, json_object_key_to_iter(key)); \ - key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ - key = json_object_iter_key(n), \ - n = json_object_iter_next(object, json_object_key_to_iter(key))) + for (key = json_object_iter_key(json_object_iter(object)), \ + n = json_object_iter_next(object, json_object_key_to_iter(key)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(n), \ + n = json_object_iter_next(object, json_object_key_to_iter(key))) #endif +NM_AUTO_DEFINE_FCN0 (json_t *, _nm_auto_decref_json, json_decref) +#define nm_auto_decref_json nm_auto(_nm_auto_decref_json) + #endif /* WITH_JANSON */ #endif /* __NM_JANSSON_H__ */ diff --git a/shared/nm-utils/nm-logging-fwd.h b/shared/nm-utils/nm-logging-fwd.h index 303d5951..900dfff8 100644 --- a/shared/nm-utils/nm-logging-fwd.h +++ b/shared/nm-utils/nm-logging-fwd.h @@ -94,18 +94,20 @@ typedef enum { /*< skip >*/ _LOGL_N, /* the number of logging levels including "OFF" */ } NMLogLevel; -gboolean _nm_log_enabled (NMLogLevel level, - NMLogDomain domain); +gboolean _nm_log_enabled_impl (gboolean mt_require_locking, + NMLogLevel level, + NMLogDomain domain); void _nm_log_impl (const char *file, guint line, const char *func, + gboolean mt_require_locking, NMLogLevel level, NMLogDomain domain, int error, const char *ifname, const char *con_uuid, const char *fmt, - ...) _nm_printf (9, 10); + ...) _nm_printf (10, 11); #endif /* __NM_LOGGING_DEFINES_H__ */ diff --git a/shared/nm-utils/nm-macros-internal.h b/shared/nm-utils/nm-macros-internal.h index 1f3970fe..42299c96 100644 --- a/shared/nm-utils/nm-macros-internal.h +++ b/shared/nm-utils/nm-macros-internal.h @@ -45,6 +45,18 @@ #define nm_auto(fcn) __attribute__ ((__cleanup__(fcn))) +/* This is required to make LTO working. + * + * See https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/76#note_112694 + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200#c28 + */ +#ifndef __clang__ +#define _nm_externally_visible __attribute__ ((__externally_visible__)) +#else +#define _nm_externally_visible +#endif + + #if __GNUC__ >= 7 #define _nm_fallthrough __attribute__ ((__fallthrough__)) #else @@ -67,6 +79,28 @@ /*****************************************************************************/ +/* most of our code is single-threaded with a mainloop. Hence, we usually don't need + * any thread-safety. Sometimes, we do need thread-safety (nm-logging), but we can + * avoid locking if we are on the main-thread by: + * + * - modifications of shared data is done infrequently and only from the + * main-thread (nm_logging_setup()) + * - read-only access is done frequently (nm_logging_enabled()) + * - from the main-thread, we can do that without locking (because + * all modifications are also done on the main thread. + * - from other threads, we need locking. But this is expected to be + * done infrequently too. Important is the lock-free fast-path on the + * main-thread. + * + * By defining NM_THREAD_SAFE_ON_MAIN_THREAD you indicate that this code runs + * on the main-thread. It is by default defined to "1". If you have code that + * is also used on another thread, redefine the define to 0 (to opt in into + * the slow-path). + */ +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + +/*****************************************************************************/ + #define NM_AUTO_DEFINE_FCN_VOID(CastType, name, func) \ static inline void name (void *v) \ { \ @@ -415,7 +449,7 @@ NM_G_ERROR_MSG (GError *error) /*****************************************************************************/ /* macro to return strlen() of a compile time string. */ -#define NM_STRLEN(str) ( sizeof ("" str) - 1 ) +#define NM_STRLEN(str) ( sizeof (""str"") - 1 ) /* returns the length of a NULL terminated array of pointers, * like g_strv_length() does. The difference is: @@ -826,12 +860,33 @@ fcn (void) \ /*****************************************************************************/ -#define nm_streq(s1, s2) (strcmp (s1, s2) == 0) -#define nm_streq0(s1, s2) (g_strcmp0 (s1, s2) == 0) +static inline gboolean +nm_streq (const char *s1, const char *s2) +{ + return strcmp (s1, s2) == 0; +} + +static inline gboolean +nm_streq0 (const char *s1, const char *s2) +{ + return (s1 == s2) + || (s1 && s2 && strcmp (s1, s2) == 0); +} #define NM_STR_HAS_PREFIX(str, prefix) \ (strncmp ((str), ""prefix"", NM_STRLEN (prefix)) == 0) +#define NM_STR_HAS_SUFFIX(str, suffix) \ + ({ \ + const char *_str = (str); \ + gsize _l = strlen (_str); \ + \ + ( (_l >= NM_STRLEN (suffix)) \ + && (memcmp (&_str[_l - NM_STRLEN (suffix)], \ + ""suffix"", \ + NM_STRLEN (suffix)) == 0)); \ + }) + /*****************************************************************************/ static inline GString * @@ -1127,6 +1182,28 @@ nm_clear_g_cancellable (GCancellable **cancellable) return FALSE; } +/* If @cancellable_id is not 0, clear it and call g_cancellable_disconnect(). + * @cancellable may be %NULL, if there is nothing to disconnect. + * + * It's like nm_clear_g_signal_handler(), except that it uses g_cancellable_disconnect() + * instead of g_signal_handler_disconnect(). + * + * Note the warning in glib documentation about dead-lock and what g_cancellable_disconnect() + * actually does. */ +static inline gboolean +nm_clear_g_cancellable_disconnect (GCancellable *cancellable, gulong *cancellable_id) +{ + gulong id; + + if ( cancellable_id + && (id = *cancellable_id) != 0) { + *cancellable_id = 0; + g_cancellable_disconnect (cancellable, id); + return TRUE; + } + return FALSE; +} + /*****************************************************************************/ static inline GVariant * @@ -1243,17 +1320,17 @@ fcn_name (lookup_type val) \ /*****************************************************************************/ -#define _NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, ORIG_FUNC, VERSIONED_FUNC, ARGS_TYPED, ARGS) \ -RETURN_TYPE VERSIONED_FUNC ARGS_TYPED; \ -RETURN_TYPE VERSIONED_FUNC ARGS_TYPED \ +#define _NM_BACKPORT_SYMBOL_IMPL(version, return_type, orig_func, versioned_func, args_typed, args) \ +return_type versioned_func args_typed; \ +_nm_externally_visible return_type versioned_func args_typed \ { \ - return ORIG_FUNC ARGS; \ + return orig_func args; \ } \ -RETURN_TYPE ORIG_FUNC ARGS_TYPED; \ -__asm__(".symver "G_STRINGIFY(VERSIONED_FUNC)", "G_STRINGIFY(ORIG_FUNC)"@"G_STRINGIFY(VERSION)) +return_type orig_func args_typed; \ +__asm__(".symver "G_STRINGIFY(versioned_func)", "G_STRINGIFY(orig_func)"@"G_STRINGIFY(version)) -#define NM_BACKPORT_SYMBOL(VERSION, RETURN_TYPE, FUNC, ARGS_TYPED, ARGS) \ -_NM_BACKPORT_SYMBOL_IMPL(VERSION, RETURN_TYPE, FUNC, _##FUNC##_##VERSION, ARGS_TYPED, ARGS) +#define NM_BACKPORT_SYMBOL(version, return_type, func, args_typed, args) \ +_NM_BACKPORT_SYMBOL_IMPL(version, return_type, func, _##func##_##version, args_typed, args) /*****************************************************************************/ @@ -1371,6 +1448,14 @@ nm_strcmp_p (gconstpointer a, gconstpointer b) : _b); \ }) +/* evaluates to (void) if _A or _B are not constant or of different types */ +#define NM_CONST_MAX(_A, _B) \ + (__builtin_choose_expr (( __builtin_constant_p (_A) \ + && __builtin_constant_p (_B) \ + && __builtin_types_compatible_p (typeof (_A), typeof (_B))), \ + ((_A) > (_B)) ? (_A) : (_B), \ + ((void) 0))) + /*****************************************************************************/ static inline guint diff --git a/shared/nm-utils/nm-secret-utils.c b/shared/nm-utils/nm-secret-utils.c index 65f99c65..ec5cc6b1 100644 --- a/shared/nm-utils/nm-secret-utils.c +++ b/shared/nm-utils/nm-secret-utils.c @@ -17,6 +17,7 @@ * Boston, MA 02110-1301 USA. * * (C) Copyright 2018 Red Hat, Inc. + * (C) Copyright 2015 - 2019 Jason A. Donenfeld . All Rights Reserved. */ #include "nm-default.h" @@ -132,3 +133,29 @@ nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len) _secret_buf_free, secret); } + +/*****************************************************************************/ + +/** + * nm_utils_memeqzero_secret: + * @data: the data pointer to check (may be %NULL if @length is zero). + * @length: the number of bytes to check. + * + * Checks that all bytes are zero. This always takes the same amount + * of time to prevent timing attacks. + * + * Returns: whether all bytes are zero. + */ +gboolean +nm_utils_memeqzero_secret (gconstpointer data, gsize length) +{ + const guint8 *const key = data; + volatile guint8 acc = 0; + gsize i; + + for (i = 0; i < length; i++) { + acc |= key[i]; + asm volatile("" : "=r"(acc) : "0"(acc)); + } + return 1 & ((acc - 1) >> 8); +} diff --git a/shared/nm-utils/nm-secret-utils.h b/shared/nm-utils/nm-secret-utils.h index 9df31afe..034ef7bd 100644 --- a/shared/nm-utils/nm-secret-utils.h +++ b/shared/nm-utils/nm-secret-utils.h @@ -75,6 +75,19 @@ typedef struct { }; } NMSecretPtr; +static inline void +nm_secret_ptr_bzero (NMSecretPtr *secret) +{ + if (secret) { + if (secret->len > 0) { + if (secret->ptr) + nm_explicit_bzero (secret->ptr, secret->len); + } + } +} + +#define nm_auto_bzero_secret_ptr nm_auto(nm_secret_ptr_bzero) + static inline void nm_secret_ptr_clear (NMSecretPtr *secret) { @@ -90,12 +103,24 @@ nm_secret_ptr_clear (NMSecretPtr *secret) #define nm_auto_clear_secret_ptr nm_auto(nm_secret_ptr_clear) +#define NM_SECRET_PTR_INIT() \ + ((const NMSecretPtr) { \ + .len = 0, \ + .ptr = NULL, \ + }) + #define NM_SECRET_PTR_STATIC(_len) \ ((const NMSecretPtr) { \ .len = _len, \ .ptr = ((guint8 [_len]) { }), \ }) +#define NM_SECRET_PTR_ARRAY(_arr) \ + ((const NMSecretPtr) { \ + .len = G_N_ELEMENTS (_arr) * sizeof ((_arr)[0]), \ + .ptr = &((_arr)[0]), \ + }) + static inline void nm_secret_ptr_clear_static (const NMSecretPtr *secret) { @@ -148,4 +173,6 @@ GBytes *nm_secret_buf_to_gbytes_take (NMSecretBuf *secret, gssize actual_len); /*****************************************************************************/ +gboolean nm_utils_memeqzero_secret (gconstpointer data, gsize length); + #endif /* __NM_SECRET_UTILS_H__ */ diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index c9ef89e9..6a43c670 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -23,10 +23,12 @@ #include "nm-shared-utils.h" -#include #include #include #include +#include + +#include "nm-errno.h" /*****************************************************************************/ @@ -34,7 +36,116 @@ const void *const _NM_PTRARRAY_EMPTY[1] = { NULL }; /*****************************************************************************/ -const NMIPAddr nm_ip_addr_zero = { 0 }; +const NMIPAddr nm_ip_addr_zero = { }; + +/* this initializes a struct in_addr/in6_addr and allows for untrusted + * arguments (like unsuitable @addr_family or @src_len). It's almost safe + * in the sense that it verifies input arguments strictly. Also, it + * uses memcpy() to access @src, so alignment is not an issue. + * + * Only potential pitfalls: + * + * - it allows for @addr_family to be AF_UNSPEC. If that is the case (and the + * caller allows for that), the caller MUST provide @out_addr_family. + * - when setting @dst to an IPv4 address, the trailing bytes are not touched. + * Meaning, if @dst is an NMIPAddr union, only the first bytes will be set. + * If that matter to you, clear @dst before. */ +gboolean +nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family) +{ + nm_assert (dst); + + switch (addr_family) { + case AF_UNSPEC: + if (!out_addr_family) { + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert_not_reached (); + return FALSE; + } + switch (src_len) { + case sizeof (struct in_addr): addr_family = AF_INET; break; + case sizeof (struct in6_addr): addr_family = AF_INET6; break; + default: + return FALSE; + } + break; + case AF_INET: + if (src_len != sizeof (struct in_addr)) + return FALSE; + break; + case AF_INET6: + if (src_len != sizeof (struct in6_addr)) + return FALSE; + break; + default: + /* when the callers allow undefined @addr_family, they must provide + * an @out_addr_family argument. */ + nm_assert (out_addr_family); + return FALSE; + } + + nm_assert (src); + + memcpy (dst, src, src_len); + NM_SET_OUT (out_addr_family, addr_family); + return TRUE; +} + +/*****************************************************************************/ + +pid_t +nm_utils_gettid (void) +{ + return (pid_t) syscall (SYS_gettid); +} + +/* Used for asserting that this function is called on the main-thread. + * The main-thread is determined by remembering the thread-id + * of when the function was called the first time. + * + * When forking, the thread-id is again reset upon first call. */ +gboolean +_nm_assert_on_main_thread (void) +{ + G_LOCK_DEFINE_STATIC (lock); + static pid_t seen_tid; + static pid_t seen_pid; + pid_t tid; + pid_t pid; + gboolean success = FALSE; + + tid = nm_utils_gettid (); + nm_assert (tid != 0); + + G_LOCK (lock); + + if (G_LIKELY (tid == seen_tid)) { + /* we don't care about false positives (when the process forked, and the thread-id + * is accidentally re-used) . It's for assertions only. */ + success = TRUE; + } else { + pid = getpid (); + nm_assert (pid != 0); + + if ( seen_tid == 0 + || seen_pid != pid) { + /* either this is the first time we call the function, or the process + * forked. In both cases, remember the thread-id. */ + seen_tid = tid; + seen_pid = pid; + success = TRUE; + } + } + + G_UNLOCK (lock); + + return success; +} /*****************************************************************************/ @@ -561,6 +672,8 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family, return FALSE; if (slash) { + /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't + * do that. */ prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, 0, addr_family == AF_INET ? 32 : 128, @@ -1360,6 +1473,53 @@ nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +/** + * nm_g_type_find_implementing_class_for_property: + * @gtype: the GObject type which has a property @pname + * @pname: the name of the property to look up + * + * This is only a helper function for printf debugging. It's not + * used in actual code. Hence, the function just asserts that + * @pname and @gtype arguments are suitable. It cannot fail. + * + * Returns: the most ancestor type of @gtype, that + * implements the property @pname. It means, it + * searches the type hierarchy to find the type + * that added @pname. + */ +GType +nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname) +{ + nm_auto_unref_gtypeclass GObjectClass *klass = NULL; + GParamSpec *pspec; + + g_return_val_if_fail (pname, G_TYPE_INVALID); + + klass = g_type_class_ref (gtype); + g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), G_TYPE_INVALID); + + pspec = g_object_class_find_property (klass, pname); + g_return_val_if_fail (pspec, G_TYPE_INVALID); + + gtype = G_TYPE_FROM_CLASS (klass); + + while (TRUE) { + nm_auto_unref_gtypeclass GObjectClass *k = NULL; + + k = g_type_class_ref (g_type_parent (gtype)); + + g_return_val_if_fail (G_IS_OBJECT_CLASS (k), G_TYPE_INVALID); + + if (g_object_class_find_property (k, pname) != pspec) + return gtype; + + gtype = G_TYPE_FROM_CLASS (k); + } +} + +/*****************************************************************************/ + static void _str_append_escape (GString *s, char ch) { @@ -1691,7 +1851,7 @@ nm_utils_fd_wait_for_event (int fd, int event, gint64 timeout_ns) r = ppoll (&pollfd, 1, pts, NULL); if (r < 0) - return -errno; + return -NM_ERRNO_NATIVE (errno); if (r == 0) return 0; return pollfd.revents; @@ -1718,10 +1878,12 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) k = read (fd, p, nbytes); if (k < 0) { - if (errno == EINTR) + int errsv = errno; + + if (errsv == EINTR) continue; - if (errno == EAGAIN && do_poll) { + if (errsv == EAGAIN && do_poll) { /* We knowingly ignore any return value here, * and expect that any error/EOF is reported @@ -1731,7 +1893,7 @@ nm_utils_fd_read_loop (int fd, void *buf, size_t nbytes, bool do_poll) continue; } - return n > 0 ? n : -errno; + return n > 0 ? n : -NM_ERRNO_NATIVE (errsv); } if (k == 0) @@ -2375,3 +2537,205 @@ nm_utils_memeqzero (gconstpointer data, gsize length) /* Now we know that's zero, memcmp with self. */ return memcmp (data, p, length) == 0; } + +/** + * nm_utils_bin2hexstr_full: + * @addr: pointer of @length bytes. If @length is zero, this may + * also be %NULL. + * @length: number of bytes in @addr. May also be zero, in which + * case this will return an empty string. + * @delimiter: either '\0', otherwise the output string will have the + * given delimiter character between each two hex numbers. + * @upper_case: if TRUE, use upper case ASCII characters for hex. + * @out: if %NULL, the function will allocate a new buffer of + * either (@length*2+1) or (@length*3) bytes, depending on whether + * a @delimiter is specified. In that case, the allocated buffer will + * be returned and must be freed by the caller. + * If not %NULL, the buffer must already be preallocated and contain + * at least (@length*2+1) or (@length*3) bytes, depending on the delimiter. + * + * Returns: the binary value converted to a hex string. If @out is given, + * this always returns @out. If @out is %NULL, a newly allocated string + * is returned. + */ +char * +nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out) +{ + const guint8 *in = addr; + const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + char *out0; + + if (out) + out0 = out; + else { + out0 = out = g_new (char, delimiter == '\0' + ? length * 2 + 1 + : length * 3); + } + + /* @out must contain at least @length*3 bytes if @delimiter is set, + * otherwise, @length*2+1. */ + + if (length > 0) { + nm_assert (in); + for (;;) { + const guint8 v = *in++; + + *out++ = LOOKUP[v >> 4]; + *out++ = LOOKUP[v & 0x0F]; + length--; + if (!length) + break; + if (delimiter) + *out++ = delimiter; + } + } + + *out = '\0'; + return out0; +} + +guint8 * +nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len) +{ + const char *in = hexstr; + guint8 *out = buffer; + gboolean delimiter_has = TRUE; + guint8 delimiter = '\0'; + gsize len; + + nm_assert (hexstr); + nm_assert (buffer); + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && in[0] == '0' + && in[1] == 'x') + in += 2; + + while (TRUE) { + const guint8 d1 = in[0]; + guint8 d2; + int i1, i2; + + i1 = nm_utils_hexchar_to_int (d1); + if (i1 < 0) + goto fail; + + /* If there's no leading zero (ie "aa:b:cc") then fake it */ + d2 = in[1]; + if ( d2 + && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) { + *out++ = (i1 << 4) + i2; + d2 = in[2]; + if (!d2) + break; + in += 2; + } else { + /* Fake leading zero */ + *out++ = i1; + if (!d2) { + if (!delimiter_has) { + /* when using no delimiter, there must be pairs of hex chars */ + goto fail; + } + break; + } + in += 1; + } + + if (--buffer_len == 0) + goto fail; + + if (delimiter_has) { + if (d2 != delimiter) { + if (delimiter) + goto fail; + if (delimiter_candidates) { + while (delimiter_candidates[0]) { + if (delimiter_candidates++[0] == d2) + delimiter = d2; + } + } + if (!delimiter) { + if (delimiter_required) + goto fail; + delimiter_has = FALSE; + continue; + } + } + in++; + } + } + + len = out - buffer; + if ( required_len == 0 + || len == required_len) { + NM_SET_OUT (out_len, len); + return buffer; + } + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} + +guint8 * +nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len) +{ + guint8 *buffer; + gsize buffer_len, len; + + g_return_val_if_fail (hexstr, NULL); + + nm_assert (required_len > 0 || out_len); + + if ( allow_0x_prefix + && hexstr[0] == '0' + && hexstr[1] == 'x') + hexstr += 2; + + if (!hexstr[0]) + goto fail; + + if (required_len > 0) + buffer_len = required_len; + else + buffer_len = strlen (hexstr) / 2 + 3; + + buffer = g_malloc (buffer_len); + + if (nm_utils_hexstr2bin_full (hexstr, + FALSE, + delimiter_required, + delimiter_candidates, + required_len, + buffer, + buffer_len, + &len)) { + NM_SET_OUT (out_len, len); + return buffer; + } + + g_free (buffer); + +fail: + NM_SET_OUT (out_len, 0); + return NULL; +} diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index e28a5fb2..65e34959 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -26,6 +26,18 @@ /*****************************************************************************/ +pid_t nm_utils_gettid (void); + +gboolean _nm_assert_on_main_thread (void); + +#if NM_MORE_ASSERTS > 5 +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { nm_assert (_nm_assert_on_main_thread ()); } G_STMT_END +#else +#define NM_ASSERT_ON_MAIN_THREAD() G_STMT_START { ; } G_STMT_END +#endif + +/*****************************************************************************/ + static inline gboolean _NM_INT_NOT_NEGATIVE (gssize val) { @@ -128,6 +140,18 @@ nm_ip_addr_set (int addr_family, gpointer dst, gconstpointer src) : sizeof (struct in6_addr)); } +gboolean nm_ip_addr_set_from_untrusted (int addr_family, + gpointer dst, + gconstpointer src, + gsize src_len, + int *out_addr_family); + +static inline gboolean +nm_ip4_addr_is_localhost (in_addr_t addr4) +{ + return (addr4 & htonl (0xFF000000u)) == htonl (0x7F000000u); +} + /*****************************************************************************/ #define NM_CMP_RETURN(c) \ @@ -305,7 +329,7 @@ _nm_strndup_a_step (char *s, const char *str, gsize len) * * Usually, an inline function nm_strdup_int64() would be enough. However, * that cannot be used for guint64. So, we would also need nm_strdup_uint64(). - * This causes suble error potential, because the caller needs to ensure to + * This causes subtle error potential, because the caller needs to ensure to * use the right one (and compiler isn't going to help as it silently casts). * * Instead, this generic macro is supposed to handle all integers correctly. */ @@ -688,20 +712,26 @@ nm_utils_error_set_literal (GError **error, int error_code, const char *literal) g_set_error ((error), NM_UTILS_ERROR, error_code, __VA_ARGS__) #define nm_utils_error_set_errno(error, errsv, fmt, ...) \ - g_set_error ((error), \ - NM_UTILS_ERROR, \ - NM_UTILS_ERROR_UNKNOWN, \ - fmt, \ - ##__VA_ARGS__, \ - g_strerror (({ \ - const int _errsv = (errsv); \ - \ - ( _errsv >= 0 \ - ? _errsv \ - : ( (_errsv == G_MININT) \ - ? G_MAXINT \ - : -errsv)); \ - }))) + G_STMT_START { \ + char _bstrerr[NM_STRERROR_BUFSIZE]; \ + \ + g_set_error ((error), \ + NM_UTILS_ERROR, \ + NM_UTILS_ERROR_UNKNOWN, \ + fmt, \ + ##__VA_ARGS__, \ + nm_strerror_native_r (({ \ + const int _errsv = (errsv); \ + \ + ( _errsv >= 0 \ + ? _errsv \ + : ( G_UNLIKELY (_errsv == G_MININT) \ + ? G_MAXINT \ + : -errsv)); \ + }), \ + _bstrerr, \ + sizeof (_bstrerr))); \ + } G_STMT_END /*****************************************************************************/ @@ -777,6 +807,11 @@ GParamSpec *nm_g_object_class_find_property_from_gtype (GType gtype, /*****************************************************************************/ +GType nm_g_type_find_implementing_class_for_property (GType gtype, + const char *pname); + +/*****************************************************************************/ + typedef enum { NM_UTILS_STR_UTF8_SAFE_FLAG_NONE = 0, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL = 0x0001, @@ -1093,4 +1128,31 @@ nm_strv_ptrarray_take_gstring (GPtrArray *cmd, int nm_utils_getpagesize (void); +/*****************************************************************************/ + +char *nm_utils_bin2hexstr_full (gconstpointer addr, + gsize length, + char delimiter, + gboolean upper_case, + char *out); + +guint8 *nm_utils_hexstr2bin_full (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + guint8 *buffer, + gsize buffer_len, + gsize *out_len); + +#define nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \ + nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL) + +guint8 *nm_utils_hexstr2bin_alloc (const char *hexstr, + gboolean allow_0x_prefix, + gboolean delimiter_required, + const char *delimiter_candidates, + gsize required_len, + gsize *out_len); + #endif /* __NM_SHARED_UTILS_H__ */ diff --git a/shared/nm-utils/nm-test-utils.h b/shared/nm-utils/nm-test-utils.h index c235d93d..c5ea5e3f 100644 --- a/shared/nm-utils/nm-test-utils.h +++ b/shared/nm-utils/nm-test-utils.h @@ -1060,7 +1060,7 @@ nmtst_reexec_sudo (void) execvp (__nmtst_internal.sudo_cmd, argv); errsv = errno; - g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, strerror (errsv)); + g_error (">> exec %s failed: %d - %s", __nmtst_internal.sudo_cmd, errsv, nm_strerror_native (errsv)); } /*****************************************************************************/ @@ -1214,7 +1214,7 @@ nmtst_inet_from_string (int addr_family, const char *str) static inline const char * nmtst_inet_to_string (int addr_family, gconstpointer addr) { - static char buf[MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; + static char buf[NM_CONST_MAX (INET6_ADDRSTRLEN, INET_ADDRSTRLEN)]; g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); g_assert (addr); @@ -1370,7 +1370,7 @@ nmtst_file_unlink_if_exists (const char *name) if (unlink (name) != 0) { errsv = errno; if (errsv != ENOENT) - g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, strerror (errsv)); + g_error ("nmtst_file_unlink_if_exists(%s): failed with %s", name, nm_strerror_native (errsv)); } } @@ -1383,7 +1383,7 @@ nmtst_file_unlink (const char *name) if (unlink (name) != 0) { errsv = errno; - g_error ("nmtst_file_unlink(%s): failed with %s", name, strerror (errsv)); + g_error ("nmtst_file_unlink(%s): failed with %s", name, nm_strerror_native (errsv)); } } diff --git a/shared/nm-utils/tests/test-shared-general.c b/shared/nm-utils/tests/test-shared-general.c index 7d22e56d..d53b21d9 100644 --- a/shared/nm-utils/tests/test-shared-general.c +++ b/shared/nm-utils/tests/test-shared-general.c @@ -23,6 +23,7 @@ #include "nm-utils/nm-time-utils.h" #include "nm-utils/nm-random-utils.h" +#include "nm-utils/unaligned.h" #include "nm-utils/nm-test-utils.h" @@ -204,6 +205,49 @@ test_nm_strndup_a (void) /*****************************************************************************/ +static void +test_nm_ip4_addr_is_localhost (void) +{ + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.0"))); + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.0.0.1"))); + g_assert ( nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("127.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("126.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("128.5.0.1"))); + g_assert (!nm_ip4_addr_is_localhost (nmtst_inet4_from_string ("129.5.0.1"))); +} + +/*****************************************************************************/ + +static void +test_unaligned (void) +{ + int shift; + + for (shift = 0; shift <= 32; shift++) { + guint8 buf[100] = { }; + guint8 val = 0; + + while (val == 0) + val = nmtst_get_rand_int () % 256; + + buf[shift] = val; + + g_assert_cmpint (unaligned_read_le64 (&buf[shift]), ==, (guint64) val); + g_assert_cmpint (unaligned_read_be64 (&buf[shift]), ==, ((guint64) val) << 56); + g_assert_cmpint (unaligned_read_ne64 (&buf[shift]), !=, 0); + + g_assert_cmpint (unaligned_read_le32 (&buf[shift]), ==, (guint32) val); + g_assert_cmpint (unaligned_read_be32 (&buf[shift]), ==, ((guint32) val) << 24); + g_assert_cmpint (unaligned_read_ne32 (&buf[shift]), !=, 0); + + g_assert_cmpint (unaligned_read_le16 (&buf[shift]), ==, (guint16) val); + g_assert_cmpint (unaligned_read_be16 (&buf[shift]), ==, ((guint16) val) << 8); + g_assert_cmpint (unaligned_read_ne16 (&buf[shift]), !=, 0); + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int main (int argc, char **argv) @@ -215,6 +259,8 @@ int main (int argc, char **argv) g_test_add_func ("/general/test_nm_make_strv", test_make_strv); g_test_add_func ("/general/test_nm_strdup_int", test_nm_strdup_int); g_test_add_func ("/general/test_nm_strndup_a", test_nm_strndup_a); + g_test_add_func ("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost); + g_test_add_func ("/general/test_unaligned", test_unaligned); return g_test_run (); } -- cgit 1.3.0-6-gf8a5