Make powerof2 macro ubsan safe
Subtracting one from the smallest value expressable by the provided
variable could cause an underflow operation. In particular, this is
problematic when code similar to:
uint64_t foo = 0;
if (powerof2(foo)) {
...;
}
is run with integer sanitization enabled. The macro would subtract one
from zero, underflowing and triggering the sanitizer.
Make the powerof2() macro ubsan safe, by explicitly handling underflows.
Note: This change DOES NOT make powerof2() accurate. We continue to
falsely return "true" for 0 and negative numbers (see attached tests).
Found while investigating Bug: 122975762
Test: see added testcase
Test: atest ziparchive-tests
Change-Id: I5408ce5c18868d797bcae8f115ddb7c4c1ced81e
diff --git a/libc/include/sys/param.h b/libc/include/sys/param.h
index 5cde4b7..16fed86 100644
--- a/libc/include/sys/param.h
+++ b/libc/include/sys/param.h
@@ -51,8 +51,17 @@
#endif
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
-/** Returns true if the argument is a power of two. */
-#define powerof2(x) ((((x)-1)&(x))==0)
+/*
+ * Returns true if the binary representation of the argument is all zeros
+ * or has exactly one bit set. Contrary to the macro name, this macro
+ * DOES NOT determine if the provided value is a power of 2. In particular,
+ * this function falsely returns true for powerof2(0) and some negative
+ * numbers.
+ */
+#define powerof2(x) \
+ ({ __typeof__(x) _x = (x); \
+ __typeof__(x) _x2; \
+ __builtin_add_overflow(_x, -1, &_x2) ? 1 : ((_x2&_x) == 0 ); })
/** Returns the lesser of its two arguments. */
#define MIN(a,b) (((a)<(b))?(a):(b))