]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
macro: add macro that simplifies going backwards through an array via pointers
authorLennart Poettering <lennart@poettering.net>
Mon, 21 Mar 2022 13:23:38 +0000 (14:23 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 23 Mar 2022 12:46:08 +0000 (21:46 +0900)
Inspired by #22797, let's avoid some UB when iterating through arrays.

src/basic/macro.h
src/test/test-macro.c

index fb1ae65d282dce8537586b1734f409377b5eee52..68d8b062e87330983304a1ed875014d37d65ea00 100644 (file)
@@ -463,4 +463,20 @@ typedef struct {
 
 assert_cc(sizeof(dummy_t) == 0);
 
+/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for for
+ * loops that count down from a high pointer until some base. A naive loop would implement this like this:
+ *
+ * for (p = end-1; p >= base; p--) …
+ *
+ * But this is not safe because p before the base is UB in C. With this macro the loop becomes this instead:
+ *
+ * for (p = PTR_SUB1(end, base); p; p = PTR_SUB1(p, base)) …
+ *
+ * And is free from UB! */
+#define PTR_SUB1(p, base)                                \
+        ({                                               \
+                typeof(p) _q = (p);                      \
+                _q && _q > (base) ? &_q[-1] : NULL;      \
+        })
+
 #include "log.h"
index 960ff4bc092592d0f4c91af8fad7bf914372edbc..ba319953cd86c83f5c854b7aedb1f4b864b239ed 100644 (file)
@@ -433,4 +433,27 @@ TEST(DECIMAL_STR_MAX) {
         assert_se(DECIMAL_STR_MAX(uint64_t) == DECIMAL_STR_WIDTH(u64_longest)+1);
 }
 
+TEST(PTR_SUB1) {
+        static const uint64_t x[4] = { 2, 3, 4, 5 };
+        const uint64_t *p;
+
+        p = x + ELEMENTSOF(x)-1;
+        assert_se(*p == 5);
+
+        p = PTR_SUB1(p, x);
+        assert_se(*p == 4);
+
+        p = PTR_SUB1(p, x);
+        assert_se(*p == 3);
+
+        p = PTR_SUB1(p, x);
+        assert_se(*p == 2);
+
+        p = PTR_SUB1(p, x);
+        assert_se(!p);
+
+        p = PTR_SUB1(p, x);
+        assert_se(!p);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);