[PATCH] <sys/param.h>: Add safety to nitems(): check that argument is an array
Alejandro Colomar
colomar.6.4.3 at gmail.com
Mon Sep 28 15:55:01 UTC 2020
'nitems()' calculates the length of an array in number of items.
It is safe: if a pointer is passed to the macro (or function, in C++),
the compilation is broken due to:
- In >= C11: _Static_assert()
- In C89, C99: Negative anonymous bitfield
- In C++: The template requires an array
This patch also adds some other macros, which are required by 'nitems()':
__is_same_type(_a, _b):
Returns non-zero if the two input arguments are of the same type.
__is_array(_a):
Returns non-zero if the input argument is of an array type.
__must_be(_e, _msg):
Allows using _Static_assert() everywhere an expression can be used.
It evaluates '(int)0' or breaks the compilation.
__must_be_array(_a):
It evaluates to '(int)0' if the argument is of an array type.
Else, it breaks compilation.
__nitems(_a):
It implements the basic sizeof division needed to calculate
the array length. It does what nitems(_a) did before this patch.
I'd like to put the contents of this patch in the public domain.
Feel free to do anything with it.
Signed-off-by: Alejandro Colomar <colomar.6.4.3 at gmail.com>
---
Hi,
I sent a patch like this one to glibc a few days ago.
I hope it's good for you too :-)
Disclaimer:
I don't have a FreeBSD system, so the code is not tested there.
Please test that it doesn't break anything.
Cheers,
Alex
sys/sys/param.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 48 insertions(+), 1 deletion(-)
diff --git a/sys/sys/param.h b/sys/sys/param.h
index d03f64f13a6..d512d21768a 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -299,7 +299,6 @@
#ifndef howmany
#define howmany(x, y) (((x)+((y)-1))/(y))
#endif
-#define nitems(x) (sizeof((x)) / sizeof((x)[0]))
#define rounddown(x, y) (((x)/(y))*(y))
#define rounddown2(x, y) ((x)&(~((y)-1))) /* if y is power of two */
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */
@@ -310,6 +309,54 @@
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
+/* Macros related to the types of variables */
+#define __is_same_type(_a, _b) ( \
+ __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b)) \
+)
+#define __is_array(_a) (!__is_same_type((_a), &(_a)[0]))
+
+/* Macros for embedding _Static_assert() in expressions */
+#if __STDC_VERSION__ >= 201112L
+# define __must_be(_e, _msg) ( \
+ 0 * (int)sizeof( \
+ struct { \
+ _Static_assert((_e), _msg); \
+ char_ISO_C_forbids_a_struct_with_no_members; \
+ } \
+ ) \
+)
+#else /* __STDC_VERSION__ < 201112L */
+# define __must_be(_e, _msg) ( \
+ 0 * (int)sizeof( \
+ struct { \
+ int : (-!(_e)); \
+ char _ISO_C_forbids_a_struct_with_no_members; \
+ } \
+ ) \
+)
+#endif /* __STDC_VERSION__ < 201112L */
+#define __must_be_array(_a) __must_be(__is_array(_a), "Must be an array!")
+
+/* Macros for array sizes */
+#if defined(__cplusplus)
+# if __cplusplus >= 201103L
+template<typename _tp, std::size_t _len>
+ constexpr inline std::size_t
+ nitems(const _tp(&)[_len]) noexcept
+{
+ return _len;
+}
+# else /* __cplusplus < 201103L */
+template<typename _tp, std::size_t _len>
+ char
+ (&__nitems_chararr(const _Tp(&)[_len]))[_len];
+# define nitems(_a) (sizeof(__nitems_chararr(_a)))
+# endif /* __cplusplus < 201103L */
+#else /* !defined(__cplusplus) */
+# define __nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+# define nitems(_a) (__nitems(_a) + __must_be_array(_a))
+#endif /* !defined(__cplusplus) */
+
#ifdef _KERNEL
/*
* Basic byte order function prototypes for non-inline functions.
--
2.28.0
More information about the freebsd-bugs
mailing list