From: Timo Sirainen Date: Mon, 9 Nov 2020 14:09:05 +0000 (+0200) Subject: lib: Add and use TYPE_CHECKS() macro to standardize type checking for functions X-Git-Tag: 2.3.14.rc1~370 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7068d53e8d59c38b57cc311105c65dbddc482512;p=thirdparty%2Fdovecot%2Fcore.git lib: Add and use TYPE_CHECKS() macro to standardize type checking for functions This helps especially to avoid adding COMPILE_ERROR_*() macros to NULL pointers, which results in ubsan errors: runtime error: applying zero offset to null pointer These changes also remove the use of gcc-specific ({...}) code in the macros, making them slightly more portable. --- diff --git a/src/lib/array.h b/src/lib/array.h index f22305b7e4..eef813f322 100644 --- a/src/lib/array.h +++ b/src/lib/array.h @@ -192,8 +192,8 @@ array_append_i(struct array *array, const void *data, unsigned int count) } #define array_append(array, data, count) \ - array_append_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ - data, count) + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_append_i(&(array)->arr, data, count)) static inline void array_append_array_i(struct array *dest_array, const struct array *src_array) @@ -202,8 +202,8 @@ array_append_array_i(struct array *dest_array, const struct array *src_array) buffer_append_buf(dest_array->buffer, src_array->buffer, 0, SIZE_MAX); } #define array_append_array(dest_array, src_array) \ - array_append_array_i(&(dest_array)->arr + ARRAY_TYPES_CHECK(dest_array, src_array), \ - &(src_array)->arr) + TYPE_CHECKS(void, ARRAY_TYPES_CHECK(dest_array, src_array), \ + array_append_array_i(&(dest_array)->arr, &(src_array)->arr)) static inline void array_insert_i(struct array *array, unsigned int idx, @@ -214,8 +214,8 @@ array_insert_i(struct array *array, unsigned int idx, } #define array_insert(array, idx, data, count) \ - array_insert_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ - idx, data, count) + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_insert_i(&(array)->arr, idx, data, count)) static inline void array_delete_i(struct array *array, unsigned int idx, unsigned int count) @@ -278,8 +278,8 @@ void *array_idx_get_space_i(struct array *array, unsigned int idx); void array_idx_set_i(struct array *array, unsigned int idx, const void *data); #define array_idx_set(array, idx, data) \ - array_idx_set_i(&(array)->arr + ARRAY_TYPE_CHECK(array, data), \ - idx, data) + TYPE_CHECKS(void, ARRAY_TYPE_CHECK(array, data), \ + array_idx_set_i(&(array)->arr, idx, data)) void array_idx_clear_i(struct array *array, unsigned int idx); #define array_idx_clear(array, idx) \ @@ -330,9 +330,9 @@ array_swap_i(struct array *array1, struct array *array2) array2->buffer = buffer; array2->element_size = elsize; } -#define array_swap(array1, array2) \ - array_swap_i(&(array1)->arr + ARRAY_TYPES_CHECK(array1, array2), \ - &(array2)->arr) +#define array_swap(array1, array2) \ + TYPE_CHECKS(void, ARRAY_TYPES_CHECK(array1, array2), \ + array_swap_i(&(array1)->arr, &(array2)->arr)) bool array_cmp_i(const struct array *array1, const struct array *array2) ATTR_PURE; @@ -343,13 +343,13 @@ bool array_cmp_i(const struct array *array1, bool array_equal_fn_i(const struct array *array1, const struct array *array2, int (*cmp)(const void*, const void *)) ATTR_PURE; -#define array_equal_fn(array1, array2, cmp) \ - array_equal_fn_i(&(array1)->arr - \ - ARRAY_TYPES_CHECK(array1, array2), \ - &(array2)->arr - \ - CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ - typeof(*(array2)->v))), \ - (int (*)(const void *, const void *))cmp) +#define array_equal_fn(array1, array2, cmp) \ + TYPE_CHECKS(bool, \ + ARRAY_TYPES_CHECK(array1, array2) || \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ + typeof(*(array2)->v))), \ + array_equal_fn_i(&(array1)->arr, &(array2)->arr, \ + (int (*)(const void *, const void *))cmp)) bool array_equal_fn_ctx_i(const struct array *array1, const struct array *array2, int (*cmp)(const void*, const void *, const void *), @@ -358,15 +358,14 @@ bool array_equal_fn_ctx_i(const struct array *array1, context can't be void* as ``const typeof(context)'' won't compile, so ``const typeof(*context)*'' is required instead, and that requires a complete type. */ -#define array_equal_fn_ctx(array1, array2, cmp, ctx) \ - array_equal_fn_ctx_i(&(array1)->arr - \ - ARRAY_TYPES_CHECK(array1, array2), \ - &(array2)->arr - \ - CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ - typeof(*(array2)->v), \ - const typeof(*ctx)*)), \ - (int (*)(const void *, const void *, const void *))cmp, \ - ctx) +#define array_equal_fn_ctx(array1, array2, cmp, ctx) \ + TYPE_CHECKS(bool, \ + ARRAY_TYPES_CHECK(array1, array2) || \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array1)->v), \ + typeof(*(array2)->v), \ + const typeof(*ctx)*)), \ + array_equal_fn_ctx_i(&(array1)->arr, &(array2)->arr, \ + (int (*)(const void *, const void *, const void *))cmp, ctx)) void array_reverse_i(struct array *array); #define array_reverse(array) \ @@ -374,18 +373,19 @@ void array_reverse_i(struct array *array); void array_sort_i(struct array *array, int (*cmp)(const void *, const void *)); #define array_sort(array, cmp) \ - array_sort_i(&(array)->arr - \ - CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array)->v), \ - typeof(*(array)->v))), \ - (int (*)(const void *, const void *))cmp) + TYPE_CHECKS(void, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(*(array)->v), \ + typeof(*(array)->v))), \ + array_sort_i(&(array)->arr, (int (*)(const void *, const void *))cmp)) void *array_bsearch_i(struct array *array, const void *key, int (*cmp)(const void *, const void *)); #define array_bsearch(array, key, cmp) \ - ARRAY_TYPE_CAST_MODIFIABLE(array)array_bsearch_i(&(array)->arr - \ - CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ - typeof(*(array)->v))), \ - (const void *)key, (int (*)(const void *, const void *))cmp) + TYPE_CHECKS(void *, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(*(array)->v))), \ + ARRAY_TYPE_CAST_MODIFIABLE(array)array_bsearch_i(&(array)->arr, \ + (const void *)key, (int (*)(const void *, const void *))cmp)) /* Returns pointer to first element for which cmp(key,elem)==0, or NULL */ const void *array_lsearch_i(const struct array *array, const void *key, @@ -395,13 +395,13 @@ static inline void *array_lsearch_modifiable_i(struct array *array, const void * { return (void *)array_lsearch_i(array, key, cmp); } -#define ARRAY_LSEARCH_CALL(modifiable, array, key, cmp) \ - array_lsearch##modifiable##i( \ - &(array)->arr - \ - CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ - typeof(*(array)->v))), \ - (const void *)key, \ - (int (*)(const void *, const void *))cmp) +#define ARRAY_LSEARCH_CALL(modifiable, array, key, cmp) \ + TYPE_CHECKS(void *, \ + CALLBACK_TYPECHECK(cmp, int (*)(typeof(const typeof(*key) *), \ + typeof(*(array)->v))), \ + array_lsearch##modifiable##i( \ + &(array)->arr, (const void *)key, \ + (int (*)(const void *, const void *))cmp)) #define array_lsearch(array, key, cmp) \ ARRAY_TYPE_CAST_CONST(array)ARRAY_LSEARCH_CALL(_, array, key, cmp) #define array_lsearch_modifiable(array, key, cmp) \ diff --git a/src/lib/buffer.h b/src/lib/buffer.h index 2c5ae797c8..0901bc8a6b 100644 --- a/src/lib/buffer.h +++ b/src/lib/buffer.h @@ -18,14 +18,15 @@ void buffer_create_from_data(buffer_t *buffer, void *data, size_t size); /* Create a non-modifiable buffer from given data. */ void buffer_create_from_const_data(buffer_t *buffer, const void *data, size_t size); -#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) > 401 -#define buffer_create_from_data(b,d,s) ({ \ - (void)COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)); \ - buffer_create_from_data((b), (d), (s)); }) -#define buffer_create_from_const_data(b,d,s) ({ \ - (void)COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)); \ - buffer_create_from_const_data((b), (d), (s)); }) -#endif +#define buffer_create_from_data(b,d,s) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)), \ + buffer_create_from_data((b), (d), (s))) +#define buffer_create_from_const_data(b,d,s) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE(__builtin_object_size((d),1) < ((s)>0?(s):1)), \ + buffer_create_from_const_data((b), (d), (s))) + /* Creates a dynamically growing buffer. Whenever write would exceed the current size it's grown. */ buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size); diff --git a/src/lib/hash.h b/src/lib/hash.h index 53b2ffa89b..65b6b8ef33 100644 --- a/src/lib/hash.h +++ b/src/lib/hash.h @@ -22,44 +22,34 @@ void hash_table_create(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size, hash_callback_t *hash_cb, hash_cmp_callback_t *key_compare_cb); -#if defined (__GNUC__) && !defined(__cplusplus) -# define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ - ({(void)COMPILE_ERROR_IF_TRUE( \ +#define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE( \ sizeof((*table)._key) != sizeof(void *) || \ - sizeof((*table)._value) != sizeof(void *)); \ - (void)COMPILE_ERROR_IF_TRUE( \ - !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ - int (*)(typeof((*table)._key), typeof((*table)._key))) && \ - !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ - int (*)(typeof((*table)._const_key), typeof((*table)._const_key)))); \ - (void)COMPILE_ERROR_IF_TRUE( \ + sizeof((*table)._value) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TRUE( \ + !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ + int (*)(typeof((*table)._key), typeof((*table)._key))) && \ + !__builtin_types_compatible_p(typeof(&key_cmp_cb), \ + int (*)(typeof((*table)._const_key), typeof((*table)._const_key)))) || \ + COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(&hash_cb), \ unsigned int (*)(typeof((*table)._key))) && \ !__builtin_types_compatible_p(typeof(&hash_cb), \ - unsigned int (*)(typeof((*table)._const_key)))); \ + unsigned int (*)(typeof((*table)._const_key)))), \ hash_table_create(&(*table)._table, pool, size, \ (hash_callback_t *)hash_cb, \ - (hash_cmp_callback_t *)key_cmp_cb);}) -#else -# define hash_table_create(table, pool, size, hash_cb, key_cmp_cb) \ - hash_table_create(&(*table)._table, pool, size, \ - (hash_callback_t *)hash_cb, \ - (hash_cmp_callback_t *)key_cmp_cb) -#endif + (hash_cmp_callback_t *)key_cmp_cb)) /* Create hash table where comparisons are done directly with the pointers. */ void hash_table_create_direct(struct hash_table **table_r, pool_t node_pool, unsigned int initial_size); -#if defined (__GNUC__) && !defined(__cplusplus) -# define hash_table_create_direct(table, pool, size) \ - ({(void)COMPILE_ERROR_IF_TRUE( \ +#define hash_table_create_direct(table, pool, size) \ + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TRUE( \ sizeof((*table)._key) != sizeof(void *) || \ - sizeof((*table)._value) != sizeof(void *)); \ - hash_table_create_direct(&(*table)._table, pool, size);}) -#else -# define hash_table_create_direct(table, pool, size) \ - hash_table_create_direct(&(*table)._table, pool, size) -#endif + sizeof((*table)._value) != sizeof(void *)), \ + hash_table_create_direct(&(*table)._table, pool, size)) #define hash_table_is_created(table) \ ((table)._table != NULL) @@ -76,23 +66,23 @@ void hash_table_clear(struct hash_table *table, bool free_collisions); void *hash_table_lookup(const struct hash_table *table, const void *key) ATTR_PURE; #define hash_table_lookup(table, key) \ - HASH_VALUE_CAST(table)hash_table_lookup((table)._table, \ - (const void *)((const char *)(key) + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._key, (table)._const_key, key))) + TYPE_CHECKS(void *, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._key, (table)._const_key, key), \ + HASH_VALUE_CAST(table)hash_table_lookup((table)._table, (key))) bool hash_table_lookup_full(const struct hash_table *table, const void *lookup_key, void **orig_key_r, void **value_r); #ifndef __cplusplus # define hash_table_lookup_full(table, lookup_key, orig_key_r, value_r) \ + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, lookup_key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, orig_key_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(orig_key_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)), \ hash_table_lookup_full((table)._table, \ - (void *)((const char *)(lookup_key) + \ - COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, lookup_key)), \ - (void *)((orig_key_r) + \ - COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, orig_key_r) + \ - COMPILE_ERROR_IF_TRUE(sizeof(*(orig_key_r)) != sizeof(void *))), \ - (void *)((value_r) + \ - COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r) + \ - COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)))) + (lookup_key), (void *)(orig_key_r), (void *)(value_r))) #else /* C++ requires (void **) casting, but that's not possible with strict aliasing, so .. we'll just disable the type checks */ @@ -107,18 +97,21 @@ void hash_table_insert(struct hash_table *table, void *key, void *value); If the key already exists, preserve the original key and update only the value.*/ void hash_table_update(struct hash_table *table, void *key, void *value); #define hash_table_insert(table, key, value) \ - hash_table_insert((table)._table, \ - (void *)((char*)(key) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key)), \ - (void *)((char*)(value) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value))) + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value), \ + hash_table_insert((table)._table, (void *)(key), (void *)(value))) #define hash_table_update(table, key, value) \ - hash_table_update((table)._table, \ - (void *)((char *)(key) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key)), \ - (void *)((char *)(value) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value))) + TYPE_CHECKS(void, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._key, key) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._value, value), \ + hash_table_update((table)._table, (void *)(key), (void *)(value))) bool hash_table_try_remove(struct hash_table *table, const void *key); #define hash_table_try_remove(table, key) \ - hash_table_try_remove((table)._table, \ - (const void *)((const char *)(key) + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, key))) + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE((table)._const_key, (table)._key, key), \ + hash_table_try_remove((table)._table, (const void *)(key))) #define hash_table_remove(table, key) \ STMT_START { \ if (unlikely(!hash_table_try_remove(table, key))) \ @@ -138,12 +131,12 @@ bool hash_table_iterate(struct hash_iterate_context *ctx, void **key_r, void **value_r); #ifndef __cplusplus # define hash_table_iterate(ctx, table, key_r, value_r) \ - hash_table_iterate(ctx, \ - (void *)((key_r) + \ - COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, key_r) + \ - COMPILE_ERROR_IF_TRUE(sizeof(*(key_r)) != sizeof(void *)) + \ - COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *))), \ - (void *)((value_r) + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r))) + TYPE_CHECKS(bool, \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._keyp, key_r) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(key_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TRUE(sizeof(*(value_r)) != sizeof(void *)) || \ + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE((table)._valuep, value_r), \ + hash_table_iterate(ctx, (void *)(key_r), (void *)(value_r))) #else /* C++ requires (void **) casting, but that's not possible with strict aliasing, so .. we'll just disable the type checks */ diff --git a/src/lib/macros.h b/src/lib/macros.h index e93ce6352d..6b2cdf2b3b 100644 --- a/src/lib/macros.h +++ b/src/lib/macros.h @@ -165,9 +165,12 @@ COMPILE_ERROR_IF_TRUE( \ !__builtin_types_compatible_p(typeof(_a1), typeof(_b)) && \ !__builtin_types_compatible_p(typeof(_a2), typeof(_b))) +# define TYPE_CHECKS(return_type, checks, func) \ + (FALSE ? (return_type)(checks) : (func)) #else # define COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE(_a, _b) 0 # define COMPILE_ERROR_IF_TYPES2_NOT_COMPATIBLE(_a1, _a2, _b) 0 +# define TYPE_CHECKS(return_type, checks, func) (func) #endif #if __GNUC__ > 2