Skip to content

Commit 20ec958

Browse files
harishankarvKernel Patches Daemon
authored andcommitted
bpf, verifier: Introduce tnum_step to step through tnum's members
This commit introduces tnum_step(), a function that, when given t, and a number z returns the smallest member of t larger than z. The number z must be greater or equal to the smallest member of t and less than the largest member of t. The first step is to compute j, a number that keeps all of t's known bits, and matches all unknown bits to z's bits. Since j is a member of the t, it is already a candidate for result. However, we want our result to be (minimally) greater than z. There are only two possible cases: (1) Case j <= z. In this case, we want to increase the value of j and make it > z. (2) Case j > z. In this case, we want to decrease the value of j while keeping it > z. (Case 1) j <= z t = xx11x0x0 z = 10111101 (189) j = 10111000 (184) ^ k (Case 1.1) Let's first consider the case where j < z. We will address j == z later. Since z > j, there had to be a bit position that was 1 in z and a 0 in j, beyond which all positions of higher significance are equal in j and z. Further, this position could not have been unknown in a, because the unknown positions of a match z. This position had to be a 1 in z and known 0 in t. Let k be position of the most significant 1-to-0 flip. In our example, k = 3 (starting the count at 1 at the least significant bit). Setting (to 1) the unknown bits of t in positions of significance smaller than k will not produce a result > z. Hence, we must set/unset the unknown bits at positions of significance higher than k. Specifically, we look for the next larger combination of 1s and 0s to place in those positions, relative to the combination that exists in z. We can achieve this by concatenating bits at unknown positions of t into an integer, adding 1, and writing the bits of that result back into the corresponding bit positions previously extracted from z. >From our example, considering only positions of significance greater than k: t = xx..x z = 10..1 + 1 ----- 11..0 This is the exact combination 1s and 0s we need at the unknown bits of t in positions of significance greater than k. Further, our result must only increase the value minimally above z. Hence, unknown bits in positions of significance smaller than k should remain 0. We finally have, result = 11110000 (240) (Case 1.2) Now consider the case when j = z, for example t = 1x1x0xxx z = 10110100 (180) j = 10110100 (180) Matching the unknown bits of the t to the bits of z yielded exactly z. To produce a number greater than z, we must set/unset the unknown bits in t, and *all* the unknown bits of t candidates for being set/unset. We can do this similar to Case 1.1, by adding 1 to the bits extracted from the masked bit positions of z. Essentially, this case is equivalent to Case 1.1, with k = 0. t = 1x1x0xxx z = .0.1.100 + 1 --------- .0.1.101 This is the exact combination of bits needed in the unknown positions of t. After recalling the known positions of t, we get result = 10110101 (181) (Case 2) j > z t = x00010x1 z = 10000010 (130) j = 10001011 (139) ^ k Since j > z, there had to be a bit position which was 0 in z, and a 1 in j, beyond which all positions of higher significance are equal in j and z. This position had to be a 0 in z and known 1 in t. Let k be the position of the most significant 0-to-1 flip. In our example, k = 4. Because of the 0-to-1 flip at position k, a member of t can become greater than z if the bits in positions greater than k are themselves >= to z. To make that member *minimally* greater than z, the bits in positions greater than k must be exactly = z. Hence, we simply match all of t's unknown bits in positions more significant than k to z's bits. In positions less significant than k, we set all t's unknonwn bits to 0 to retain minimality. In our example, in positions of greater significance than k (=4), t=x000. These positions are matched with z (1000) to produce 1000. In positions of lower significance than k, t=10x1. All unknown bits are set to 0 to produce 1001. The final result is: result = 10001001 (137) This concludes the computation for a result > z that is a member of t. The procedure for tnum_step() in this commit implements the idea described above. As a proof of correctness, we verified the algorithm against a logical specification of tnum_step. The specification asserts the following about the inputs t, z and output res that: 1. res is a member of t, and 2. res is strictly greater than z, and 3. there does not exist another value res2 such that 3a. res2 is also a member of t, and 3b. res2 is greater than z 3c. res2 is smaller than res We checked the implementation against this logical specification using an SMT solver. The verification formula in SMTLIB format is available at [1]. The verification returned an "unsat": indicating that no input assignment exists for which the implementation and the specification produce different outputs. In addition, we also automatically generated the logical encoding of the C implementation using Agni [2] and verified it against the same specification. This verification also returned an "unsat", confirming that the implementation is equivalent to the specification. The formula for this check is also available at [3]. [1] https://pastebin.com/raw/2eRWbiit [2] https://github.com/bpfverif/agni [3] https://pastebin.com/raw/EztVbBJ2 Co-developed-by: Matan Shachnai <[email protected]> Signed-off-by: Matan Shachnai <[email protected]> Co-developed-by: Srinivas Narayana <[email protected]> Signed-off-by: Srinivas Narayana <[email protected]> Co-developed-by: Santosh Nagarakatte <[email protected]> Signed-off-by: Santosh Nagarakatte <[email protected]> Signed-off-by: Harishankar Vishwanathan <[email protected]>
1 parent 2456350 commit 20ec958

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

