Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 28 Sep 2020 17:54:33 +0200
From:      Alejandro Colomar <colomar.6.4.3@gmail.com>
To:        freebsd-bugs@freebsd.org
Cc:        Alejandro Colomar <colomar.6.4.3@gmail.com>
Subject:   [PATCH] <sys/param.h>: Add safety to nitems(): check that argument is an array
Message-ID:  <20200928155432.22245-1-colomar.6.4.3@gmail.com>

next in thread | raw e-mail | index | archive | help
'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@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




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20200928155432.22245-1-colomar.6.4.3>