aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Thompson <daniel.thompson@linaro.org>2022-05-04 12:54:20 +0100
committerDaniel Thompson <daniel.thompson@linaro.org>2022-05-04 12:54:20 +0100
commitad6525bf355a301ca52b1dc3639fa340409c79b9 (patch)
tree464df5c3281f2fc6af10846c5edcb13beb9b7fc5
parent9a4791991f0501aaed0ec1b3f4bbbc8950418818 (diff)
[RFC] linux/err.h: Refactor IS_ERR_VALUE(x) to improve clang reasoningclang-analyzer/initial_review
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
-rw-r--r--include/linux/err.h53
1 files changed, 53 insertions, 0 deletions
diff --git a/include/linux/err.h b/include/linux/err.h
index a139c64aef2a..6ebb9aea23b0 100644
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -19,8 +19,61 @@
#ifndef __ASSEMBLY__
+#ifdef __clang_analyzer__
+//#define WANT_SIGNED_LONG_IS_ERR_VALUE
+#define WANT_UNSIGNED_LONG_IS_ERR_VALUE
+#endif
+
+#if defined WANT_SIGNED_LONG_IS_ERR_VALUE
+
+/*
+ * This should be an identical check to the "real" IS_ERR_VALUE().
+ *
+ * It's only purpose is to ensure clang-analyzer learns that x != 0
+ * when IS_ERR_VALUE(x) is true. It cannot make this inference from
+ * the normal implementation of IS_ERR_VALUE() above.
+ */
+#define IS_ERR_VALUE(x) \
+ unlikely(({ \
+ long _l = (long)(void *)(x); \
+ _l < 0 && -MAX_ERRNO <= _l; \
+ }))
+
+#elif defined WANT_UNSIGNED_LONG_IS_ERR_VALUE
+
+/*
+ * This is partway between the two IS_ERR_VALUE() implementations above and
+ * below.
+ *
+ * It is structurally like the one above and explicitly proves that x is
+ * non-zero. However this is not enough to prevent clang-analyzer from later
+ * assuming that x is non-zero.
+ *
+ * This implies that the failure to reason about the range of x is linked to
+ * casting it as an unsigned long.
+ */
+#define IS_ERR_VALUE(x) \
+ unlikely(({ \
+ unsigned long _l = (unsigned long)(void *)(x); \
+ _l != 0 && -MAX_ERRNO <= _l; \
+ }))
+
+#else
+
+/*
+ * This is the original implementation of IS_ERR_VALUE().
+ *
+ * By treating the value as unsigned then the range check can be a single
+ * comparison. Currently clang-analyzer does not use this predicate
+ * to reason about the possible values of x.
+ *
+ * In particular clang-analyzer will explore code paths where
+ * IS_ERR_VALUE(x) is true but then later assume that x is zero.
+ */
#define IS_ERR_VALUE(x) unlikely((unsigned long)(void *)(x) >= (unsigned long)-MAX_ERRNO)
+#endif
+
static inline void * __must_check ERR_PTR(long error)
{
return (void *) error;