From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.128.47 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794328180; b=F+S3wF9UK/J+c624r2586vdd1i2HUfVsBna8nSr0/UPuJkVxJYnGMUcrrQxxo/IAr0LL bcNL8KWY70X2ezCQS2DqOEADVl+87DkK7sAF23JoyE0r2ASzdH1o9N2R0N3pcSLKIw4gu QoHxsokOvyUSBTmcUr72x6wxg9iWpzAKAc7230YFNzkIcwwHzVtxxdPbHKb+A3OF7zgDZ Kb/A/PrX7+e9gaAOjf/Qw6sqkXND1mWKCtICI0XRgwa+nBX9bqJfEQ2TBxBFutZGa3KO/ DYDOeVzZ2uo/tWc3yebzHbQbvmKQZVYKO2npNPYc5+wTrdY2lOqoVmREVjoku6vYSxw== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794328180; h=DMARC-Filter:Received:DKIM-Signature:X-Google-DKIM-Signature: X-Forwarded-Encrypted:X-Gm-Message-State:X-Gm-Gg:X-Received:Received: From:To:Cc:Subject:Date:Message-ID:X-Mailer:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding; bh=slpIs/HinfX8orW6FfIUeyL7Jch9yZ8MPXuFx7Rx4z4=; b=2lE4mLIDHYstS2L62NEF8dM+Tpt+G86W/TeHv7n7PAYVdB9wnK7N744OunFMhkBadpkM QKb8UxZDNg2vaaWMMWMka9LSepADTvfxmk0GwQqFXWVSVvUyoFeUfkmY1SC2cfU9Nai8Z 3IKYQLaXF7bB9OuKY/5ARLfFqm2kkbAmCytrw7+lwZFnKQhEQ09a/E2+V8xsVnqkH7T7e /S8BYTDnjM3Bw836mCTdFbin4q/0DQ28NSSaLgQOdLvb/ZRmDUsp11gEsBv+sNVqaYPi/ 0IE1WK/hPteuPmO51gaTc3uTgXMX05e+HGs93ACcPFowCgCahO0ndQS1xGmlwtOZdfQ== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=K8z5h8Ww; arc=none smtp.remote-ip=209.85.128.47 Received: from mail-wm1-f47.google.com (mail-wm1-f47.google.com [209.85.128.47]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3D5A0606A89F for ; Tue, 10 Nov 2026 16:29:39 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 3D5A0606A89F Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20260707 header.b=K8z5h8Ww Received: by mail-wm1-f47.google.com with SMTP id 5b1f17b1804b1-4b467581b98so20931655e9.0 for ; Tue, 10 Nov 2026 08:29:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20260707; t=1794328178; x=1794932978; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=slpIs/HinfX8orW6FfIUeyL7Jch9yZ8MPXuFx7Rx4z4=; b=K8z5h8WwIy3qV71SEQiQc/wqoD9AkocZMNSA2AieW/NNac/vbySxOGGBpNLiR1POPx h86zDGtxFYKk0Z+Kpt00xkGNcyJNZXQJ0rh6KmU8ks3uoCK4sJ4Afu64E8BQEQGGj0Of H5oxALxPZb6SjIJlaWG33+tFyDPLKtTSs3CnVA0tl4demd/c/ATXz1Y3bQ4eL2VG3HSf QL7w3VW6MznGDJ9CQyXs509EM2bgrFRzRTGNKtUmXhLr6empvXu9cFYYqjeYCDz5ShIz SSKbC6DzSpwz/KCrDuTblNlmchzSpERkDFsvYTxQTfJfhZhyLRZeRP1b4ESYgFkGhiUz GFHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794328178; x=1794932978; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=slpIs/HinfX8orW6FfIUeyL7Jch9yZ8MPXuFx7Rx4z4=; b=iwXOBvHF2yaHgYf7F3CzESNlivwe9Sk41wEYzfRLyD0TwEUVECrSneBROU8PNcvoZm v8mMpruTgRTYTVoM2jwgJen8+1tWfea5OVbTRJUVF+750CJDY0DIpVh1clHNVwik+Y7h 3k3ah2W+pTudkoFnf7ZCcZlRmckwms0vShhek5CBEzUZruRegsGrKPC/YHCsnvoyeJmr qY/yAXfSHFbE3x+OPsecvpbKuAtO/QDXAG7dsozZDCptpfciPX2asxxajBi1g/iYic// 8LkbwhSpHxs9UBngyjqJSr0/amMcOsK3wMAKsqOChJXVqhZLZ4gXDQaRG45RK/b03FZL LS7Q== X-Forwarded-Encrypted: i=1; AL+hErGDrPYh/MD5Sr3VQZYeT/nw+uDDG6TdiLldx9/IDyiRMGjpb7V6k6lYQkvXTDk5ksNkFNyrNpwYXaju0M8=@vger.kernel.org X-Gm-Message-State: APuU4Z/H5N6MmIy4Oa/1jcD4H1UbVb+xaC00VYdNUcCQzAzyCJO71+8M Q3iPUvrzGciBOTTEwzFJwPVN3sNPo8LGt+wthmSo1iOgIISJprj/LX+K X-Gm-Gg: AYBFou0TerBZEc0KSOt0MLN5EVeK5z8D0k4IK9fTbNA5+1wLMNCIBEa7iUTmS9pWzD7 bNI+4g8G4sGJv9UeN4KMCEFyrU3A3F7OXGBqaaAW6QqXOB81WAyZymsRkSJLUDCagSL6pVg8+Ey quJsx753O5H0LYYbz+N7Rt/gSZj5KBpxEOgoULVrWN+qyttal2LTQr8W5Mj27FIwDrGf0ze4NES BqKJKpskd/LoC+sKmcVM721aaRnIMHGXNam7hhujqvUiUehVpiaftgC9xMQS2139AQh/i5ksaKM xK+3o9DnQG2dhHihjm6AA8RVwp6R/paTrZ1hqjJwexBt90kPA/Gv2c5LxyApLbmhKhTmjYEFMHn kGVi+IMTlOVLLfcSiO+fv4ZZ/txUCt05cL2/xIdzGehXTESw/QqUHyFiX8Our1sHvoAfW8P5Y7V zNmVBZuzXU11p0XIQOdfWhHBsYkT+9zuDrV1uPl0VMkN1gtWS0UvOuRO9MlGNsz1lEIFnFbdIlI qabEMxsM0gOrrihUPtSr1TTEunqNhivm2PGFr360xjfXZgtFV2ejQhWx2HITI0ec/iYMo47kRAu kNyY1hETMQEAI6M5DybWlvxebFftny18cP92HfVNBrfg71k= X-Received: by 2002:a05:600c:3b2a:b0:4a1:d675:b031 with SMTP id 5b1f17b1804b1-4ba02336374mr56174855e9.6.1794328177518; Tue, 10 Nov 2026 08:29:37 -0800 (PST) Received: from localhost.localdomain ([2a04:ee41:4:b2de:1ac0:4dff:fe0f:3782]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4ba01fea745sm71287485e9.10.2026.11.10.08.29.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Nov 2026 08:29:36 -0800 (PST) From: Anton Protopopov To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Eduard Zingerman , Kumar Kartikeya Dwivedi , Alan Maguire , John Fastabend , Quentin Monnet , Christian Brauner , KP Singh , Yonghong Song , Jiri Olsa , Emil Tsalapatis , Ihor Solodrai , Sasha Levin , bpf@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Anton Protopopov Subject: [PATCH v3 bpf-next 10/18] bpf: Allow writes to private stack objects in bpf_scan_ir Date: Tue, 10 Nov 2026 16:40:30 +0000 Message-ID: <20261110164046.454431-11-a.s.protopopov@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261110164046.454431-1-a.s.protopopov@gmail.com> References: <20261110164046.454431-1-a.s.protopopov@gmail.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit A write to a stack object of the function is not observable outside of it as long as the object has not escaped. Let such writes and memsets stay on a clean path. A write after the object may have been captured, a volatile or atomic one and a write to other memory still make the path dirty. Signed-off-by: Anton Protopopov --- scripts/llvm-plugins/bpf_scan_ir.cpp | 85 +++++++++++++++++++++------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/scripts/llvm-plugins/bpf_scan_ir.cpp b/scripts/llvm-plugins/bpf_scan_ir.cpp index 3684918eb7fa..c45fb97d1b93 100644 --- a/scripts/llvm-plugins/bpf_scan_ir.cpp +++ b/scripts/llvm-plugins/bpf_scan_ir.cpp @@ -4,13 +4,17 @@ #include "../bpf/fmodret_metadata.h" #include +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/CaptureTracking.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/Constants.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" @@ -50,41 +54,76 @@ static bool errnoConstant(const Value *V, int64_t &N) return C && isErrno(C->getValue(), N); } -static bool cleanInstruction(const Instruction &I) -{ - if (auto *Intrinsic = dyn_cast(&I)) { - switch (Intrinsic->getIntrinsicID()) { - case Intrinsic::assume: - case Intrinsic::lifetime_start: - case Intrinsic::lifetime_end: - case Intrinsic::invariant_start: - case Intrinsic::invariant_end: - return true; - default: - break; +class CleanInstructions { + DominatorTree &DT; + DenseMap PrivateWrites; + + bool privateDestination(const Value *Pointer, const Instruction &I) + { + auto It = PrivateWrites.find(&I); + if (It != PrivateWrites.end()) + return It->second; + auto *Allocation = dyn_cast(getUnderlyingObject(Pointer)); + bool Private = false; + + if (Allocation && Allocation->getFunction() == I.getFunction()) { + // A later call may receive the initialized object. Only captures + // that can precede this write make the write externally visible. +#if LLVM_VERSION_MAJOR >= 21 + Private = !PointerMayBeCapturedBefore(Allocation, true, &I, &DT, true, 256); +#else + Private = !PointerMayBeCapturedBefore(Allocation, true, true, &I, &DT, true, 256); +#endif } + PrivateWrites[&I] = Private; + return Private; } - return !I.mayHaveSideEffects(); -} -static bool cleanBB(BasicBlock *BB) +public: + explicit CleanInstructions(DominatorTree &DT) : DT(DT) {} + + bool isClean(const Instruction &I) + { + if (auto *Store = dyn_cast(&I)) + return !Store->isVolatile() && !Store->isAtomic() && + privateDestination(Store->getPointerOperand(), I); + if (auto *Memory = dyn_cast(&I)) + return !Memory->isVolatile() && + privateDestination(Memory->getRawDest(), I); + if (auto *Intrinsic = dyn_cast(&I)) { + switch (Intrinsic->getIntrinsicID()) { + case Intrinsic::assume: + case Intrinsic::lifetime_start: + case Intrinsic::lifetime_end: + case Intrinsic::invariant_start: + case Intrinsic::invariant_end: + return true; + default: + break; + } + } + return !I.mayHaveSideEffects(); + } +}; + +static bool cleanBB(BasicBlock *BB, CleanInstructions &Clean) { for (Instruction &I : *BB) { - if (!cleanInstruction(I)) + if (!Clean.isClean(I)) return false; } return true; } /* Add the errors which clean paths of F are known to return to VALUES */ -static void cleanReturnValues(Function &F, ReturnValues &Values) +static void cleanReturnValues(Function &F, CleanInstructions &Clean, ReturnValues &Values) { SmallPtrSet CleanBBs; SmallVector Worklist; bool Changed; /* The clean blocks which the entry reaches through clean blocks */ - if (cleanBB(&F.getEntryBlock())) { + if (cleanBB(&F.getEntryBlock(), Clean)) { CleanBBs.insert(&F.getEntryBlock()); Worklist.push_back(&F.getEntryBlock()); } @@ -92,7 +131,7 @@ static void cleanReturnValues(Function &F, ReturnValues &Values) BasicBlock *BB = Worklist.pop_back_val(); for (BasicBlock *Successor : successors(BB)) { - if (!CleanBBs.contains(Successor) && cleanBB(Successor)) { + if (!CleanBBs.contains(Successor) && cleanBB(Successor, Clean)) { CleanBBs.insert(Successor); Worklist.push_back(Successor); } @@ -128,7 +167,7 @@ static void cleanReturnValues(Function &F, ReturnValues &Values) bool Entered = CleanBBs.contains(&BB); int64_t N; - if (!Return || !Return->getReturnValue() || !cleanBB(&BB)) + if (!Return || !Return->getReturnValue() || !cleanBB(&BB, Clean)) continue; for (BasicBlock *Pred : predecessors(&BB)) Entered |= CleanBBs.contains(Pred); @@ -188,6 +227,7 @@ public: return PreservedAnalyses::all(); SmallVector Descriptions; + auto &FAM = MAM.getResult(M).getManager(); for (Function &F : M) { if (F.isIntrinsic() || F.isDeclaration() || F.hasAvailableExternallyLinkage()) @@ -202,7 +242,10 @@ public: Flags |= FMODRET_FN_WEAK; if (Ret->isIntegerTy(64)) Flags |= FMODRET_FN_RET64; - cleanReturnValues(F, Values); + auto &DT = FAM.getResult(F); + CleanInstructions Clean(DT); + + cleanReturnValues(F, 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)}); -- 2.43.0