include/linux/tnum.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,6 @@ static inline bool tnum_subreg_is_const(struct tnum a)
125125
{
126126
return !(tnum_subreg(a)).mask;
127127
}
128-
128+
/* Returns the smallest member of t larger than z. */
129+
u64 tnum_step(struct tnum t, u64 z);
129130
#endif /* _LINUX_TNUM_H */

kernel/bpf/tnum.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,55 @@ struct tnum tnum_const_subreg(struct tnum a, u32 value)
253253
{
254254
return tnum_with_subreg(a, tnum_const(value));
255255
}
256+
257+
/* Given tnum t, and a number z such that tmin <= z < tmax, where tmin
258+
* is the smallest member of the t (= t.value) and tmax is the largest
259+
* member of t (= t.value | t.mask) returns the smallest member of t
260+
* larger than z.
261+
*
262+
* For example,
263+
* t = x11100x0
264+
* z = 11110001 (241)
265+
* result = 11110010 (242)
266+
*
267+
* Note: if this function is called with z >= tmax, it just returns
268+
* early with tmax; if this function is called with z < tmin, the
269+
* algorithm already returns tmin.
270+
*/
271+
u64 tnum_step(struct tnum t, u64 z)
272+
{
273+
u64 tmax, j, p, q, r, s, v, u, w, res;
274+
u8 k;
275+
276+
tmax = t.value | t.mask;
277+
278+
/* if z >= largest member of t, return largest member of t */
279+
if (z >= tmax)
280+
return tmax;
281+
282+
/* keep t's known bits, and match all unknown bits to z */
283+
j = t.value | z & t.mask;
284+
285+
if (j > z) {
286+
p = ~z & t.value & ~t.mask;
287+
k = fls64(p); /* k is the most-significant 0-to-1 flip */
288+
q = U64_MAX << k;
289+
r = q & z; /* positions > k matched to z */
290+
s = ~q & t.value; /* positions <= k matched to t.value */
291+
v = r | s;
292+
res = v;
293+
} else {
294+
p = z & ~t.value & ~t.mask;
295+
k = fls64(p); /* k is the most-significant 1-to-0 flip */
296+
q = U64_MAX << k;
297+
r = q & t.mask & z; /* unknown positions > k, matched to z */
298+
s = q & ~t.mask; /* known positions > k, set to 1 */
299+
v = r | s;
300+
/* add 1 to unknown positions > k to make value greater than z */
301+
u = v + (1ULL << k);
302+
/* extract bits in unknown positions > k from u, rest from t.value */
303+
w = u & t.mask | t.value;
304+
res = w;
305+
}
306+
return res;
307+
}

0 commit comments

Comments
 (0)