[RFC bpf-next v0 1/7] Add inital wrange32 definition along with checks for umin/umax

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add struct wrange32 (short for "32-bit wrapped range") and umin/umax
helpers, the latter simply return start/end at the moment. We call the
fields start and end instead of umin and umax, because later patch will
lift the umin <= umax requirement, so we can have cases where umax <
umin; and continuing to call them umin/umax would be confusing.

A struct wrange32 modeled with z3Py is also attached to show that wrange32 in
its current form work as intended.

Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@xxxxxxxx>
---
 include/linux/wrange.h                       |  26 ++++
 tools/testing/selftests/bpf/formal/wrange.py | 150 +++++++++++++++++++
 2 files changed, 176 insertions(+)
 create mode 100644 include/linux/wrange.h
 create mode 100755 tools/testing/selftests/bpf/formal/wrange.py

diff --git a/include/linux/wrange.h b/include/linux/wrange.h
new file mode 100644
index 000000000000..e2316c7bbb2d
--- /dev/null
+++ b/include/linux/wrange.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _LINUX_WRANGE_H
+#define _LINUX_WRANGE_H
+
+#include <linux/types.h>
+
+struct wrange32 {
+	/* Start with a usual u32 min/max.
+	 *
+	 * Requiring umin/start <= umax/end, and cannot be use to track s32
+	 * range.
+	 */
+	u32 start; /* umin */
+	u32 end; /* umax */
+};
+
+/* Helper functions that will be required later */
+static inline u32 wrange32_umin(struct wrange32 a) {
+	return a.start;
+}
+
+static inline u32 wrange32_umax(struct wrange32 a) {
+	return a.end;
+}
+
+#endif /* _LINUX_WRANGE_H */
diff --git a/tools/testing/selftests/bpf/formal/wrange.py b/tools/testing/selftests/bpf/formal/wrange.py
new file mode 100755
index 000000000000..8836f4cbbedb
--- /dev/null
+++ b/tools/testing/selftests/bpf/formal/wrange.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+import abc
+from z3 import *
+
+
+# Helpers
+BitVec32 = lambda n: BitVec(n, bv=32)
+BitVecVal32 = lambda v: BitVecVal(v, bv=32)
+
+class Wrange(abc.ABC):
+    SIZE = None # Bitwidth, this will be defined in the subclass
+    name: str
+    start: BitVecRef
+    end: BitVecRef
+
+    def __init__(self, name, start=None, end=None):
+        self.name = name
+        self.start = BitVec(f'Wrange32-{name}-start', bv=self.SIZE) if start is None else start
+        assert(self.start.size() == self.SIZE)
+        self.end = BitVec(f'Wrange32-{name}-end', bv=self.SIZE) if end is None else end
+        assert(self.end.size() == self.SIZE)
+
+    def wellformed(self):
+        # start <= end
+        return ULE(self.start, self.end)
+
+    @property
+    def umin(self):
+        return self.start
+
+    @property
+    def umax(self):
+        return self.end
+
+    # Not used in wrange.c, but helps with checking later
+    def contains(self, val: BitVecRef):
+        assert(val.size() == self.SIZE)
+        # umin <= val <= umax
+        return And(ULE(self.umin, val), ULE(val, self.umax))
+
+
+class Wrange32(Wrange):
+    SIZE = 32 # Working with 32-bit integers
+
+
+__all__ = [
+        'Wrange',
+        'Wrange32',
+        'BitVec32',
+        'BitVecVal32',
+]
+
+
+def main():
+    # A random 32-bit integer called x, that can be of any possible value
+    # unless constrained
+    x = BitVec32('x')
+
+    w1 = Wrange32('w1', start=BitVecVal32(1), end=BitVecVal32(1))
+    print(f'Given w1 start={w1.start} end={w1.end}')
+    print('\nChecking w1 is wellformed')
+    prove(
+        w1.wellformed(),
+    )
+    print('\nChecking w1.umin is 1')
+    prove(
+        w1.umin == BitVecVal32(1),
+    )
+    print('\nChecking w1.umax is 1')
+    prove(
+        w1.umax == BitVecVal32(1),
+    )
+    print('\nChecking that w1 contains 1')
+    prove(
+        w1.contains(BitVecVal32(1)),
+    )
+    print('\nChecking that w1 is a set of {1}, with only one element')
+    prove(
+        w1.contains(x) == (x == BitVecVal32(1)),
+    )
+
+    w2 = Wrange32('w2', start=BitVecVal32(2), end=BitVecVal32(2**32 - 1))
+    print(f'\nGiven w2 start={w2.start} end={w2.end}')
+    print('\nChecking w2 is wellformed')
+    prove(
+        w2.wellformed(),
+    )
+    print('\nChecking w2.umin is 2')
+    prove(
+        w2.umin == BitVecVal32(2),
+    )
+    print('\nChecking w2.umax is 2**32-1')
+    prove(
+        w2.umax == BitVecVal32(2**32 - 1),
+    )
+    print('\nChecking that w2 contains 2**32 - 1')
+    prove(
+        w2.contains(BitVecVal32(2**32 - 1)),
+    )
+    print('\nChecking that w2 does NOT contains 1')
+    prove(
+        Not(w2.contains(BitVecVal32(1))),
+    )
+    print('\nChecking that w2 is a set of {2..2**32-1}')
+    prove(
+        # Contrain x such that 2 <= x <= 2**32-1 and check that if x between 2
+        # and 2**32-1 (inclusive), then w2.contains(x) will return true.
+        #
+        # In addition to that, check that the reverse is also true. That is if
+        # x it _not_ a value between 2 and 2**32-1, then w2.contains(x) will
+        # return false.
+        w2.contains(x) == And(ULE(BitVecVal32(2), x), ULE(x, BitVecVal32(2**32-1))),
+    )
+
+    # Right now our semantic doesn't allow umax/end < umin/start
+    w3 = Wrange32('w3', start=BitVecVal32(2), end=BitVecVal32(0))
+    print(f'\nGiven w3 start={w3.start} end={w3.end}')
+    print('\nChecking w3 is NOT wellformed')
+    prove(
+        Not(w3.wellformed()),
+    )
+
+    # General checks that does not assum the value of start/end, except that it
+    # meets the requirement that start <= end.
+    w = Wrange32('w') # Given a Wrange32 called w
+    x = BitVec32('x') # And an 32-bit integer x (redeclared for clarity)
+    print(f'\nGiven any possible Wrange32 called w, and any possible 32-bit integer called x')
+    print('\nChecking if w.contains(x) == True, then w.umin <= (u32)x is also true')
+    prove(
+        Implies(
+            And(
+                w.wellformed(),
+                w.contains(x),
+            ),
+            ULE(w.umin, x),
+        )
+    )
+    print('\nChecking if w.contains(x) == True, then (u32)x <= w.umax is also true')
+    prove(
+        Implies(
+            And(
+                w.wellformed(),
+                w.contains(x),
+            ),
+            ULE(x, w.umax),
+        )
+    )
+
+if __name__ == '__main__':
+    main()
-- 
2.42.0





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux