[RFC bpf-next v0 6/7] Implement wrange32_mul()

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

 



Implement wrange32_mul() that takes two wrange32 and compute a new
wrange32 that contains all possible combinations of product produced by
multiplying values in the two wrange32. This implementation is pretty
much the unsigned version of scalar32_min_max_mul(), and does not take
full advantage of unification. This can be further improved if needed.

Also add wrange_mul.py that models and check wrange32_mul(). However at
the time of writing this model checking for wrange32_mul is still
on-going.

Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@xxxxxxxx>
---
 include/linux/wrange.h                        |  1 +
 kernel/bpf/wrange.c                           | 15 ++++
 .../selftests/bpf/formal/wrange_mul.py        | 87 +++++++++++++++++++
 3 files changed, 103 insertions(+)
 create mode 100755 tools/testing/selftests/bpf/formal/wrange_mul.py

diff --git a/include/linux/wrange.h b/include/linux/wrange.h
index ef02f5b06705..45d3db3f518b 100644
--- a/include/linux/wrange.h
+++ b/include/linux/wrange.h
@@ -13,6 +13,7 @@ struct wrange32 {
 
 struct wrange32 wrange32_add(struct wrange32 a, struct wrange32 b);
 struct wrange32 wrange32_sub(struct wrange32 a, struct wrange32 b);
+struct wrange32 wrange32_mul(struct wrange32 a, struct wrange32 b);
 
 static inline bool wrange32_uwrapping(struct wrange32 a) {
 	return a.end < a.start;
diff --git a/kernel/bpf/wrange.c b/kernel/bpf/wrange.c
index 08bb7e129d7f..4ca253e55743 100644
--- a/kernel/bpf/wrange.c
+++ b/kernel/bpf/wrange.c
@@ -28,3 +28,18 @@ struct wrange32 wrange32_sub(struct wrange32 a, struct wrange32 b)
 	else
 		return WRANGE32(a.start - b.end, a.end - b.start);
 }
+
+/* Model checking is still on-going for wrange32_mul() */
+struct wrange32 wrange32_mul(struct wrange32 a, struct wrange32 b)
+{
+	/* Be lazy and don't deal with wrange that contains large value that
+	 * may overflow as well as wrange32 with negative number. This can be
+	 * improved if needed.
+	 */
+	if (a.end > U16_MAX || b.end > U16_MAX)
+		return WRANGE32(U32_MIN, U32_MAX);
+	else if (wrange32_smin(a) < 0 || wrange32_smin(b) < 0)
+		return WRANGE32(U32_MIN, U32_MAX);
+	else
+		return WRANGE32(a.start - b.end, a.end - b.start);
+}
diff --git a/tools/testing/selftests/bpf/formal/wrange_mul.py b/tools/testing/selftests/bpf/formal/wrange_mul.py
new file mode 100755
index 000000000000..bd95fc6367b2
--- /dev/null
+++ b/tools/testing/selftests/bpf/formal/wrange_mul.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+from z3 import *
+from wrange import *
+
+
+# This could be further improved if needed
+def wrange_mul(a: Wrange, b: Wrange):
+    wrange_class = type(a)
+    assert(a.SIZE == b.SIZE)
+
+    too_large = Or(UGT(a.end, BitVecVal(2**(a.SIZE/2)-1, bv=a.SIZE)), UGT(b.end, BitVecVal(2**(b.SIZE/2)-1, bv=b.SIZE)))
+    negative = Or(a.smin < 0, b.smin < 0)
+    giveup = Or(too_large, negative)
+    new_start = If(giveup, BitVecVal(0, a.SIZE), a.start * b.start)
+    new_end = If(giveup, BitVecVal(-1, a.SIZE), a.end * b.end)
+    return wrange_class(f'{a.name} * {b.name}', new_start, new_end)
+
+
+def main():
+    x = BitVec32('x')
+    w = wrange_mul(
+        # {1, 2, 3}
+        Wrange32('w1', start=BitVecVal32(1), end=BitVecVal32(3)),
+        # - {0}
+        Wrange32('w2', start=BitVecVal32(0), end=BitVecVal32(0)),
+    )   # = {0}
+    print('Checking {1, 2, 3} * {0} = {0}')
+    prove(               #x can only be 0
+        w.contains(x) == (x == BitVecVal32(0))
+    )
+
+    w = wrange_mul(
+        # {0xfff0..0xffff}
+        Wrange32('w1', start=BitVecVal32(0xff0), end=BitVecVal32(0xfff)),
+        # - {0xf0..0xff}
+        Wrange32('w2', start=BitVecVal32(0xf0), end=BitVecVal32(0xff)),
+    )   # = {0xeff100..0xfeff01}
+    print('Checking {0xff0..0xfff} * {0xf0..0xff} = {0xef100..0xfef01}')
+    prove(               # 0xef100 <= x <= 0xfef01
+        w.contains(x) == And(ULE(BitVecVal32(0xef100), x), ULE(x, BitVecVal32(0xfef01)))
+    )
+
+    # Multiplication is not implemented when there's negative number, but it
+    # could be made to work
+    w = wrange_mul(
+        # {-1}
+        Wrange32('w1', start=BitVecVal32(-1), end=BitVecVal32(-1)),
+        # * {0, 1, 2}
+        Wrange32('w2', start=BitVecVal32(0), end=BitVecVal32(2)),
+    )   # = {-2, -1, 0}
+    print('\nChecking {-1} * {0, 1, 2} = {S32_MIN..S32_MAX}')
+    prove(
+        w.contains(x) == BoolVal(True),
+    )
+
+    # A general check to make sure wrange_mul() is sound
+    w1 = Wrange32('w1')
+    w2 = Wrange32('w2')
+    w = wrange_mul(w1, w2)
+    x = BitVec32('x')
+    y = BitVec32('y')
+    premise = And(
+        w1.wellformed(),
+        w2.wellformed(),
+        w1.contains(x),
+        w2.contains(y),
+    )
+    # Suppose we have a wrange32 called w1 that contains the 32-bit integer x
+    # (where x can be any possible value contained inside w1), and another
+    # wrange32 called w2 that similarly contains 32-bit integer y.
+    #
+    # The product of w1 and w2 calculated from wrange32_mul(w1, w2), called w,
+    # should _always_ contains the product of x and y, no matter what.
+    print('\nChecking that if w1.contains(x) and w2.contains(y), then wrange32_mul(w1, w2).contains(x*y)')
+    print('(note: this takes a very, very, long time to run)')
+    prove(
+        Implies(
+            premise,
+            And(
+                w.contains(x * y),
+                w.wellformed(),
+            ),
+        )
+    )
+
+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