]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add and use TYPE_CHECKS() macro to standardize type checking for functions
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 9 Nov 2020 14:09:05 +0000 (16:09 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 9 Nov 2020 15:06:11 +0000 (17:06 +0200)
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.

src/lib/array.h
src/lib/buffer.h
src/lib/hash.h
src/lib/macros.h

index f22305b7e494f096f8745f41f07e6810450484a7..eef813f322efc2851ff0c9213d36d992f1a7b6ff 100644 (file)
@@ -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)                      \
index 2c5ae797c806dc7a42cfb50af568c63c1c27cbeb..0901bc8a6b6782150250221ec86ad996b25c9a43 100644 (file)
@@ -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);
index 53b2ffa89b5409ee049898230ede54184492180c..65b6b8ef3388cae40f9f981fc19463deff18d2c3 100644 (file)
@@ -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 */
index e93ce6352d2695ec16a46e6d2de9256bfaf1d764..6b2cdf2b3b190b05c824170a29214a22f230d3db 100644 (file)
        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