mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-10-26 10:57:38 +08:00
Add cnum64_intersect_linear(), which intersects a cnum64 interval with the set of integers congruent to 'base' modulo 'step', i.e. the points base + step * k. It tightens the interval by rounding its signed minimum up and its signed maximum down to the nearest such point, and returns an empty cnum when no point falls within the interval. The arithmetic is done on the signed bounds [smin, smax]. The line is defined over the integers, while a cnum64 is an arc on the u64 circle, so a u64-modular computation would misplace the line for negative values whenever 'step' does not divide 2^64. Working from the signed bounds keeps the residues consistent, e.g. it can reason about a range like [-9, 3] with step 3. Also add imod() to bpf_verifier.h: a mathematical modulo returning an always-non-negative residue (unlike C's '%', which follows the sign of the dividend). It is used by cnum64_intersect_linear() and by the base/step tracking added in subsequent patches. This is a building block for tracking scalar registers whose value is known to lie on a line base + step * k. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261009-scev-minimal-rebase-v3-14-bcba9bc53ac9@gmail.com
157 lines
4.9 KiB
C
157 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
|
|
|
|
#include <linux/bits.h>
|
|
#include <linux/bpf_verifier.h> /* for imod() */
|
|
|
|
#define T 32
|
|
#include "cnum_defs.h"
|
|
#undef T
|
|
|
|
#define T 64
|
|
#include "cnum_defs.h"
|
|
#undef T
|
|
|
|
struct cnum32 cnum32_from_cnum64(struct cnum64 cnum)
|
|
{
|
|
if (cnum64_is_empty(cnum))
|
|
return CNUM32_EMPTY;
|
|
|
|
if (cnum.size >= U32_MAX)
|
|
return (struct cnum32){ .base = 0, .size = U32_MAX };
|
|
else
|
|
return (struct cnum32){ .base = (u32)cnum.base, .size = cnum.size };
|
|
}
|
|
|
|
/*
|
|
* Suppose 'a' and 'b' are laid out as follows:
|
|
*
|
|
* 64-bit number axis --->
|
|
*
|
|
* N*2^32 (N+1)*2^32 (N+2)*2^32 (N+3)*2^32
|
|
* ||------|---|=====|-------||----------|=====|-------||----------|=====|----|--||
|
|
* | |< b >| |< b >| |< b >| |
|
|
* | | | |
|
|
* |<--+--------------------------- a ---------------------------+--->|
|
|
* | |
|
|
* |<-------------------------- t -------------------------->|
|
|
*
|
|
* In such a case it is possible to infer a more tight representation t
|
|
* such that ∀ v ∈ a, (u32)v ∈ b: v ∈ t.
|
|
*/
|
|
struct cnum64 cnum64_cnum32_intersect(struct cnum64 a, struct cnum32 b)
|
|
{
|
|
/*
|
|
* To simplify reasoning, rotate the circles so that [virtual] a1 starts
|
|
* at u32 boundary, b1 represents b in this new frame of reference.
|
|
*/
|
|
struct cnum32 b1 = { b.base - (u32)a.base, b.size };
|
|
struct cnum64 t = a;
|
|
u64 d, b1_max;
|
|
|
|
if (cnum64_is_empty(a) || cnum32_is_empty(b))
|
|
return CNUM64_EMPTY;
|
|
|
|
if (cnum32_urange_overflow(b1)) {
|
|
b1_max = (u32)b1.base + (u32)b1.size; /* overflow here is fine and necessary */
|
|
if ((u32)a.size > b1_max && (u32)a.size < b1.base) {
|
|
/*
|
|
* N*2^32 (N+1)*2^32
|
|
* ||=====|------------|=====||=====|---------|---|=====||
|
|
* |b1 ->| |<- b1||b1 ->| | |<- b1|
|
|
* |<----------------- a1 ------------------>|
|
|
* |<-------------- t ------------>|<-- d -->| (after adjustment)
|
|
* ^
|
|
* b1_max
|
|
*/
|
|
d = (u32)a.size - b1_max;
|
|
t.size -= d;
|
|
} else {
|
|
/*
|
|
* No adjustments possible in the following cases:
|
|
*
|
|
* ||=====|------------|=====||===|=|-------------|=|===||
|
|
* |b1 ->| |<- b1||b1 +>| |<+ b1|
|
|
* |<----------------- a1 ------>| |
|
|
* |<----------------- (or) a1 ------------------->|
|
|
*/
|
|
}
|
|
} else {
|
|
if (t.size < b1.base)
|
|
/*
|
|
* N*2^32 (N+1)*2^32
|
|
* ||----------|--|=======|--||------>
|
|
* |<-- a1 -->| |<- b ->|
|
|
*/
|
|
return CNUM64_EMPTY;
|
|
/*
|
|
* N*2^32 (N+1)*2^32
|
|
* ||-------------|========|-||-----| -------|========|-||
|
|
* | |<- b1 ->| | |<- b1 ->|
|
|
* |<------------+ a1 ------------>|
|
|
* |<------ t ------>| (after adjustment)
|
|
*/
|
|
t.base += b1.base;
|
|
t.size -= b1.base;
|
|
b1_max = b1.base + b1.size;
|
|
d = 0;
|
|
if ((u32)a.size < b1.base)
|
|
/*
|
|
* N*2^32 (N+1)*2^32
|
|
* ||-------------|========|-||------|-------|========|-||
|
|
* | |<- b1 ->| | |<- b1 ->|
|
|
* |<------------+-- a1 --+-------->|
|
|
* |<- t ->|<-- d -->| (after adjustment)
|
|
*/
|
|
d = (u32)a.size + (BIT_ULL(32) - b1_max);
|
|
else if ((u32)a.size >= b1_max)
|
|
/*
|
|
* N*2^32 (N+1)*2^32
|
|
* ||--|========|------------||--|========|-------|-----||
|
|
* | |<- b1 ->| |<- b1 ->| |
|
|
* |<-+------------------ a1 ------------+------>|
|
|
* |<-------------- t --------------->|<- d ->| (after adjustment)
|
|
*/
|
|
d = (u32)a.size - b1_max;
|
|
if (t.size < d)
|
|
return CNUM64_EMPTY;
|
|
t.size -= d;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
/* Intersect 'in' with the set of integers defined by equation 'base + step * k'. */
|
|
struct cnum64 cnum64_intersect_linear(struct cnum64 in, u16 base, u16 step)
|
|
{
|
|
s64 smin = cnum64_smin(in);
|
|
s64 smax = cnum64_smax(in);
|
|
s64 lo, hi;
|
|
u16 d;
|
|
|
|
if (step <= 1 || cnum64_is_empty(in) || cnum64_srange_overflow(in))
|
|
return in;
|
|
/*
|
|
* Round smin up to the next value congruent to 'base' modulo 'step',
|
|
* i.e. increase smin by d = (base - smin) mod step:
|
|
*
|
|
* |<---- d ---->|
|
|
* |-----------|=============|...
|
|
* base+step*k smin base+step*(k+1)
|
|
*/
|
|
d = imod(base - imod(smin, step), step);
|
|
if ((u64)smax - (u64)smin < d)
|
|
return CNUM64_EMPTY;
|
|
lo = smin + d;
|
|
/*
|
|
* Round smax down to the previous value congruent to 'base' modulo 'step',
|
|
* i.e. decrease smax by d = (smax - base) mod step:
|
|
*
|
|
* |<--- d --->|
|
|
* ...|===========|-------------|
|
|
* base+step*k smax base+step*(k+1)
|
|
*/
|
|
d = imod(imod(smax, step) - base, step);
|
|
hi = smax - d;
|
|
return cnum64_from_srange(lo, hi);
|
|
}
|