From: Anton Protopopov <a.s.protopopov@gmail.com>
To: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Alan Maguire <almagui@kernel.org>,
John Fastabend <john.fastabend@gmail.com>,
Quentin Monnet <qmo@kernel.org>,
Christian Brauner <brauner@kernel.org>,
KP Singh <kpsingh@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
Sasha Levin <sashal@kernel.org>,
bpf@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: Anton Protopopov <a.s.protopopov@gmail.com>
Subject: [PATCH v1 bpf-next 11/18] bpf: Follow PHIs in bpf_scan_ir
Date: Fri, 6 Nov 2026 21:03:00 +0000 [thread overview]
Message-ID: <20261106210314.1767267-11-a.s.protopopov@gmail.com> (raw)
In-Reply-To: 20261106205523.1765622-1-a.s.protopopov@gmail.com
An error often reaches the return through a PHI in the block of the
return which joins it with the results of other paths, some of them
dirty. Take the incoming values of such a PHI on the edges from the
blocks which only clean paths reach. Do not follow a PHI in an earlier
block: a later branch may split its values between clean and dirty
paths.
Take also the errors which LazyValueInfo knows exactly: an incoming
value on its edge and a returned value at the return.
Signed-off-by: Anton Protopopov <a.s.protopopov@gmail.com>
---
scripts/llvm-plugins/bpf_scan_ir.cpp | 84 ++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 5 deletions(-)
diff --git a/scripts/llvm-plugins/bpf_scan_ir.cpp b/scripts/llvm-plugins/bpf_scan_ir.cpp
index 0fc60b2130b1..81de8f941568 100644
--- a/scripts/llvm-plugins/bpf_scan_ir.cpp
+++ b/scripts/llvm-plugins/bpf_scan_ir.cpp
@@ -8,11 +8,13 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/CaptureTracking.h"
+#include "llvm/Analysis/LazyValueInfo.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Analysis.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
+#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/GlobalVariable.h"
@@ -34,9 +36,40 @@ namespace {
static constexpr int MaxErrno = 4095;
+// is Range in [-4095, 0) ?
+static bool isErrnoRange(const ConstantRange &Range)
+{
+ unsigned Width = Range.getBitWidth();
+
+ if (Width != 32 && Width != 64)
+ return false;
+
+ auto L = APInt(Width, 0) - APInt(Width, MaxErrno);
+ auto R = APInt(Width, 0);
+ return !Range.isEmptySet() && ConstantRange(L, R).contains(Range);
+}
+
/* The errors which clean paths of a function return */
using ReturnValues = SmallVector<int64_t, 8>;
+/* Return true and set N if RANGE holds a single errno */
+static bool singleErrno(const ConstantRange &Range, int64_t &N)
+{
+ const APInt *C = Range.getSingleElement();
+
+ if (!C || !isErrnoRange(Range))
+ return false;
+ N = C->getSExtValue();
+ return true;
+}
+
+/* Return true and set N if the value at use U is a single errno */
+static bool singleErrnoAtUse(const Use &U, LazyValueInfo &LVI, int64_t &N)
+{
+ return U.get()->getType()->isIntegerTy() &&
+ singleErrno(LVI.getConstantRangeAtUse(U, false), N);
+}
+
/* Return true and set N if V is an errno constant */
static bool errnoConstant(const Value *V, int64_t &N)
{
@@ -49,6 +82,42 @@ static bool errnoConstant(const Value *V, int64_t &N)
return N >= -MaxErrno && N < 0;
}
+/*
+ * Add the errors which V, the value returned in RET, is known to be on the
+ * edges to RET from CLEANBBS to VALUES. V may be a PHI in RET. A PHI in an
+ * earlier block is not followed: a later branch may split its values between
+ * clean and dirty paths.
+ */
+static void collectErrnos(Value *V, BasicBlock *Ret, LazyValueInfo &LVI,
+ const SmallPtrSetImpl<BasicBlock *> &CleanBBs, ReturnValues &Values)
+{
+ int64_t N;
+
+ auto *Phi = dyn_cast<PHINode>(V);
+
+ if (!Phi || Phi->getParent() != Ret)
+ return;
+ for (unsigned i = 0; i < Phi->getNumIncomingValues(); i++) {
+ Value *Incoming = Phi->getIncomingValue(i);
+ BasicBlock *Pred = Phi->getIncomingBlock(i);
+
+ if (!CleanBBs.contains(Pred))
+ continue;
+ if (errnoConstant(Incoming, N)) {
+ Values.push_back(N);
+ continue;
+ }
+ /*
+ * LazyValueInfo knows the ranges of integers only. On an edge it
+ * also uses the branch condition, as in "if (err == -EINVAL) goto
+ * out", where err is the error.
+ */
+ if (Incoming->getType()->isIntegerTy() &&
+ singleErrno(LVI.getConstantRangeOnEdge(Incoming, Pred, Ret), N))
+ Values.push_back(N);
+ }
+}
+
class CleanInstructions {
DominatorTree &DT;
DenseMap<const Instruction *, bool> PrivateWrites;
@@ -111,7 +180,8 @@ static bool cleanBB(BasicBlock *BB, CleanInstructions &Clean)
}
/* Add the errors which clean paths of F are known to return to VALUES */
-static void cleanReturnValues(Function &F, CleanInstructions &Clean, ReturnValues &Values)
+static void cleanReturnValues(Function &F, LazyValueInfo &LVI, CleanInstructions &Clean,
+ ReturnValues &Values)
{
SmallPtrSet<BasicBlock *, 32> CleanBBs;
SmallVector<BasicBlock *, 32> Worklist;
@@ -154,8 +224,7 @@ static void cleanReturnValues(Function &F, CleanInstructions &Clean, ReturnValue
/*
* The returns of the clean blocks which only clean paths reach or which
- * one of them branches to. Dirty paths may also reach the latter, but
- * the returned constant is the same on all paths.
+ * one of them branches to
*/
for (BasicBlock &BB : F) {
auto *Return = dyn_cast<ReturnInst>(BB.getTerminator());
@@ -168,8 +237,11 @@ static void cleanReturnValues(Function &F, CleanInstructions &Clean, ReturnValue
Entered |= CleanBBs.contains(Pred);
if (!Entered)
continue;
- if (errnoConstant(Return->getReturnValue(), N))
+ if (singleErrnoAtUse(Return->getOperandUse(0), LVI, N)) {
Values.push_back(N);
+ continue;
+ }
+ collectErrnos(Return->getReturnValue(), &BB, LVI, CleanBBs, Values);
}
llvm::sort(Values);
Values.erase(std::unique(Values.begin(), Values.end()), Values.end());
@@ -237,10 +309,11 @@ public:
Flags |= FMODRET_FN_WEAK;
if (Ret->isIntegerTy(64))
Flags |= FMODRET_FN_RET64;
+ auto &LVI = FAM.getResult<LazyValueAnalysis>(F);
auto &DT = FAM.getResult<DominatorTreeAnalysis>(F);
CleanInstructions Clean(DT);
- cleanReturnValues(F, Clean, Values);
+ cleanReturnValues(F, LVI, Clean, Values);
/* A weak definition always has a record, see fmodret_metadata.h */
if (!Values.empty() || (Flags & FMODRET_FN_WEAK))
Descriptions.push_back({&F, Flags, std::move(Values)});
@@ -253,6 +326,7 @@ public:
PreservedAnalyses PA;
PA.preserve<FunctionAnalysisManagerModuleProxy>();
PA.preserveSet<CFGAnalyses>();
+ PA.preserve<LazyValueAnalysis>();
return PA;
}
--
2.43.0
next prev parent reply other threads:[~2026-11-06 20:52 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 21:02 [PATCH v1 bpf-next 01/18] bpf: Add BTF ID sets for fmod_ret targets Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 02/18] bpf: Add a btf_id_set8_entry helper Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 03/18] bpf: Build fmod_ret target sets from BTF decl tags Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 04/18] selftests/bpf: Declare bpf_fentry_test1 in tracing tests Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 05/18] bpf: Declare fmod_ret targets with BTF_FMODRET sets Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 06/18] selftests/bpf: Test fmod_ret BTF decl tags and attach permission Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 07/18] scripts: Add LLVM plugin infrastructure Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 08/18] bpf: Add the bpf_scan_ir LLVM plugin Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 09/18] bpf: Find fmod_ret candidates in bpf_scan_ir Anton Protopopov
2026-11-06 21:02 ` [PATCH v1 bpf-next 10/18] bpf: Allow writes to private stack objects " Anton Protopopov
2026-11-06 21:03 ` Anton Protopopov [this message]
2026-11-06 21:03 ` [PATCH v1 bpf-next 12/18] bpf: Follow integer casts " Anton Protopopov
2026-11-06 21:03 ` [PATCH v1 bpf-next 13/18] bpf: Check the return values of fmod_ret programs on found targets Anton Protopopov
2026-11-06 21:03 ` [PATCH v1 bpf-next 14/18] bpf: Require the JIT for fmod_ret programs on found 64-bit targets Anton Protopopov
2026-11-06 21:03 ` [PATCH v1 bpf-next 15/18] resolve_btfids: Tag fmod_ret candidates as targets Anton Protopopov
2026-11-06 21:50 ` bot+bpf-ci
2026-11-06 21:03 ` [PATCH v1 bpf-next 16/18] selftests/bpf: Test fmod_ret candidates found by bpf_scan_ir Anton Protopopov
2026-11-06 21:50 ` bot+bpf-ci
2026-11-06 21:03 ` [PATCH v1 bpf-next 17/18] selftests/bpf: Enable bpf_scan_ir in the config Anton Protopopov
2026-11-06 21:35 ` bot+bpf-ci
2026-11-06 21:03 ` [PATCH v1 bpf-next 18/18] bpftool: List and show fmod_ret targets Anton Protopopov
2026-11-06 21:35 ` bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106210314.1767267-11-a.s.protopopov@gmail.com \
--to=a.s.protopopov@gmail.com \
--cc=almagui@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=ihor.solodrai@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=memxor@gmail.com \
--cc=qmo@kernel.org \
--cc=sashal@kernel.org \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®