From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wm1-f41.google.com (mail-wm1-f41.google.com [209.85.128.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1491E32E75E for ; Thu, 6 Nov 2025 12:53:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433620; cv=none; b=nNQfknR3QoF50k3mtuO7Kt7ahb7DlzJX2mxQo/ZIRnAdTRIOw5PECtLRfoaKOhQHut7zofy9MFNi/pvaLpkJzD/GmGJJbcBkFhS7Gq2mu+CXHYE0FYUwLFGd+mlPevtLFVGri7M617vjJzmG7du0se98wg9ltuLUXzx30Ng+MNg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433620; c=relaxed/simple; bh=qoilpeljCvPQlUDiBXIQ3DZYfxhU4fylmaiXskS/+os=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NBd+tjpqLFTCsWdqwoHRL2Nw/QQxLwgrMcdutxmv5PCdL0vk3ndDV6dxh/YvrIhwJGTy9sElqbYw6BL0MBh1QNMduMMEB8SQ14umDeNz+2rcGhPxApvydwL40/r+qaQnK+dzXrCO+1z/+zrha9krw6pALCeEOop71KQnb1TxM+o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FuKRJt7A; arc=none smtp.client-ip=209.85.128.41 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.b="FuKRJt7A" Received: by mail-wm1-f41.google.com with SMTP id 5b1f17b1804b1-4710665e7deso4068855e9.1 for ; Thu, 06 Nov 2025 04:53:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433616; x=1763038416; 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; bh=XpM6NCHkSKQ5mxm/ZfH0Q/wE6qOhVyZUTPoMaF9H0do=; b=FuKRJt7AoYPcewJgxCxaz3rxYV98Q6NfO1Yxix2uQ3mF2uJ9FZT+wZuJ7t0wonYmRR GL0C1NK2xLwjX5GRuiVCyrfy57NkSRJt3AjbOLa3VCB4BD0SwlDSmaIXA7g65GY4z+tD DrRUBg34VTYT/m7TLJ0Nx9eVDrccfZoZnyFjqhmMtcm3agHyXWEKZ4Ii/1YcMt4upgTS Sz4rheXuGuS4CdRqQeys+/Lsrfa1fBE73iVfR0hHeNn2y0cJ2NSh15A+VtI7z0zt1jAb UrNl20WfPTpPQmkjuUDzDA3R0cI0d61t0yXvIg/DWgH1f6errUX1oJbSlOMuFn5eMJVg S2sw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433616; x=1763038416; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XpM6NCHkSKQ5mxm/ZfH0Q/wE6qOhVyZUTPoMaF9H0do=; b=CGEu2kedsc9n/oxHtbtTcNUyoist8REO8Fybay40K85/H63DPhXgl/ad+Pl25xFRjw +ssFXir+KTSuSYrBF8O/AQ4NWA9QaQJbnarUIjj+x2bxILlK3/0+EuBsq1j/z1vpW264 xhxEYplzKUYfejkR3qxq7YPFgLLt96W8EP+LwULyR40bPvUXLWRmln7COr3MOAv887si mTiVnDCnwzGQ2SbFEvLEL5P+dJgpXIowbaBhK0jn6SPyrNQrjrgRlLxKWrp8NAHAIKKg l3XTtwX596YXrloDgjNaq57dgJGSjbRYsKpOn+kNZsVD5T5+Ok9Y63vm9klViR2e3qFR 9KMw== X-Forwarded-Encrypted: i=1; AJvYcCW2gn1iAbQdQe5L2S/wCJFnpvRLUC+SpQ1qLWuGvrfEFAjSxPoRfkHn89jYPlHW6Rd/vpgCjmTQyltDE5I=@vger.kernel.org X-Gm-Message-State: AOJu0Yw6SxpXwRBYbsofhkOpdXupjyX5Y+gHYQd+AfjASS3A6xxBBF+o ZYBXfBb9Js+H6QXCBY1bdIb4mLW98X+J9Rm/RKykjSDxgcIxvSTf8XA= X-Gm-Gg: ASbGncvdy+2Lhig4cFey2biXrLUxo5jfDRJznsBpTyYSbw3p+9HFzZaUX0c2TXbLcHv faAODfzpT/KaLM0V3NZAvwRK6XltSWlY5Gsp+V5NBIqM7uaYwIIHzNIMQQAfCV8WM+pMMQovIqL qUOXc0Tzp/K8prWXlMArD4HdTQax736ilG/ZAc9l87NHBvogzYXwFnOUaBCVMwzTKmz9LhgURlX fRVm8h8+Nf2hTeGMcJGgkxKaxT/4zk5JaLv4+mp7NnecxxgAEqy+6DjHjUYTRsZtDH4+g99ZVpo aQgVIP3ex20sVeiJtVrqOcs8DytZvHZnDJi01JWTemfP0wE/eKiJjgKp2W+Bg7cm7jOmN3qW5OK VNIcyMUxXFPRR0oZLXL7xo2t6u0iQrWlb32iC4uwIpXVkNmkWGFEaAvUd75YzVJ7r3KiPriXuJJ qAV9d655bRNY8xCNlkqdRVdxwVLEH1Jh1wlvDCWdBUVdwRZD0YW7BlDxk= X-Google-Smtp-Source: AGHT+IF2FVn/XgqY2wbn9JXv1izwwwhwPAjKfxLHTcwdcdDDPlOnsWlzWAekdlibP7ZK/jVHh5i06g== X-Received: by 2002:a05:600c:6219:b0:477:67ca:cdbb with SMTP id 5b1f17b1804b1-47767cad1a3mr16460225e9.36.1762433616190; Thu, 06 Nov 2025 04:53:36 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:35 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 01/17] bpf: Add BCF expr and proof rule definitions Date: Thu, 6 Nov 2025 13:52:39 +0100 Message-Id: <20251106125255.1969938-2-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds include/uapi/linux/bcf.h with: - Expression node format: struct bcf_expr { u8 code; u8 vlen; u16 params; u32 args[]; } * code encodes (operation(5 bits)| type (3 bits)). The op first reuses all the bpf encoding, e.g., BPF_ADD/BPF_SUB, and then, extends it with bitvector specific ops, e.g., extract. * args[] holds u32 indices into the same arena (DAG by backwards indices); BV VAL is the only op whose args carry immediates. * params packs op-specific fields; helpers are provided to access bit widths, extract ranges, extension sizes, boolean literal bits. - Proof buffer layout and rule ids: * struct bcf_proof_header { magic=3DBCF_MAGIC, expr_cnt, step_cnt }. * struct bcf_proof_step { u16 rule; u8 premise_cnt; u8 param_cnt; u32 arg= s[] }; args[] is (premise ids followed by parameters). * Rule classes are ORed into rule (BCF_RULE_CORE/BOOL/BV). This patch is UAPI-only; no kernel behavior change. Signed-off-by: Hao Sun --- include/uapi/linux/bcf.h | 197 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 include/uapi/linux/bcf.h diff --git a/include/uapi/linux/bcf.h b/include/uapi/linux/bcf.h new file mode 100644 index 000000000000..459ad3ed5c6f --- /dev/null +++ b/include/uapi/linux/bcf.h @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__LINUX_BCF_H__ +#define _UAPI__LINUX_BCF_H__ + +#include +#include + +/* Expression Types */ +#define BCF_TYPE(code) ((code) & 0x07) +#define BCF_BV 0x00 /* Bitvector */ +#define BCF_BOOL 0x01 /* Boolean */ +#define BCF_LIST 0x02 /* List of vals */ +#define __MAX_BCF_TYPE 0x03 + +#define BCF_OP(code) ((code) & 0xf8) +/* Common Operations */ +#define BCF_VAL 0x08 /* Value/Constant */ +#define BCF_VAR 0x18 /* Variable */ +#define BCF_ITE 0x28 /* If-Then-Else */ + +/* Bitvector Operations */ +#define BCF_SDIV 0xb0 +#define BCF_SMOD 0xd0 +#define BCF_EXTRACT 0x38 /* Bitvector extraction */ +#define BCF_SIGN_EXTEND 0x48 /* Sign extension */ +#define BCF_ZERO_EXTEND 0x58 /* Zero extension */ +#define BCF_BVSIZE 0x68 /* Bitvector size */ +#define BCF_BVNOT 0x78 /* Bitvector not */ +#define BCF_FROM_BOOL 0x88 /* Bool list to Bitvector */ +#define BCF_CONCAT 0x98 /* Concatenation */ +#define BCF_REPEAT 0xa8 /* Bitvector repeat */ + +/* Boolean Operations */ +#define BCF_CONJ 0x00 /* Conjunction (AND) */ +#define BCF_DISJ 0x40 /* Disjunction (OR) */ +#define BCF_NOT 0x80 /* Negation */ +#define BCF_IMPLIES 0x90 /* Implication */ +#define BCF_XOR 0x38 /* Exclusive OR */ +#define BCF_BITOF 0x48 /* Bitvector to Boolean */ + +/* Boolean Literals/Vals */ +#define BCF_FALSE 0x00 +#define BCF_TRUE 0x01 + +/** + * struct bcf_expr - BCF expression structure + * @code: Operation code (operation | type) + * @vlen: Argument count + * @params: Operation parameters + * @args: Argument indices + * + * Parameter encoding by type: + * - Bitvector: [7:0] bit width, except: + * - [15:8] and [7:0] extract `start` and `end` for EXTRACT; + * - [15:8] repeat count for REPEAT; + * - [15:8] extension size for SIGN/ZERO_EXTEND + * - Boolean: + * - [0] literal value for constants; + * - [7:0] bit index for BITOF. + * - List: element type encoding: + * - [7:0] for types; + * - [15:8] for type parameters, e.g., bit width. + */ +struct bcf_expr { + __u8 code; + __u8 vlen; + __u16 params; + __u32 args[]; +}; + +#define BCF_PARAM_LOW(p) ((p) & 0xff) +#define BCF_PARAM_HIGH(p) (((p) >> 8) & 0xff) + +/* Operation-specific parameter meanings */ +#define BCF_BV_WIDTH(p) BCF_PARAM_LOW(p) +#define BCF_EXT_LEN(p) BCF_PARAM_HIGH(p) +#define BCF_EXTRACT_START(p) BCF_PARAM_HIGH(p) +#define BCF_EXTRACT_END(p) BCF_PARAM_LOW(p) +#define BCF_REPEAT_N(p) BCF_PARAM_HIGH(p) +#define BCF_BOOL_LITERAL(p) ((p) & 1) +#define BCF_BITOF_BIT(p) BCF_PARAM_LOW(p) +#define BCF_LIST_TYPE(p) BCF_PARAM_LOW(p) +#define BCF_LIST_TYPE_PARAM(p) BCF_PARAM_HIGH(p) + +/* BCF proof definitions */ +#define BCF_MAGIC 0x0BCF + +struct bcf_proof_header { + __u32 magic; + __u32 expr_cnt; + __u32 step_cnt; +}; + +/** + * struct bcf_proof_step - Proof step + * @rule: Rule identifier (class | rule) + * @premise_cnt: Number of premises + * @param_cnt: Number of parameters + * @args: Arguments (premises followed by parameters) + */ +struct bcf_proof_step { + __u16 rule; + __u8 premise_cnt; + __u8 param_cnt; + __u32 args[]; +}; + +/* Rule Class */ +#define BCF_RULE_CLASS(r) ((r) & 0xe000) +#define BCF_RULE_CORE 0x0000 +#define BCF_RULE_BOOL 0x2000 +#define BCF_RULE_BV 0x4000 + +#define BCF_STEP_RULE(r) ((r) & 0x1fff) + +/* Core proof rules */ +#define BCF_CORE_RULES(FN) \ + FN(ASSUME) \ + FN(EVALUATE) \ + FN(DISTINCT_VALUES) \ + FN(ACI_NORM) \ + FN(ABSORB) \ + FN(REWRITE) \ + FN(REFL) \ + FN(SYMM) \ + FN(TRANS) \ + FN(CONG) \ + FN(TRUE_INTRO) \ + FN(TRUE_ELIM) \ + FN(FALSE_INTRO) \ + FN(FALSE_ELIM) + +#define BCF_RULE_NAME(x) BCF_RULE_##x +#define BCF_RULE_ENUM_VARIANT(x) BCF_RULE_NAME(x), + +enum bcf_core_rule { + BCF_RULE_CORE_UNSPEC =3D 0, + BCF_CORE_RULES(BCF_RULE_ENUM_VARIANT) + __MAX_BCF_CORE_RULES, +}; + +/* Boolean proof rules */ +#define BCF_BOOL_RULES(FN) \ + FN(RESOLUTION) \ + FN(FACTORING) \ + FN(REORDERING) \ + FN(SPLIT) \ + FN(EQ_RESOLVE) \ + FN(MODUS_PONENS) \ + FN(NOT_NOT_ELIM) \ + FN(CONTRA) \ + FN(AND_ELIM) \ + FN(AND_INTRO) \ + FN(NOT_OR_ELIM) \ + FN(IMPLIES_ELIM) \ + FN(NOT_IMPLIES_ELIM) \ + FN(EQUIV_ELIM) \ + FN(NOT_EQUIV_ELIM) \ + FN(XOR_ELIM) \ + FN(NOT_XOR_ELIM) \ + FN(ITE_ELIM) \ + FN(NOT_ITE_ELIM) \ + FN(NOT_AND) \ + FN(CNF_AND_POS) \ + FN(CNF_AND_NEG) \ + FN(CNF_OR_POS) \ + FN(CNF_OR_NEG) \ + FN(CNF_IMPLIES_POS) \ + FN(CNF_IMPLIES_NEG) \ + FN(CNF_EQUIV_POS) \ + FN(CNF_EQUIV_NEG) \ + FN(CNF_XOR_POS) \ + FN(CNF_XOR_NEG) \ + FN(CNF_ITE_POS) \ + FN(CNF_ITE_NEG) \ + FN(ITE_EQ) + +enum bcf_bool_rule { + BCF_RULE_BOOL_UNSPEC =3D 0, + BCF_BOOL_RULES(BCF_RULE_ENUM_VARIANT) + __MAX_BCF_BOOL_RULES, +}; + +/* Bitvector proof rules */ +#define BCF_BV_RULES(FN) \ + FN(BITBLAST) \ + FN(POLY_NORM) \ + FN(POLY_NORM_EQ) + +enum bcf_bv_rule { + BCF_RULE_BV_UNSPEC =3D 0, + BCF_BV_RULES(BCF_RULE_ENUM_VARIANT) + __MAX_BCF_BV_RULES, +}; +#undef BCF_RULE_ENUM_VARIANT + +#endif /* _UAPI__LINUX_BCF_H__ */ -- 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D798232ED2E for ; Thu, 6 Nov 2025 12:53:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433622; cv=none; b=evybhEORK240MEDYBq6flRoG2H9je4aa+3+mniwuQG9xJkeVGqHkkm75WBqqq9+xB2Hcojs1W2RM6Ysyy+ec3h7JUJuYvFFc841apwG+VMekXgorKJpYnn8ZYOGwJF7o11yTvN36JzgF5OEgkByG//KtjqF6xiaFrN29m7vCdvc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433622; c=relaxed/simple; bh=Wn62Zez1jdd7w4AVVJUjEc4EUS5oDZoivioEIfat5uw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PQV3Hf7cCpMQsXWfJQL8arKuCvxUnl6N9dEGt0tFacp1UzUpz/MUdv+GQqb5CkICuTlmQGdNgObZxPVcaxjg2D4k4HE1ZfMYsav+KvGiJl1sZ7T+AUN5hydntCKjlAb8eKKZbEMcsdff73k1PeJFiis9Hf5LtMiy+RierkBCZxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gd4FRocV; arc=none smtp.client-ip=209.85.221.45 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.b="gd4FRocV" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-429b72691b4so769451f8f.3 for ; Thu, 06 Nov 2025 04:53:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433617; x=1763038417; 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; bh=QjtDkhHcJRxmLbo+3NnqG1n70KbrxmZUo5oVszYsvH4=; b=gd4FRocVNvDTcz6qPQrSEdwDKmDEKTtgqDpA+Oz5v9aDOcXWuKMrZc0hOh8njm7awe 4QqTf/aIigxwakvL9XUMq6/vaejFmSb91PhJ7jFv5jVjnRSByufGXp7/9xN/4EVo9+o5 Scb9o2U+TDbg8MS6GZXr1JpFigx2FaIW1FEtcsZgjmGFPdHI21UwoV23WnBezwWUuthb nntfdAKJ7z2sibNz3HWBhayWscM1sg8dlinIkoYdI1P+Z3WTJRmxFSQnkKxZVGpz3cqN jY1R5vu5YLI613ZUxyNlrTqWwcAGRmTz+jPXp/B5wWlI9EE0Ocsts5mamTLEUBVh9qql jdiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433617; x=1763038417; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=QjtDkhHcJRxmLbo+3NnqG1n70KbrxmZUo5oVszYsvH4=; b=hv1VhxVccSF34NbYjPK7scl5xbb9ciTfUXDGZcTdq7YefHcY1KKROEaUMkEERmhTqE 0/iNZvoEOwAtXlQQH7WWhnsQG8hppsHGUJvBqxSRt2Gy+KdLz/DiVQJqds/ULg3HYtsg 5nY9QFN7V58SO/E4mXjRbS1L27RgXZN7GwvR6MvrMH70P8R+XFl6y16HcxZnT5Pa2qUp 0hKf7TpJ/3/HEqEFccOIf1dKr5GUtOr+7R11hdq0DQVkOrUtQTDmzdSFbIvbGCR3bT6+ Of7gmc/qr6MaqGH5FCz3lJjyUcIsh94CjDW5NZc/kk/vrmJwW2h7pLH8ML7LjrK78FJb BR1Q== X-Forwarded-Encrypted: i=1; AJvYcCVuHreE5cuuIGjNw2+/70yeh83fdgLLLt5Sl7aCB8RzZFiD9cz8JW1waf0u8qMrN2IcFhzCNP/YlUKiDyo=@vger.kernel.org X-Gm-Message-State: AOJu0YxSflu6mKl/FqCGp27ZZjS0/PmpiwhuirIuiy8cW6h+2/ZHq+AC siP6+iy8Ss0NYgAolnbzY9GderAPJx/nKaI4LCHturfcEVWN4gwYsSQ= X-Gm-Gg: ASbGncuz0aOgOyg3fvcCtzD1jwC9kUcgagPN8UiQVkx39LvuwMTbj1mKg7fllZuxY1P QL6adoZzsqVNo5E+M8XRki4WR4Iq67AZGCyYjDAZSTnHHkvZ58vs4DJNOXInOHC+pniI5njuVB/ y3iSPdflCwbf336weUbAcuHTZOMwkHVt1FKV2Rq+9R1RzurHrCO+3WcTgT+dBOhxpQiWQQY/qAj QThlfa4UHjYe80PJNys7NokYqdQKQKjs2JWTJRG6JfyVqdDnq6VZ4um26yL8ZI6zjCoZwIh8Rj0 Kxc8ThqAgtuCcO1EJNYOwQGyKBqiuR7yzqQVOuqntrpcCfpRNQ696Y7IV7Dt/iVuF9j2sUShpoQ 0hXBycQv8to+3Xe0ALiGZ0/fRhylaePl33QJH+p7GJYoYtX9+Tgxv2AqtAHEmWVgikeGjKRK3Yy AimgK6gNkl2hU7RN0NIJPu/dfbtSJGVAJfDDk41ruNR4jsILDI21znRWKmylxOPPe3yw== X-Google-Smtp-Source: AGHT+IGG+QFoVtb620WWFyrVxoAV2LS1/PGKzF2MVcldpxTRUrKcWwcJB+ZdIU69/0EzvgaWMLX4bQ== X-Received: by 2002:a05:6000:4308:b0:429:be56:e508 with SMTP id ffacd0b85a97d-429e3311e53mr6100201f8f.58.1762433616900; Thu, 06 Nov 2025 04:53:36 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:36 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 02/17] bpf: Add bcf_checker top-level workflow Date: Thu, 6 Nov 2025 13:52:40 +0100 Message-Id: <20251106125255.1969938-3-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce the in-kernel BCF proof checker skeleton and public entry point. The checker validates userspace proofs of refinement conditions. Proof size hard limit: MAX_BCF_PROOF_SIZE 8M. kernel/bpf/bcf_checker.c implements: - Buffer/header validation (check_hdr): enforces BCF_MAGIC, size alignment, and overflow-safe size arithmetic for expr/step regions. - Expression arena load (check_exprs): builds a bitmap of validated indices, enforces that each non-VAL argument points to an earlier index, stubs type_check() for future semantic checks, and records the arena size as the starting point for dynamic ids (id_gen). - Step load/validation (check_steps): copies steps, verifies per-step size, rule class/id bounds via rule_class_max(), that all premises refer to strictly earlier steps, records the single ASSUME step (check_assume stub= ), computes last_ref for each step for lifetime mgmt, and requires every non-final step to be referenced later. - Rule application loop (apply_rules): iterates steps in order, dispatches = by rule class to apply_core_rule/apply_bool_rule/apply_bv_rule (stubs now), - Memory management: bcf_checker_state owns copied arenas and per-step stat= e; dynamically created exprs (facts) are ref-counted, and tracked in an xarr= ay indexed by new ids >=3D expr_size, to be added in the next patch set. Rule semantics, type checking, and integration with the verifier appear in later patches; this change only defines the top-level checker structure. Signed-off-by: Hao Sun --- .clang-format | 2 + include/linux/bcf_checker.h | 18 ++ kernel/bpf/Makefile | 2 +- kernel/bpf/bcf_checker.c | 440 ++++++++++++++++++++++++++++++++++++ 4 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 include/linux/bcf_checker.h create mode 100644 kernel/bpf/bcf_checker.c diff --git a/.clang-format b/.clang-format index f371a13b4d19..7f2f85bc4c1f 100644 --- a/.clang-format +++ b/.clang-format @@ -747,6 +747,8 @@ ForEachMacros: - 'ynl_attr_for_each_nested' - 'ynl_attr_for_each_payload' - 'zorro_for_each_dev' + - 'bcf_for_each_arg' + - 'bcf_for_each_pm_step' =20 IncludeBlocks: Preserve IncludeCategories: diff --git a/include/linux/bcf_checker.h b/include/linux/bcf_checker.h new file mode 100644 index 000000000000..220552653c80 --- /dev/null +++ b/include/linux/bcf_checker.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LINUX_BCF_CHECKER_H__ +#define __LINUX_BCF_CHECKER_H__ + +#include +#include +#include +#include /* For log level. */ + +#define MAX_BCF_PROOF_SIZE (8 * 1024 * 1024) + +typedef void (*bcf_logger_cb)(void *private, const char *fmt, va_list args= ); + +int bcf_check_proof(struct bcf_expr *goal_exprs, u32 goal, bpfptr_t proof, + u32 proof_size, bcf_logger_cb logger, u32 level, + void *private); + +#endif /* __LINUX_BCF_CHECKER_H__ */ diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 7fd0badfacb1..5084a0a918e4 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -6,7 +6,7 @@ cflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) :=3D -fno-gc= se endif CFLAGS_core.o +=3D -Wno-override-init $(cflags-nogcse-yy) =20 -obj-$(CONFIG_BPF_SYSCALL) +=3D syscall.o verifier.o inode.o helpers.o tnum= .o log.o token.o liveness.o +obj-$(CONFIG_BPF_SYSCALL) +=3D syscall.o verifier.o inode.o helpers.o tnum= .o log.o token.o liveness.o bcf_checker.o obj-$(CONFIG_BPF_SYSCALL) +=3D bpf_iter.o map_iter.o task_iter.o prog_iter= .o link_iter.o obj-$(CONFIG_BPF_SYSCALL) +=3D hashtab.o arraymap.o percpu_freelist.o bpf_= lru_list.o lpm_trie.o map_in_map.o bloom_filter.o obj-$(CONFIG_BPF_SYSCALL) +=3D local_storage.o queue_stack_maps.o ringbuf.o diff --git a/kernel/bpf/bcf_checker.c b/kernel/bpf/bcf_checker.c new file mode 100644 index 000000000000..33b6e5f04e7f --- /dev/null +++ b/kernel/bpf/bcf_checker.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Per proof step state. */ +struct bcf_step_state { + /* The conclusion of the current step. */ + struct bcf_expr *fact; + u32 fact_id; + /* + * The last step referring to the current step. After `last_ref`, the + * `fact` is no longer used by any other steps and can be freed. + */ + u32 last_ref; +}; + +/* + * The expr buffer `exprs` below as well as `steps` rely on the fact that = the + * size of each arg is the same as the size of the struct bcf_expr, and no + * padings in between and after. + */ +static_assert(struct_size_t(struct bcf_expr, args, 1) =3D=3D + sizeof_field(struct bcf_expr, args[0]) * 2); +static_assert(struct_size_t(struct bcf_proof_step, args, 1) =3D=3D + sizeof_field(struct bcf_proof_step, args[0]) * 2); + +/* Size of expr/step in u32 plus the node itself */ +#define EXPR_SZ(expr) ((expr)->vlen + 1) +#define STEP_SZ(step) ((step)->premise_cnt + (step)->param_cnt + 1) + +#define bcf_for_each_arg(arg_id, expr) \ + for (u32 ___i =3D 0; \ + ___i < (expr)->vlen && (arg_id =3D (expr)->args[___i], true); \ + ___i++) + +#define bcf_for_each_pm_step(step_id, step) \ + for (u32 ___i =3D 0; ___i < (step)->premise_cnt && \ + (step_id =3D (step)->args[___i], true); \ + ___i++) + +struct bcf_checker_state { + /* + * Exprs from the proof, referred to as `static expr`. They exist + * during the entire checking phase. + */ + struct bcf_expr *exprs; + /* + * Each expr of `exprs` is followed by their arguments. The `valid_idx` + * bitmap records the valid indices of exprs. + */ + unsigned long *valid_idx; + /* Size of exprs array. */ + u32 expr_size; + /* + * For exprs that are allocated dynamically during proof checking, e.g., + * conclusions from proof steps, they are refcounted, and each allocated + * expr has an id (increased after `expr_size`) and is stored in xarray. + * + * With this xarray, any routines below can exit early on any error + * without worrying about freeing the exprs allocated; they can be + * freed once when freeing the xarray, see free_checker_state(). + */ + u32 id_gen; + struct xarray expr_id_map; /* Id (u32) to `struct bcf_expr_ref` */ + + /* Step state tracking */ + struct bcf_proof_step *steps; + struct bcf_step_state *step_state; + u32 step_size; /* size of steps array */ + u32 step_cnt; /* valid number of steps */ + u32 cur_step; + u32 cur_step_idx; + + bcf_logger_cb logger; + void *logger_private; + u32 level; + + u32 goal; + struct bcf_expr *goal_exprs; +}; + +static void free_checker_state(struct bcf_checker_state *st) +{ + unsigned long id; + void *expr; + + kvfree(st->exprs); + kvfree(st->valid_idx); + xa_for_each(&st->expr_id_map, id, expr) { + kfree(expr); + } + xa_destroy(&st->expr_id_map); + kvfree(st->steps); + kvfree(st->step_state); + + kfree(st); +} +DEFINE_FREE(free_checker, struct bcf_checker_state *, + if (_T) free_checker_state(_T)) + +__printf(2, 3) static void verbose(struct bcf_checker_state *st, + const char *fmt, ...) +{ + va_list args; + + if (!st->logger || !st->level) + return; + va_start(args, fmt); + st->logger(st->logger_private, fmt, args); + va_end(args); +} + +/* + * Every expr has an id: (1) for static exprs, the idx to `exprs` is its i= d; + * (2) dynamically-allocated ones get one from st->id_gen++. + */ +static bool is_static_expr_id(struct bcf_checker_state *st, u32 id) +{ + return id < st->expr_size; +} + +/* + * Each arg of a bcf_expr must be an id, except for bv_val, which is a + * sequence of u32 values. + */ +static bool expr_arg_is_id(u8 code) +{ + return code !=3D (BCF_BV | BCF_VAL); +} + +static bool is_false(const struct bcf_expr *expr) +{ + return expr->code =3D=3D (BCF_BOOL | BCF_VAL) && + BCF_BOOL_LITERAL(expr->params) =3D=3D BCF_FALSE; +} + +/* + * Exprs referred to by the proof steps are static exprs from the proof. + * Hence, must be valid id in st->exprs. + */ +static bool valid_arg_id(struct bcf_checker_state *st, u32 id) +{ + return is_static_expr_id(st, id) && test_bit(id, st->valid_idx); +} + +/* + * Rather than using: + * if (!correct_condition0 or !correct_condition1) + * return err; + * the `ENSURE` macro make this more readable: + * ENSURE(c0 && c1); + */ +#define ENSURE(cond) \ + do { \ + if (!(cond)) \ + return -EINVAL; \ + } while (0) + +static int type_check(struct bcf_checker_state *st, struct bcf_expr *expr) +{ + return -EOPNOTSUPP; +} + +static int check_exprs(struct bcf_checker_state *st, bpfptr_t bcf_buf, + u32 expr_size) +{ + u32 idx =3D 0; + int err; + + st->exprs =3D + kvmemdup_bpfptr(bcf_buf, expr_size * sizeof(struct bcf_expr)); + if (IS_ERR(st->exprs)) { + err =3D PTR_ERR(st->exprs); + st->exprs =3D NULL; + return err; + } + st->expr_size =3D expr_size; + st->id_gen =3D expr_size; + + st->valid_idx =3D kvzalloc(bitmap_size(expr_size), GFP_KERNEL); + if (!st->valid_idx) { + kvfree(st->exprs); + st->exprs =3D NULL; + return -ENOMEM; + } + + while (idx < expr_size) { + struct bcf_expr *expr =3D st->exprs + idx; + u32 expr_sz =3D EXPR_SZ(expr), arg_id; + + ENSURE(idx + expr_sz <=3D expr_size); + + bcf_for_each_arg(arg_id, expr) { + if (!expr_arg_is_id(expr->code)) + break; + /* + * The bitmap enforces that each expr can refer only to + * valid, previous exprs. + */ + ENSURE(valid_arg_id(st, arg_id)); + } + + err =3D type_check(st, expr); + if (err) + return err; + + set_bit(idx, st->valid_idx); + idx +=3D expr_sz; + } + ENSURE(idx =3D=3D expr_size); + + return 0; +} + +static int check_assume(struct bcf_checker_state *st, + struct bcf_proof_step *step) +{ + return -EOPNOTSUPP; +} + +static bool is_assume(u16 rule) +{ + return rule =3D=3D (BCF_RULE_CORE | BCF_RULE_ASSUME); +} + +static u16 rule_class_max(u16 rule) +{ + switch (BCF_RULE_CLASS(rule)) { + case BCF_RULE_CORE: + return __MAX_BCF_CORE_RULES; + case BCF_RULE_BOOL: + return __MAX_BCF_BOOL_RULES; + case BCF_RULE_BV: + return __MAX_BCF_BV_RULES; + default: + return 0; + } +} + +static int check_steps(struct bcf_checker_state *st, bpfptr_t bcf_buf, + u32 step_size) +{ + u32 pos =3D 0, cur_step =3D 0, rule, step_id; + struct bcf_proof_step *step; + bool goal_found =3D false; + int err; + + st->steps =3D kvmemdup_bpfptr(bcf_buf, + step_size * sizeof(struct bcf_proof_step)); + if (IS_ERR(st->steps)) { + err =3D PTR_ERR(st->steps); + st->steps =3D NULL; + return err; + } + st->step_size =3D step_size; + + /* + * First pass: validate each step and count how many there are. While + * iterating we also ensure that premises only reference *earlier* steps. + */ + while (pos < step_size) { + step =3D st->steps + pos; + rule =3D BCF_STEP_RULE(step->rule); + + ENSURE(pos + STEP_SZ(step) <=3D step_size); + ENSURE(rule && rule < rule_class_max(step->rule)); + + /* Every step must only refer to previous established steps */ + bcf_for_each_pm_step(step_id, step) + ENSURE(step_id < cur_step); + + /* Must introduce a goal that is consistent to the one required */ + if (is_assume(step->rule)) { + ENSURE(!goal_found); /* only one goal intro step */ + goal_found =3D true; + + err =3D check_assume(st, step); + if (err) + return err; + } + + pos +=3D STEP_SZ(step); + cur_step++; + } + + /* No trailing garbage and at least two valid steps. */ + ENSURE(pos =3D=3D step_size && cur_step >=3D 2 && goal_found); + + st->step_cnt =3D cur_step; + st->step_state =3D + kvcalloc(cur_step, sizeof(*st->step_state), GFP_KERNEL); + if (!st->step_state) { + kvfree(st->steps); + st->steps =3D NULL; + return -ENOMEM; + } + + /* Second pass: fill in last reference index for each step. */ + for (pos =3D 0, cur_step =3D 0; pos < step_size; cur_step++) { + step =3D st->steps + pos; + bcf_for_each_pm_step(step_id, step) + st->step_state[step_id].last_ref =3D cur_step; + + pos +=3D STEP_SZ(step); + } + + /* + * Every step (except the last one) must be referenced by at + * least one later step. + */ + for (cur_step =3D 0; cur_step < st->step_cnt - 1; cur_step++) + ENSURE(st->step_state[cur_step].last_ref); + + return 0; +} + +static int apply_core_rule(struct bcf_checker_state *st, + struct bcf_proof_step *step) +{ + return -EOPNOTSUPP; +} + +static int apply_bool_rule(struct bcf_checker_state *st, + struct bcf_proof_step *step) +{ + return -EOPNOTSUPP; +} + +static int apply_bv_rule(struct bcf_checker_state *st, + struct bcf_proof_step *step) +{ + return -EOPNOTSUPP; +} + +static int apply_rules(struct bcf_checker_state *st) +{ + struct bcf_expr *fact; + int err; + + verbose(st, "checking %u proof steps\n", st->step_cnt); + + while (st->cur_step_idx < st->step_size) { + struct bcf_proof_step *step =3D st->steps + st->cur_step_idx; + u16 class =3D BCF_RULE_CLASS(step->rule); + + if (signal_pending(current)) + return -EAGAIN; + if (need_resched()) + cond_resched(); + + if (class =3D=3D BCF_RULE_CORE) + err =3D apply_core_rule(st, step); + else if (class =3D=3D BCF_RULE_BOOL) + err =3D apply_bool_rule(st, step); + else if (class =3D=3D BCF_RULE_BV) + err =3D apply_bv_rule(st, step); + else { + WARN_ONCE(1, "Unknown rule class: %u", class); + err =3D -EFAULT; + } + if (err) + return err; + + st->cur_step_idx +=3D STEP_SZ(step); + st->cur_step++; + } + + /* The last step must refute the goal by concluding `false` */ + fact =3D st->step_state[st->step_cnt - 1].fact; + ENSURE(is_false(fact)); + verbose(st, "proof accepted\n"); + + return 0; +} + +static int check_hdr(struct bcf_proof_header *hdr, bpfptr_t proof, + u32 proof_size) +{ + u32 expr_size, step_size, sz; + bool overflow =3D false; + + ENSURE(proof_size < MAX_BCF_PROOF_SIZE && proof_size > sizeof(*hdr) && + (proof_size % sizeof(u32) =3D=3D 0)); + + if (copy_from_bpfptr(hdr, proof, sizeof(*hdr))) + return -EFAULT; + + ENSURE(hdr->magic =3D=3D BCF_MAGIC && hdr->expr_cnt && hdr->step_cnt > 1); + + overflow |=3D check_mul_overflow(hdr->expr_cnt, sizeof(struct bcf_expr), + &expr_size); + overflow |=3D check_mul_overflow( + hdr->step_cnt, sizeof(struct bcf_proof_step), &step_size); + overflow |=3D check_add_overflow(expr_size, step_size, &sz); + ENSURE(!overflow && (proof_size - sizeof(*hdr)) =3D=3D sz); + + return 0; +} + +int bcf_check_proof(struct bcf_expr *goal_exprs, u32 goal, bpfptr_t proof, + u32 proof_size, bcf_logger_cb logger, u32 level, + void *private) +{ + struct bcf_checker_state *st __free(free_checker) =3D NULL; + struct bcf_proof_header hdr; + int err; + + err =3D check_hdr(&hdr, proof, proof_size); + if (err) + return err; + + st =3D kzalloc(sizeof(*st), GFP_KERNEL_ACCOUNT); + if (!st) + return -ENOMEM; + xa_init(&st->expr_id_map); + st->goal_exprs =3D goal_exprs; + st->goal =3D goal; + st->logger =3D logger; + st->logger_private =3D private; + st->level =3D level; + + bpfptr_add(&proof, sizeof(struct bcf_proof_header)); + err =3D check_exprs(st, proof, hdr.expr_cnt); + + bpfptr_add(&proof, hdr.expr_cnt * sizeof(struct bcf_expr)); + err =3D err ?: check_steps(st, proof, hdr.step_cnt); + err =3D err ?: apply_rules(st); + return err; +} +EXPORT_SYMBOL_GPL(bcf_check_proof); --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85B1332AAC0 for ; Thu, 6 Nov 2025 12:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433621; cv=none; b=Mhkub+1fsp4ARS1QoL1i4cXFTPr1KzWden8K+7h27/cp0iEt0N30mjKtzSH4pTsqLYR+IVXQNA2DpzdK+S1rpAmje7hFh7KcvzUO2+hUM+6Q+2LtxJ2n7OW2cn8zY9V0PoPftN+3DxQjzNhpYHw2jt6XSK+IB5GtY2Tzx4hbP5A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433621; c=relaxed/simple; bh=lnliqdz3U4tbAfip1w9XCBAoh+WGXYM0C9rl3tucgdU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uhCvMFgub1fny5KfTvo71NOcrDatXOKBpUr/GlNsVtT9A3IOYOAbmwqeutJgZdJ8XDrxs6fovKQjtfdNx7DmV/V2lQQL4wneBLyOgPQepkgb9VaPdsiSYqoA5Tx2vnJpEnAAt66fvINJR1XjCXLsXM4FmxmSMfhVqdHBb2KDV7M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Sx+2DDAY; arc=none smtp.client-ip=209.85.221.53 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.b="Sx+2DDAY" Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-426fd62bfeaso379901f8f.2 for ; Thu, 06 Nov 2025 04:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433617; x=1763038417; 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; bh=VtWO+ldHw1Wy/NkP7lvHXeHyDhyCdc2lFNTCoxBTl4g=; b=Sx+2DDAYZK9+8aDYXaajurQ2q4cd+nQv1BQmSg83bwUvPWFt0o2BP9L+j4YZljotHb ivv7kfyA4uiCX1Z/vz7QSxUgvTUICveElw80SGQPpo11nFDsVx96qWb5wx0Y9UbVmmFs fTtseg0F7WuYEeDI23/Wnz1Ya7WExkmlRZIPL1aNP8Ru4Qu7Bh/lBSmLCt461OwGKPmr HLmZ2pnIkAilZRzIfez+lfId9D4b7kGgOSEqAtpW9YczPsbZfpgF/3yHlDpi2JDsriO6 RRYbcreW8VnQ381hSvLNS8i3cqpzbcFfqDwdM87XBJA3otd3YFyFmvEQNE/z/m7ztG8A 4//w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433617; x=1763038417; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=VtWO+ldHw1Wy/NkP7lvHXeHyDhyCdc2lFNTCoxBTl4g=; b=Z6V4tdqNtNEPg7VLGfYA9H5lL3CL1ReLxKKNNMmhvj20JfOxjQlOx0KP1fbuhPvoNe u9HltgNORqX/lRhqVOBKz4KEssOC3lOPaoiqR3NeDsbLQrsU1E/mqPd8VpDoZASA9CGg WDCaSqweyiQe9yp1dNqbdMXrxdVFLaJgVppBsZMLHpPSYwtEXwdrbnHDxfFz2BR9YbRd NdUuYEXATQEZEqlbPPwMjk2b/DH1rrGgMbdFb2Bvy1HriBbcyLpanbEc8oLuageEACPx irYpupBN0sBcw1usft1S7kHRdZdw86BAKqokmd9m6y8wMN0QdnAntbVUlk7qMHea+w+T AVJA== X-Forwarded-Encrypted: i=1; AJvYcCUeTT/e9EGGXQsAjtt+9lJA85I5fjNujhOxI1ou2QtQo1ghM/fbm3Vpgr1YeJFEVnFOiwiOnT41dCAQbK0=@vger.kernel.org X-Gm-Message-State: AOJu0Yyyd30NF0pUItweeBkb2IfYMNEqDeBPiMs1lf7aXXILz129Sb0K uIvq7hKbLrsISlWIwZ7eNckM0cjzAOVL/7C+yj7DrWqkpRpGYSHzjlY= X-Gm-Gg: ASbGncszcvOkZ65euwniuCQpDztvLXApsmC5wBMBMzdA6ySPRg2Je6/YWoQgORbJCZT N4cos+iT+BaULITvrGgYI2aMZCreAKVzQcADyK0swdvy11MdjsASzHDUBwQIKDCkNYr0N395RXe 01pZb8VeYwJhNxTe8ISE2M1nis7d6SQEcmjlj8N5IlZ8eL5o/cVUh5kyQwmGr0IMlpsAlT/Zici tAMx0ITBlqFt/IKroVT7acpEEXbk1VC+sItOiSBysxrqEFrQdU7L4oLNjmL4v22wxsa0UV3DbfC uBr1PBg1Qd/1rxFgQX/Na8eKuSp3WG/VEF3RMJ4iWvGAufwTk+2S/vHVASgwyirihQvxu6y+hHz h99G3nsvRVlYlm3ZNDjFzwJx4MaQr3EDNL4dje5kzHPl3EOxl/ngBskP+/fR+9tzU2cxK3Nf7+A lACEjvvK7NtGoWie/0gCSmCgUIgzr9VgORl0CL77pAW+wgrpOlShv3Y2kJ08iahzjAHw== X-Google-Smtp-Source: AGHT+IGueWTt8hn/ZGltEhVjG/PwRBMDPPm6r+dASr9SWGX5Tn9PcRkBUL7A4DVVGKBJwyW+I5n/Iw== X-Received: by 2002:a05:6000:2885:b0:429:cf86:1247 with SMTP id ffacd0b85a97d-429e331395dmr6073417f8f.57.1762433617479; Thu, 06 Nov 2025 04:53:37 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:37 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 03/17] bpf: Add UAPI fields for BCF proof interaction Date: Thu, 6 Nov 2025 13:52:41 +0100 Message-Id: <20251106125255.1969938-4-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Extend union bpf_attr for BPF_PROG_LOAD to support proof-based verifier refinement. The new fields let the verifier export the refinement condition to userspace, preserve its state behind an anon-fd, and later accept a proof and resume analysis. - bcf_buf, bcf_buf_size, bcf_buf_true_size: shared buffer used first to export expressions+condition to userspace and later to receive a proof. true_size refers to the condition size and proof size accordingly. - bcf_fd: anon-fd that owns the preserved verifier_env. Set by the kernel on request; userspace passes it back when submitting a proof. Tools uapi is also updated. Signed-off-by: Hao Sun --- include/uapi/linux/bpf.h | 21 +++++++++++++++++++++ kernel/bpf/syscall.c | 23 ++++++++++++++++++++++- tools/include/uapi/linux/bpf.h | 21 +++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1d73f165394d..fbe99fbc39ab 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1484,6 +1484,15 @@ enum { BPF_STREAM_STDERR =3D 2, }; =20 +/* bcf_flags used in BPF_PROG_LOAD command to indicate if the verifier + * requests or the user provides proofs. + */ +enum { + BCF_F_PROOF_REQUESTED =3D (1U << 0), + BCF_F_PROOF_PROVIDED =3D (1U << 1), + BCF_F_PROOF_PATH_UNREACHABLE =3D (1U << 2), +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -1624,6 +1633,18 @@ union bpf_attr { * verification. */ __s32 keyring_id; + /* BCF buffer for both the condition to be proved required by + * the verifier and the proof provided from user space. + */ + __aligned_u64 bcf_buf; + __u32 bcf_buf_size; + __u32 bcf_buf_true_size; + /* output: BCF fd for loading proof, set by the verifier when + * proof is requested. + */ + __u32 bcf_fd; + /* input/output: proof requested or provided. */ + __u32 bcf_flags; }; =20 struct { /* anonymous struct used by BPF_OBJ_* commands */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8a129746bd6c..5968b74ed7b2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2854,7 +2854,7 @@ static int bpf_prog_verify_signature(struct bpf_prog = *prog, union bpf_attr *attr } =20 /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD keyring_id +#define BPF_PROG_LOAD_LAST_FIELD bcf_flags =20 static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_s= ize) { @@ -2869,6 +2869,24 @@ static int bpf_prog_load(union bpf_attr *attr, bpfpt= r_t uattr, u32 uattr_size) if (CHECK_ATTR(BPF_PROG_LOAD)) return -EINVAL; =20 + if (!!attr->bcf_buf !=3D !!attr->bcf_buf_size || + (attr->bcf_flags & ~(BCF_F_PROOF_PROVIDED | + BCF_F_PROOF_PATH_UNREACHABLE))) + return -EINVAL; + + /* Check proof and resume the last analysis. */ + if (attr->bcf_flags & BCF_F_PROOF_PROVIDED) { + if (attr->bcf_buf_true_size > attr->bcf_buf_size || + !attr->bcf_buf_size) + return -EINVAL; + /* The resumed analysis must only uses the old, first attr. */ + memset(attr, 0, offsetof(union bpf_attr, bcf_buf)); + return -ENOTSUPP; + } + + if (attr->bcf_fd || attr->bcf_buf_true_size || attr->bcf_flags) + return -EINVAL; + if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT | BPF_F_ANY_ALIGNMENT | BPF_F_TEST_STATE_FREQ | @@ -2901,6 +2919,9 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr= _t uattr, u32 uattr_size) bpf_cap =3D bpf_token_capable(token, CAP_BPF); err =3D -EPERM; =20 + if (attr->bcf_buf && !bpf_cap) + goto put_token; + if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && (attr->prog_flags & BPF_F_ANY_ALIGNMENT) && !bpf_cap) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 1d73f165394d..fbe99fbc39ab 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -1484,6 +1484,15 @@ enum { BPF_STREAM_STDERR =3D 2, }; =20 +/* bcf_flags used in BPF_PROG_LOAD command to indicate if the verifier + * requests or the user provides proofs. + */ +enum { + BCF_F_PROOF_REQUESTED =3D (1U << 0), + BCF_F_PROOF_PROVIDED =3D (1U << 1), + BCF_F_PROOF_PATH_UNREACHABLE =3D (1U << 2), +}; + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -1624,6 +1633,18 @@ union bpf_attr { * verification. */ __s32 keyring_id; + /* BCF buffer for both the condition to be proved required by + * the verifier and the proof provided from user space. + */ + __aligned_u64 bcf_buf; + __u32 bcf_buf_size; + __u32 bcf_buf_true_size; + /* output: BCF fd for loading proof, set by the verifier when + * proof is requested. + */ + __u32 bcf_fd; + /* input/output: proof requested or provided. */ + __u32 bcf_flags; }; =20 struct { /* anonymous struct used by BPF_OBJ_* commands */ --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F28C332E739 for ; Thu, 6 Nov 2025 12:53:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433622; cv=none; b=Q70IbWLgKo/PX61n3qsJFkFQC3EYGoRoXBNR/2vNqwCcGVvZdywsrehstu5LeHzirScpnaZdCVuQ3HqpWdKZ5AGoLSNXC0p+nM/0Yj/nyWMh1StjSa1Ll2rX/NicTvK3AHLrN6wEuG1sETWw6hS0rx65nrmydkbDCU/nGlfb+uI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433622; c=relaxed/simple; bh=EnSIiRq6VHyzg36EE4lisdtnJkdNAd019Xn1/RbfzCI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=MXoFC2OsHFZG7D6Qj+M18/oO/GQdHQv7SKaQvfKlozNLcpchONK4aJh6pn8IkwuzZuM5C3fOH4c2Bj11xfKxmpXDat9n7RXPC8BLG7JTLbG4d9tpEuT4YNTM7TFYfyVS/EXG2xfFjvBEoR+x12vEXnfUMkIEiJZsxedUdXQxHB4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=YaojCmQB; arc=none smtp.client-ip=209.85.221.42 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.b="YaojCmQB" Received: by mail-wr1-f42.google.com with SMTP id ffacd0b85a97d-429eb7fafc7so651274f8f.2 for ; Thu, 06 Nov 2025 04:53:39 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433618; x=1763038418; 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; bh=J5UtbFN3SeUqWauxQUFhPcEKoaIiq/3X5SZjNQXVMzY=; b=YaojCmQB0Fg3OgZso8vv+nvyabzPAmSaWeZXdFQoQpHXACpMIxuVDBFMyyDfYFN8in fVTRbo6/Acqj30Qio+HFABgeffUKSQzl5qvVdpRq8h4o7a3guo3QAw6RxWMPuY84wAuK RWQaAHBdnDSkdsvs9N7ejWKx2coqRHezQkK3w4+2KESWJ7nOiZcAADXORDo8lqFCeQxa DhQg2n3H3ERtmz6niWqEKNmCGUUHfFc3yUk63v3hfDbVOne8cvjRWV3KEELgRJ2Min+h 4ZDJU7sAsUJnc6VIXw8R4IKVdcFUf7c0VP47gN3RFpcJwqUkShrm8jcVeGL8PxVPIt1m 1EoA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433618; x=1763038418; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=J5UtbFN3SeUqWauxQUFhPcEKoaIiq/3X5SZjNQXVMzY=; b=mqxpYxZLo6rlbXxMFp5+KTVZEB/drqBawdQku8+syMrNQP7qd46QrEq38WAoEP5VIP S0mY+bexts+oFFzHsMYMudwA8TXLeGx9kCr9zeCZ5Zn1BDdsK0Wv+vAkq7yASUohqh/N UVBuwhg53bU14GeIimXBlCRbaLMZA0wndNZ2prUx5OyqgED2G3UTTXmT3+qVTUyI7Ilc Pt7kfI/Vb2u6Hm0Gd7GuldjIpRtElrNw2f6Kwa5bIX+RPxe6CeToRThWL/UGTj9aHH+P AVd4mJJqkKU82iUpGf8tZjk4+wXE5LQ/2xZZhjhR6384r4pIl9SczmcYHuOUYqhK5/eO 3XeQ== X-Forwarded-Encrypted: i=1; AJvYcCXZgMFObs8vTi6bwncphNbZAXCfxrHbbP7P5KP7P22Cx7SmFLzpHsRaDrz7lV5LNu+2xRc6RYY3N2uICP8=@vger.kernel.org X-Gm-Message-State: AOJu0Yzq0vbEQ46ck3kl5irIABNqlMRYSSLK9vZHi2kXqRIjxxekjVB0 pvgq3u0BWQ2ZcYl//Ux0Avr6jQQdPpPd8zu7RzDCQJjLZXx5R9jX0to= X-Gm-Gg: ASbGncv4StgXl9LqD6CtORsGPOJsPF4kk7Muu07DD9HIAB+D9ZmnUx5GT91jvavJCJS x3DAMYY+OeznZNJCgA0rF7JuqZayrxDmuqe3EhAGvukmUsXogAn3w3D7hjZIKpeUTTKAsp438Uc jpYPwUyifyRG/VT6FY8vxHwGSAjMbuf2CNPr8t7GMSWdtrAM4eTGshG2NaqFApRFxNaQlrg6h7w vMh98HGRjdUPkOK3u7M8X8JZUprURtbr7irJbWAsCVUACjFIZTAYJNKr8xP8BR1qTeGZzsjCJMx 3+RecAA2VhEXcOMPSxbeCEyIP3SAy7/O7oUXa0c4VlinaYaP7rli0jho7YyBOBQm9/wPXH7lon7 dnr3/oiYl606tZMDddNBa4oP+OHyDkYG1Wik/GbMdeYCgQQnOtrTtUmMqv40MVwy0H0/qbB33b+ sTJUIVFykTcvVXSmjIgKtgxisjwbOhZb601unDtyexpQXs9s3Rj/+0i+g= X-Google-Smtp-Source: AGHT+IETLShyKsHT25JdeNK9Sz1/yzYm3e12YwX3zT1yZFekr4gNRNP0A8afPQDiZ+0+kfpa/ZLupA== X-Received: by 2002:adf:9d91:0:b0:429:ec94:67ec with SMTP id ffacd0b85a97d-429ec9469cbmr1719997f8f.1.1762433618072; Thu, 06 Nov 2025 04:53:38 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:37 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 04/17] bpf: Add top-level workflow of bcf_refine() Date: Thu, 6 Nov 2025 13:52:42 +0100 Message-Id: <20251106125255.1969938-5-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the top-level refinement hook `bcf_refine()`: - Add `struct bcf_refine_state` to `struct bpf_verifier_env`: stores the li= st of parent states forming the relevant path suffix (`parents`, `vstate_cnt= `), and traversal cursors (`cur_vstate`, `cur_jmp_entry`). A boolean `availab= le` marks whether BCF can be used in this verification. - Implement `backtrack_states()`: walking parents with `backtrack_insn()` a= nd recorded jump history and find a base state to start the symbolic tracking. - Add a stub `bcf_track()` () and `bcf_refine()` routine that: (a) derives default `reg_masks` when not provided by selecting interesting regs, (b) backtracks parents, (c) runs `bcf_track()` and a refinement callback, and (d) marks the aux flag to request a proof when a condition is produce= d. The actual symbolic tracking, condition build and concrete refinements appear in subsequent patches. Signed-off-by: Hao Sun --- include/linux/bpf.h | 1 + include/linux/bpf_verifier.h | 13 +++ kernel/bpf/verifier.c | 156 +++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a47d67db3be5..690b0b2b84ba 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1656,6 +1656,7 @@ struct bpf_prog_aux { bool changes_pkt_data; bool might_sleep; bool kprobe_write_ctx; + bool bcf_requested; u64 prog_array_member_cnt; /* counts how many times as member of prog_arr= ay */ struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cn= t */ struct bpf_arena *arena; diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index c6eb68b6389c..090430168523 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -732,6 +732,18 @@ struct bpf_scc_info { =20 struct bpf_liveness; =20 +struct bcf_refine_state { + /* The state list that decides the path suffix, on which bcf_track() + * collects symbolic information for target registers. + */ + struct bpf_verifier_state **parents; + u32 vstate_cnt; + u32 cur_vstate; + u32 cur_jmp_entry; + + bool available; /* if bcf_buf is provided. */ +}; + /* single container for all structs * one verifier_env per bpf_check() call */ @@ -838,6 +850,7 @@ struct bpf_verifier_env { struct bpf_scc_info **scc_info; u32 scc_cnt; struct bpf_iarray *succ; + struct bcf_refine_state bcf; }; =20 static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_en= v *env, int subprog) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e4928846e763..7125f7434e6f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -608,6 +608,23 @@ static bool is_atomic_load_insn(const struct bpf_insn = *insn) insn->imm =3D=3D BPF_LOAD_ACQ; } =20 +typedef int (*refine_state_fn)(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, void *ctx); + +static int bcf_refine(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, u32 reg_masks, + refine_state_fn refine_cb, void *ctx); + +static bool bcf_requested(const struct bpf_verifier_env *env) +{ + return env->prog->aux->bcf_requested; +} + +static void mark_bcf_requested(struct bpf_verifier_env *env) +{ + env->prog->aux->bcf_requested =3D true; +} + static int __get_spi(s32 off) { return (-off - 1) / BPF_REG_SIZE; @@ -23378,6 +23395,145 @@ static int do_check_common(struct bpf_verifier_en= v *env, int subprog) return ret; } =20 +static int bcf_track(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, + struct bpf_verifier_state *base) +{ + return -EOPNOTSUPP; +} + +/* + * Backtracks through parent verifier states to identify the suffix of the= path + * that is relevant for register refinement in bcf_track(). Using backtrac= k_insn(), + * this routine locates the instructions that define the target registers = and any + * registers that are transitively related. All states visited during this= process + * collectively define the path suffix. + * + * Returns the parent state of the last visited state, which serves as the= base + * state from which bcf_track() begins its analysis. + * The jump history from the collected states determines the suffix to fol= low. + */ +static struct bpf_verifier_state * +backtrack_states(struct bpf_verifier_env *env, struct bpf_verifier_state *= cur, + u32 reg_masks) +{ + struct bpf_verifier_state *base =3D NULL, *st =3D cur; + struct backtrack_state *bt =3D &env->bt; + struct bcf_refine_state *bcf =3D &env->bcf; + int first_idx =3D cur->first_insn_idx; + int last_idx =3D cur->insn_idx; + int subseq_idx =3D -1; + bool skip_first =3D true; + int i, err, log_level =3D 0; + u32 vstate_cnt; + + if (!reg_masks) + return ERR_PTR(-EFAULT); + + bt_init(bt, st->curframe); + bt->reg_masks[bt->frame] =3D reg_masks; + swap(env->log.level, log_level); /* Disable backtrack_insn() log. */ + + for (;;) { + u32 history =3D st->jmp_history_cnt; + struct bpf_jmp_history_entry *hist; + + if (last_idx < 0 || !st->parent) + break; + + for (i =3D last_idx;;) { + if (skip_first) { + err =3D 0; + skip_first =3D false; + } else { + hist =3D get_jmp_hist_entry(st, history, i); + err =3D backtrack_insn(env, i, subseq_idx, hist, bt); + } + if (err) /* Track the entire path. */ + goto out; + if (bt_empty(bt)) { /* Base state found. */ + base =3D st->parent; + goto out; + } + subseq_idx =3D i; + i =3D get_prev_insn_idx(st, i, &history); + if (i =3D=3D -ENOENT) + break; + if (i >=3D env->prog->len) + goto out; + } + + st =3D st->parent; + subseq_idx =3D first_idx; + last_idx =3D st->last_insn_idx; + first_idx =3D st->first_insn_idx; + } + +out: + bt_reset(bt); + swap(env->log.level, log_level); + + /* Collect parents and follow their jmp history. */ + vstate_cnt =3D 1; + st =3D cur->parent; + while (st !=3D base) { + vstate_cnt++; + st =3D st->parent; + } + bcf->parents =3D kmalloc_array(vstate_cnt, sizeof(st), GFP_KERNEL_ACCOUNT= ); + if (!bcf->parents) + return ERR_PTR(-ENOMEM); + bcf->vstate_cnt =3D vstate_cnt; + st =3D cur; + while (vstate_cnt) { + bcf->parents[--vstate_cnt] =3D st; + st =3D st->parent; + } + bcf->cur_vstate =3D 0; + bcf->cur_jmp_entry =3D 0; + return base; +} + +static int __used bcf_refine(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, u32 reg_masks, + refine_state_fn refine_cb, void *ctx) +{ + struct bpf_reg_state *regs =3D st->frame[st->curframe]->regs; + struct bpf_verifier_state *base; + int i, err; + + if (!env->bcf.available || st->speculative) + return 0; + /* BCF requested multiple times in an error path. */ + if (bcf_requested(env)) + return -EFAULT; + + if (!reg_masks) { + for (i =3D 0; i < BPF_REG_FP; i++) { + if (regs[i].type =3D=3D NOT_INIT) + continue; + if (regs[i].type !=3D SCALAR_VALUE && + tnum_is_const(regs[i].var_off)) + continue; + reg_masks |=3D (1 << i); + } + } + + base =3D backtrack_states(env, st, reg_masks); + if (IS_ERR(base)) + return PTR_ERR(base); + + err =3D bcf_track(env, st, base); + if (!err && refine_cb) + err =3D refine_cb(env, st, ctx); + + if (!err) + mark_bcf_requested(env); + + kfree(env->bcf.parents); + return err ?: 1; +} + /* Lazily verify all global functions based on their BTF, if they are call= ed * from main BPF program or any of subprograms transitively. * BPF global subprogs called from dead code are not validated. --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8F3D533031B for ; Thu, 6 Nov 2025 12:53:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433623; cv=none; b=sM3W/U4BrJZ8mUT3Vww3Vd6MX254dtz75GMlJ2Usatt2i2OFqCZBW/y29b9SdGggbsdQ+PBW03gp4mh5i3c9YVbOE1G7SqKXb5i9KapokNplYPrWrNjje2zB6MtrZ+P5DP81sB7TcQ2XuBsbyIGQxSzgTmUylnBcPfy/sN9ZVoo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433623; c=relaxed/simple; bh=Md++fu49nP/8mT6hALlYH/LhcbC4sBZZpRrZuND42z0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Hdvms3iMzOWpyby27ALx5ujodeVURozVNxHu8tMwgJbH5PP61DQavq2vb1v/DznJdzAh7PLA7ohcxeGtyDxf+DSH+p8ChP99SUZ63CidlJ0UK1vVCCrIPTbfvv2HTA1QIu0K65sQb2NIPOlr1BQ7RAp8Uhu6KZWETuyM5xDUoVM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=E1Z0gvPY; arc=none smtp.client-ip=209.85.221.54 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.b="E1Z0gvPY" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-429c82bf86bso667482f8f.1 for ; Thu, 06 Nov 2025 04:53:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433619; x=1763038419; 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; bh=1nHltJPQHHc4nhzlx7aFR/dmXb3N8HOdaeVX4vRP+Gs=; b=E1Z0gvPYUQge1V0DE7GFevt2ny3kcaS4Ti308ebuqTi+G9Or9qzfwOG+BBMCGOP3F8 cZ9IoXpgoj6SEER8Bu5y3hJoezuxFH3xk5jgL7dy82E/eEdQ2yCT3vfPZAjJ2ytsl10q pzTnahbpcYMtkmk8rioE7JX/yElZg9XFIkWmGV2ewJDfio/Zmc0ahvAGFSmgXAEneDoj 5IIAllv+Vj/Rl/ZbKVV2mYZGgpJHzDeKmwnVwFT5zFZC6lXzekJur7xbVSWE9CIHG9U+ Ur7bXRyDkHzl8dhJ2YZR0YGmTNQjx11J4y+75y/3raLJKTk6f4mD7mFj8xwGUir7NUS+ bB6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433619; x=1763038419; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=1nHltJPQHHc4nhzlx7aFR/dmXb3N8HOdaeVX4vRP+Gs=; b=Jr/lAxqPY9bVTJJLVkS5+TdmjknyPcgzbhkRQnrZ+UYFMWZRxyoygLta6bxDsPDv+G WZpJ0q0P4CfCDPNyQ7YJyWDETziMxrI1SMBr7GrNcJ9FgBnGP6QFigX2ilDd4od+kfp6 /4QSD86AVEY3ffGvjvGRcojVdfNKZ5j5i2mjMQBun8ypMoN2CmsMVi/lyyfBjUvhQBqC vECaibKo4uJqqtUnDAmkA82LWu2ywCkaeOknsxdB1yRboFeAqNmltfJxM+vufkI2xnNw svRqbg4pIrV6qct8ASGKmjNZ13gciQgJcx2+d6gFIdAgzURdxWkUCfZdWI25iLtpM9Pk toyg== X-Forwarded-Encrypted: i=1; AJvYcCXPJO3AvzQ8uux98h2njMYCTE81cy4JhhAk43gxQNhVebsqcqcNCpLj52+itgcH8ObBrKNDIav5aYeWyjc=@vger.kernel.org X-Gm-Message-State: AOJu0Yz2tOO0Fw6FxF6e/AwudcMtzsjMvcfb2PfBqR18bkwkzh4gBzg9 HEhkmWL/JVQb4GEGLYjhucMmivuvG3/i5XcS3+qOJ3fDUgW+/8mRt0M= X-Gm-Gg: ASbGncshGHceUisq1MSY9Ma4oWwZKy0CBap2bkUiNb4xYm/LHk0PLGD5LGjGJ+Ug8+X QbR0FcifUrNoc6sLgWlU9+Np6NE8FeL4aJJpO5jvVtPMsLN7d5ClhRmt+aekXiC7zNaVqBU0Ida Mz3Azx/eoCUZ0JF10DDndeWA9XlGuiwU1nwvks9dAOYuJqNDdB7LWzRcbYoHbKex3bWlQ6uiqUY T0/7nPWolYniDj+vWGuIHohay5XOq4fyCcbO3FfsytAq6YeeaPCl26xIsqecsCdATYmFElJlhG3 BOXavOkTq9uejN17RbuOJrJGpFeuHKJV6P1GucyT2WkbFaawi8jSjQLfFoT1CfID6Q0dTYFDSSZ QIUgyijY21W9k3+35l53OfWak8HKgIy91ukv1JvgUmf4htqfJEibXjqdg/RPTc4JQ+hlc4fna5Z 6vooU+Kqp9EHiS77T24Ufz3QoAUHvUQmb2P7oiy5l93/KodBQzNxaNSms= X-Google-Smtp-Source: AGHT+IFeg37HwbkpqMpKiz0OXhY232Pqm49gtMvdmTd/OAarHIH1KXPvOm+PnlvXfXxurhk4CsA8fA== X-Received: by 2002:a05:6000:3111:b0:429:c450:8fad with SMTP id ffacd0b85a97d-429e3311ab5mr6260946f8f.53.1762433618708; Thu, 06 Nov 2025 04:53:38 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:38 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 05/17] bpf: Add top-level workflow of bcf_track() Date: Thu, 6 Nov 2025 13:52:43 +0100 Message-Id: <20251106125255.1969938-6-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add the top-level `bcf_track()` that symbolically tracks the path suffix identified by `backtrack_states()` and records per-register expressions and path conditions. - Extend `struct bpf_reg_state` with `bcf_expr` index. Extend `env->bcf` with tracking state: expression arena (exprs/expr_cnt/expr_siz= e), branch condition list (br_conds/br_cond_cnt), and final `path_cond` and `refine_cond` index. - Disable liveness/precision/pruning side effects during tracking to ensure single-state, suffix-only analysis (short-circuit early in liveness helpe= rs, jump history/push, speculative sanitization, visited-state pruning). - Implement an env save/restore (`env_backup` + `swap_env_states`) so the tracker can reuse verifier execution without polluting global state.=20 - After tracking, copy collected `bcf_expr` bindings from the tracked state into the original state=E2=80=99s regs. The path condition is built later, Follow-ups add instruction-specific tracking, path matching and condition construction into this framework. Signed-off-by: Hao Sun --- include/linux/bpf_verifier.h | 9 +++ kernel/bpf/liveness.c | 15 ++++ kernel/bpf/verifier.c | 135 +++++++++++++++++++++++++++++++++-- 3 files changed, 154 insertions(+), 5 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 090430168523..b430702784e2 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -204,6 +204,7 @@ struct bpf_reg_state { s32 subreg_def; /* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */ bool precise; + int bcf_expr; }; =20 enum bpf_stack_slot_type { @@ -742,6 +743,14 @@ struct bcf_refine_state { u32 cur_jmp_entry; =20 bool available; /* if bcf_buf is provided. */ + bool tracking; /* In bcf_track(). */ + struct bcf_expr *exprs; + u32 expr_size; + u32 expr_cnt; + u32 *br_conds; /* each branch condition */ + u32 br_cond_cnt; + int path_cond; /* conjunction of br_conds */ + int refine_cond; /* refinement condition */ }; =20 /* single container for all structs diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c index bffb495bc933..4e44c3f0404c 100644 --- a/kernel/bpf/liveness.c +++ b/kernel/bpf/liveness.c @@ -276,6 +276,8 @@ static struct per_frame_masks *alloc_frame_masks(struct= bpf_verifier_env *env, =20 void bpf_reset_live_stack_callchain(struct bpf_verifier_env *env) { + if (env->bcf.tracking) + return; env->liveness->cur_instance =3D NULL; } =20 @@ -318,6 +320,9 @@ int bpf_mark_stack_read(struct bpf_verifier_env *env, u= 32 frame, u32 insn_idx, u { int err; =20 + if (env->bcf.tracking) + return 0; + err =3D ensure_cur_instance(env); err =3D err ?: mark_stack_read(env, env->liveness->cur_instance, frame, i= nsn_idx, mask); return err; @@ -339,6 +344,9 @@ int bpf_reset_stack_write_marks(struct bpf_verifier_env= *env, u32 insn_idx) struct bpf_liveness *liveness =3D env->liveness; int err; =20 + if (env->bcf.tracking) + return 0; + err =3D ensure_cur_instance(env); if (err) return err; @@ -349,6 +357,8 @@ int bpf_reset_stack_write_marks(struct bpf_verifier_env= *env, u32 insn_idx) =20 void bpf_mark_stack_write(struct bpf_verifier_env *env, u32 frame, u64 mas= k) { + if (env->bcf.tracking) + return; env->liveness->write_masks_acc[frame] |=3D mask; } =20 @@ -398,6 +408,8 @@ static int commit_stack_write_marks(struct bpf_verifier= _env *env, */ int bpf_commit_stack_write_marks(struct bpf_verifier_env *env) { + if (env->bcf.tracking) + return 0; return commit_stack_write_marks(env, env->liveness->cur_instance); } =20 @@ -675,6 +687,9 @@ int bpf_update_live_stack(struct bpf_verifier_env *env) struct func_instance *instance; int err, frame; =20 + if (env->bcf.tracking) + return 0; + bpf_reset_live_stack_callchain(env); for (frame =3D env->cur_state->curframe; frame >=3D 0; --frame) { instance =3D lookup_instance(env, env->cur_state, frame); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7125f7434e6f..725ea503c1c7 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3924,6 +3924,9 @@ static int push_jmp_history(struct bpf_verifier_env *= env, struct bpf_verifier_st struct bpf_jmp_history_entry *p; size_t alloc_size; =20 + if (env->bcf.tracking) + return 0; + /* combine instruction flags if we already recorded this instruction */ if (env->cur_hist_ent) { /* atomic instructions push insn_flags twice, for READ and @@ -4735,7 +4738,7 @@ static int __mark_chain_precision(struct bpf_verifier= _env *env, struct bpf_reg_state *reg; int i, fr, err; =20 - if (!env->bpf_capable) + if (!env->bpf_capable || env->bcf.tracking) return 0; =20 changed =3D changed ?: &tmp; @@ -8878,6 +8881,9 @@ static struct bpf_verifier_state *find_prev_entry(str= uct bpf_verifier_env *env, struct bpf_verifier_state *st; struct list_head *pos, *head; =20 + if (env->bcf.tracking) + return NULL; + /* Explored states are pushed in stack order, most recent states come fir= st */ head =3D explored_state(env, insn_idx); list_for_each(pos, head) { @@ -14302,7 +14308,8 @@ static bool can_skip_alu_sanitation(const struct bp= f_verifier_env *env, { return env->bypass_spec_v1 || BPF_SRC(insn->code) =3D=3D BPF_K || - cur_aux(env)->nospec; + cur_aux(env)->nospec || + env->bcf.tracking; } =20 static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux, @@ -14350,6 +14357,9 @@ static int sanitize_speculative_path(struct bpf_ver= ifier_env *env, struct bpf_verifier_state *branch; struct bpf_reg_state *regs; =20 + if (env->bcf.tracking) + return 0; + branch =3D push_stack(env, next_idx, curr_idx, true); if (!IS_ERR(branch) && insn) { regs =3D branch->frame[branch->curframe]->regs; @@ -19415,6 +19425,9 @@ static int is_state_visited(struct bpf_verifier_env= *env, int insn_idx) int n, err, states_cnt =3D 0; struct list_head *pos, *tmp, *head; =20 + if (env->bcf.tracking) + return 0; + force_new_state =3D env->test_state_freq || is_force_checkpoint(env, insn= _idx) || /* Avoid accumulating infinitely long jmp history */ cur->jmp_history_cnt > 40; @@ -20076,7 +20089,7 @@ static int do_check(struct bpf_verifier_env *env) struct bpf_insn *insns =3D env->prog->insnsi; int insn_cnt =3D env->prog->len; bool do_print_state =3D false; - int prev_insn_idx =3D -1; + int prev_insn_idx =3D env->prev_insn_idx; =20 for (;;) { struct bpf_insn *insn; @@ -20178,6 +20191,14 @@ static int do_check(struct bpf_verifier_env *env) if (err) return err; err =3D do_check_insn(env, &do_print_state); + /* + * bcf_track() only follows checked insns, errors during it + * indicate a previously refined location; The refinement + * is applied directly (see bcf_refine()), so analyzes the + * insn again with the refined state. + */ + if (err && env->bcf.tracking) + err =3D do_check_insn(env, &do_print_state); if (err >=3D 0 || error_recoverable_with_nospec(err)) { marks_err =3D bpf_commit_stack_write_marks(env); if (marks_err) @@ -23275,6 +23296,7 @@ static int do_check_common(struct bpf_verifier_env = *env, int subprog) struct bpf_reg_state *regs; int ret, i; =20 + env->prev_insn_idx =3D -1; env->prev_linfo =3D NULL; env->pass_cnt++; =20 @@ -23388,6 +23410,10 @@ static int do_check_common(struct bpf_verifier_env= *env, int subprog) } =20 ret =3D do_check(env); + + /* Invoked by bcf_track(), just return. */ + if (env->bcf.tracking) + return ret; out: if (!ret && pop_log) bpf_vlog_reset(&env->log, 0); @@ -23395,11 +23421,104 @@ static int do_check_common(struct bpf_verifier_e= nv *env, int subprog) return ret; } =20 +struct env_backup { + u32 insn_idx; + u32 prev_insn_idx; + struct bpf_verifier_stack_elem *head; + int stack_size; + u32 id_gen; + struct bpf_verifier_state *cur_state; + const struct bpf_line_info *prev_linfo; + struct list_head *explored_states; + struct list_head free_list; + u32 log_level; + u32 prev_insn_processed, insn_processed; + u32 prev_jmps_processed, jmps_processed; +}; + +static void swap_env_states(struct env_backup *env_old, + struct bpf_verifier_env *env) +{ + swap(env_old->insn_idx, env->insn_idx); + swap(env_old->prev_insn_idx, env->prev_insn_idx); + swap(env_old->head, env->head); + swap(env_old->stack_size, env->stack_size); + swap(env_old->id_gen, env->id_gen); + swap(env_old->cur_state, env->cur_state); + swap(env_old->prev_linfo, env->prev_linfo); + swap(env_old->explored_states, env->explored_states); + swap(env_old->free_list, env->free_list); + /* Disable log during bcf tracking */ + swap(env_old->log_level, env->log.level); + swap(env_old->prev_insn_processed, env->prev_insn_processed); + swap(env_old->insn_processed, env->insn_processed); + swap(env_old->prev_jmps_processed, env->prev_jmps_processed); + swap(env_old->jmps_processed, env->jmps_processed); +} + static int bcf_track(struct bpf_verifier_env *env, struct bpf_verifier_state *st, struct bpf_verifier_state *base) { - return -EOPNOTSUPP; + struct bpf_reg_state *regs =3D st->frame[st->curframe]->regs; + struct bpf_reg_state *tracked_regs; + struct bpf_verifier_state *vstate =3D NULL; + struct env_backup env_old =3D { 0 }; + struct bcf_refine_state *bcf =3D &env->bcf; + int err, i; + + bcf->expr_cnt =3D 0; + bcf->path_cond =3D -1; + bcf->refine_cond =3D -1; + + if (base) { + vstate =3D kzalloc(sizeof(struct bpf_verifier_state), + GFP_KERNEL_ACCOUNT); + if (!vstate) + return -ENOMEM; + err =3D copy_verifier_state(vstate, base); + if (err) { + free_verifier_state(vstate, true); + return err; + } + vstate->parent =3D vstate->equal_state =3D NULL; + vstate->first_insn_idx =3D base->insn_idx; + clear_jmp_history(vstate); + } + + /* Continue with the current id. */ + env_old.id_gen =3D env->id_gen; + swap_env_states(&env_old, env); + + env->bcf.tracking =3D true; + if (vstate) { + env->insn_idx =3D vstate->first_insn_idx; + env->prev_insn_idx =3D vstate->last_insn_idx; + env->cur_state =3D vstate; + err =3D do_check(env); + } else { + u32 subprog =3D st->frame[0]->subprogno; + + env->insn_idx =3D env->subprog_info[subprog].start; + err =3D do_check_common(env, subprog); + } + env->bcf.tracking =3D false; + + if (!err && !same_callsites(env->cur_state, st)) + err =3D -EFAULT; + + if (!err) { + tracked_regs =3D cur_regs(env); + for (i =3D 0; i < BPF_REG_FP; i++) + regs[i].bcf_expr =3D tracked_regs[i].bcf_expr; + } + + free_verifier_state(env->cur_state, true); + env->cur_state =3D NULL; + while (!pop_stack(env, NULL, NULL, false)) + ; + swap_env_states(&env_old, env); + return err; } =20 /* @@ -23507,6 +23626,12 @@ static int __used bcf_refine(struct bpf_verifier_e= nv *env, /* BCF requested multiple times in an error path. */ if (bcf_requested(env)) return -EFAULT; + /* BCF requested during bcf_track(), known safe just refine. */ + if (env->bcf.tracking) { + if (refine_cb) + return refine_cb(env, st, ctx); + return 0; + } =20 if (!reg_masks) { for (i =3D 0; i < BPF_REG_FP; i++) { @@ -23527,7 +23652,7 @@ static int __used bcf_refine(struct bpf_verifier_en= v *env, if (!err && refine_cb) err =3D refine_cb(env, st, ctx); =20 - if (!err) + if (!err && (env->bcf.refine_cond >=3D 0 || env->bcf.path_cond >=3D 0)) mark_bcf_requested(env); =20 kfree(env->bcf.parents); --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 09714333448 for ; Thu, 6 Nov 2025 12:53:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433623; cv=none; b=RY5TAumLksZeso9ckDQ70l7MTpS8ZVNHfArcC9oDCvTo7ZwFKzsmHHgAxICkqY3Evuwyru5mGUbAWmmwGoLEs43h93zpWDDQVMwVsj+BiBB1h2Roo6Zp4ggUtzbAPlqVovwvhAUQyIyT9M2tO+hXFtSFz3es5bpV4H3EoDQtZE4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433623; c=relaxed/simple; bh=nJMaLGpKEmP3DsyD+IyFFczvrWgzDb+7/77EaIxg+NE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=s2i3vhqgrgZraL43fp68+8mpZV79/gux6alViTiIH0Uirx9n3DXrDL1eKCgSrcYn0LQ1DrDrOep4yFS91+wBv/HP4WPmyhwVBkx1g3YPSv7MF8Fu4CptlfbjStps4FXEHPOqaInfuhP6pR+O26eCvG1AfRKlPiGyestRf0+KrxY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Bx+O8LXv; arc=none smtp.client-ip=209.85.221.46 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.b="Bx+O8LXv" Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-429b7eecf7cso657741f8f.0 for ; Thu, 06 Nov 2025 04:53:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433619; x=1763038419; 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; bh=Gboyt37Ov9MOTTH/KPaW8uj5vESoPgNEDU8dO3DpPF0=; b=Bx+O8LXvxiiSTcAE6E8Z0YHAr8UcvU4LFsOvQf/bttKd2WtVNM80bh9xYhBuQ7Scoy WdIIb5Fa+y/+3p6Hy6T+viHpCkl55dsvdGLw9ZCBA0nI89NiCk3MBGMSR+aGDRIk1+Ic hbPgXhSPulZ7VGIAtkFDUX7LRVayoa0gwRiSBazFfpCf5+M2Fx/EkJhBYYTUqM8SrMwK 0hCGuSS4wnPXikaQob3ZuWDPTFCrXuTvXceP66MxUNadN+3ToqJet1EnIkqJCD3SupQW znBNlqMxmQOrqhbEp37Mf0NwSL64nsAgZX4OCyAkrBydRvfpPcuh6tvckDD1qht2HRxt 6i9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433619; x=1763038419; 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; bh=Gboyt37Ov9MOTTH/KPaW8uj5vESoPgNEDU8dO3DpPF0=; b=RpUVTpyKmYmA+I7FR4UUNMKsFsytfkhycNan4H+259QbNqSiJEqeqQCoGkFyEhJUYi KESuuM57aFU8MJxu3MUuoovdD2r/f+nhF+MVZ8KctO187tkX7pj8UJAJJCpStZqcmBgq 2bEo0gmWKP0aKvJ+i4Mox6Cc8Ag6di5ufIS9SZLDUSlNH67uHDPYguHOSaHaJQjHRXam 1AS+MVNasUlyU2f98nanqdVOpIXhyOIjT6hjmRCvewl13vpvLcnbMBcf6Cornfj8L+xq UegvrkMhROnTqFcLV0LjibokTFQo9CimcyHrh34KXQXjoBGlZ70KQCmt1MSnxT5dRHvW nj8g== X-Forwarded-Encrypted: i=1; AJvYcCXN5tdjG0Htb5QOT+YzXSStSxXl2OjdWExe/VgMb9FMDmR3km2g0KBRtCThOfLuekrKCAFqlMVXULDzBtw=@vger.kernel.org X-Gm-Message-State: AOJu0Yx8v9oBYF/FN9RJ7i2VQVseb6Hl4IRiRDhXhoea92ZAjr02xIaa eAktgt3wuSKIOf45TE22/KJKbYSYDGvyguzK0AOMcn0OHUm/98WKN0w= X-Gm-Gg: ASbGnctL7I+q/3iNwS3SBAOlazhd/FgV8Aw+sb5KKw0vripzo8MRd3oLZehmwu2Oek5 p96p4o0IaRBrNtipZldh1nsWf4PYtjDx4dYKvQWtlG6FTNmkAwc5pPoEfwQKlCCupB8VyykDv4H CJGs4cvj/jYY/lA1tTtKgmeinQFf/ycLcAqkb7iUXPE6dhMAAlpj/SQgx42jKzNWQAcIqJYm37m sTHAVtWxrL8pxxErc0WJOacugy4Jy/A2C5pMEVnqSJSnvWOxLW0ZPNyGipG+lC7nGI5qWBQTEGp iLeKspEFuff0Q39iaLVeY/nbZbpHo4eir5GFxN2a1onFVWlQ8klXc4YEGOT0/kaJgapJd1dZAT8 IcK2k9mQNaZxsAwI4a4EIxRtdumibewzA2VEJkoNZwyTlIeZqVqJzZg6+PiEUZ7ksw0sMv8eSuX BpTHuiuB+BH31DcCTzT9cl4EN2h8PcAO+VixoJpysIdOt/BLv79dKElvk= X-Google-Smtp-Source: AGHT+IE2d3CRUrs2Qxa0+9muOrmRjSuRP78wlr1433ZpQlc/ZIYdsLyGsliVtRU0kPFyTQ8p1N80tw== X-Received: by 2002:a5d:5f54:0:b0:429:bb77:5deb with SMTP id ffacd0b85a97d-429eb1d0d12mr3086191f8f.31.1762433619310; Thu, 06 Nov 2025 04:53:39 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:39 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 06/17] bpf: Add bcf_match_path() to follow the path suffix Date: Thu, 6 Nov 2025 13:52:44 +0100 Message-Id: <20251106125255.1969938-7-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add `bcf_match_path()` to constrain `bcf_track()` to the recorded path suff= ix from parent states. The function consumes the per-state jump history arrays= in order and compares each (prev_idx, idx) pair against the verifier=E2=80=99s= current (prev_insn_idx, insn_idx): - If the current pair matches the top entry, advance to the next history en= try; when the last entry is consumed and the last state's last_insn matches, s= top tracking (PATH_DONE). - If the pair mismatches at a branch point, abandon the current fork (PATH_MISMATCH) so the tracker pops the path. - Otherwise, continue (PATH_MATCH). `do_check()` is updated under tracking mode to call `bcf_match_path()` befo= re processing each instruction and to terminate early on PATH_DONE, ensuring o= nly suffix instructions are symbolically analyzed. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 725ea503c1c7..3ecee219605f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -20082,6 +20082,63 @@ static int do_check_insn(struct bpf_verifier_env *= env, bool *do_print_state) return 0; } =20 +static struct bpf_jmp_history_entry * +get_top_jmp_entry(struct bpf_verifier_env *env) +{ + struct bcf_refine_state *bcf =3D &env->bcf; + struct bpf_verifier_state *vstate; +next: + if (bcf->cur_vstate >=3D bcf->vstate_cnt) + return NULL; + vstate =3D bcf->parents[bcf->cur_vstate]; + if (bcf->cur_jmp_entry >=3D vstate->jmp_history_cnt) { + bcf->cur_vstate++; + bcf->cur_jmp_entry =3D 0; + goto next; + } + return &vstate->jmp_history[bcf->cur_jmp_entry]; +} + +enum { PATH_MATCH, PATH_MISMATCH, PATH_DONE }; + +static int bcf_match_path(struct bpf_verifier_env *env) +{ + struct bcf_refine_state *bcf =3D &env->bcf; + struct bpf_jmp_history_entry *top =3D get_top_jmp_entry(env); + struct bpf_verifier_state *last_state; + int prev_idx; + + last_state =3D bcf->parents[bcf->vstate_cnt - 1]; + if (!top) + return last_state->last_insn_idx =3D=3D env->prev_insn_idx ? + PATH_DONE : + PATH_MATCH; + + prev_idx =3D top->prev_idx; + /* entry->prev_idx is u32:20, compiler does not sign extend this */ + if (prev_idx =3D=3D 0xfffff) + prev_idx =3D -1; + + if (prev_idx =3D=3D env->prev_insn_idx) { + if (top->idx =3D=3D env->insn_idx) { + bcf->cur_jmp_entry++; + /* Check if we have consumed the last entry */ + top =3D get_top_jmp_entry(env); + if (!top && + last_state->last_insn_idx =3D=3D env->prev_insn_idx) + return PATH_DONE; + return PATH_MATCH; + } + return PATH_MISMATCH; + } + + /* cur_state is branch taken, but the recorded one is not */ + if (is_jmp_point(env, env->insn_idx)) + return PATH_MISMATCH; + + return PATH_MATCH; +} + static int do_check(struct bpf_verifier_env *env) { bool pop_log =3D !(env->log.level & BPF_LOG_LEVEL2); @@ -20144,6 +20201,15 @@ static int do_check(struct bpf_verifier_env *env) return err; } =20 + if (env->bcf.tracking) { + int path =3D bcf_match_path(env); + + if (path =3D=3D PATH_MISMATCH) + goto process_bpf_exit; + else if (path =3D=3D PATH_DONE) + return 0; + } + if (signal_pending(current)) return -EAGAIN; =20 --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CB76E335BA7 for ; Thu, 6 Nov 2025 12:53:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433624; cv=none; b=s/qiD7GDzxu6L0UIoYobxAEy/OLxNcHF47FTbbvRo2lV950TjDttvoytPxIPwi2O4J/0yMpIw7AN3lM8ezbmmfgz5O3GQEvoKlD6ilPbWfmdeB0dKhfJo2xuG344jssylHUca7tmh+QVBIC7sL53/CHJhX3rRu/ry+7Kmc97kII= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433624; c=relaxed/simple; bh=gYI3PPdY/pxVI3TQMaLHa1tG9D9Q3pX163qPj6Y+5JI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Q+Z3bbQ3QZZkDfwW1q2sA0MGy92XuEFbls1hiuvSjOu+dba3A6YuNi6Fi9ImNLZGcp3B/dA3qYE1XYowT0Ngg4Z53LPaevhA1vME3qhj8+cUmlU9OzJnfnXHMGHsb0MUUJMYC+zgdvDc4IjDWmF1lKrpYgGpm31qFlH40gwSaQI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=X2KG0TNH; arc=none smtp.client-ip=209.85.221.54 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.b="X2KG0TNH" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-3ee130237a8so648149f8f.0 for ; Thu, 06 Nov 2025 04:53:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433620; x=1763038420; 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; bh=8vW1pnqHWzKhRlujJQJ97tr37TfsN4Lo7m0CG+Gl/ro=; b=X2KG0TNHHBut73oYurbxKGyoBsCoxsWO0/djDjSJhUdgXanM0xsOGO3BA5PG5pNDVA i3UE5jWSPa0Q5y1za81R29fj/1WP+u8gidGRtxUz/c9diDzPTtuAFMyy7utdW4gjusiD rI5Hl1KU2s4i67tn1sO7GoCpB/rNpLoQQyQfbn+ZlkC37kaT5RNjmhobqcb+y08JuUes Yi4xcnZwHP2EGbUU+Gwe4HPf+lk7dfmmAITRJJtv1gFXJJNProD694zx0pwKQAaEctfg I/Bu7hBIrgWRmSw21STgBleUqLpwJ9O4ITxaQDcpAykyTW2KTfN2v9a8JMMoRwaR4kKC 5PBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433620; x=1763038420; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8vW1pnqHWzKhRlujJQJ97tr37TfsN4Lo7m0CG+Gl/ro=; b=IqJUrHaNWg+1aBhOTfeR/BhKjG6dPVpMCAxvon/gPzE5yoQxPkC628I5MWRnNVsZfR QEvOsLMgY8+g2Ua/6LQJjFyItrhY+k5wm7SpBmvLWyxniap8r8DvYZBBQFmS29qMVgbR 4lEeVo0orurpRlEYI9hrQ/XUROQhcFRZv9wR9BtZwO+HxyIX0uWYSR0EN3Gr36zflGtW k0nTE+CGZxn4RKZf6//PUNaXgT3NEvAm6j0TV+S1qxadacdL17qkIQTg4B5FKoOkG75d PqD4b9s3sfLqBp7VHI5zrvmPIUm73mHZ2qssrjKWA7JeFup77blfrVuLCWVk3i34n4Sc d4KA== X-Forwarded-Encrypted: i=1; AJvYcCU0KI+Rvt/swh5c3SH9BKpjkjSVGWnPEhWBbqWWb86BVXIAlj8H/iTPbCrZ2dmAQ6iYTaOAF0i5Nrcs4Rg=@vger.kernel.org X-Gm-Message-State: AOJu0Yzd0HM5I6oYctsPHUvb5evh12nT8FL2zbaE+a6rFxcUlTqc58MP V1BHs0tJdYE0r8NQDBlKo8c2kIUDpX2z1hE8ZS1M/JMLISo0B5wawTc= X-Gm-Gg: ASbGncvl3KdSykQi8f9zG32RuU55TLsW50TUL0uchn1F/PMbp3O+ija+l6j48qu21dU tZ73xuTs3nnQNKE0Ij/KMXDVznil1x1UAE5skBQi/2LgWebRHNbcR3yzt0CNLQK7NP9iv6BBIBF OnWhgGcxsQDFq1ZZO2WZ7OXAWqN4DO/KnXwqClpuuEh+MfJ7zvX5Oda6GXX/hnKmTEVavBzgYr7 q+V9JmPuOLR0wGv38FJxtvCEhiEFJ8Q7Scut1B2F3blR/mfvWbJDn10p5vug7mRYOAnFA+xuy9b B7EevaS4KhDbaBlNkzwHf7HDrTQhHwzG7vbTGsPpgqIzdg410hZfCCZIA9/ie2cVq4ykHL1kxaf MS+C+NXc2FIzU+VeFV+HdH/GDq+2m5zOHHmt9vKQAWZjihEzOkAKyIbGJGaORRetlqk+yHfO/i1 aZvIK6wjGdYARNN+XhQp3zy7KgPy2/AWqPcV5AEP+CvMwY2qau5RMCdjg= X-Google-Smtp-Source: AGHT+IF67KTAG0Z+EcRSvv3TCoZ31iPTiYggpF50xcRnauZWBkj9PGoXZ+O8tYHDr6SE2qfIi6fvvw== X-Received: by 2002:a05:6000:3101:b0:429:8cda:dd4e with SMTP id ffacd0b85a97d-429e33079fbmr6395601f8f.32.1762433619927; Thu, 06 Nov 2025 04:53:39 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:39 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 07/17] bpf: Add bcf_expr management and binding Date: Thu, 6 Nov 2025 13:52:45 +0100 Message-Id: <20251106125255.1969938-8-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add expression arena management for BCF tracking and lazy binding of symbol= ic expressions to registers during `bcf_track()`. - Arena management: `bcf_alloc_expr()` grows `env->bcf.exprs` with reallocation in chunks; `bcf_add_expr()` copies a built node; `bcf_build_= expr()` constructs nodes with varargs. - Predicates/constraints: `bcf_add_pred()` creates boolean comparisons with= an immediate RHS; `bcf_add_cond()` appends branch-condition ids into `env->bcf.br_conds`. - Lazy binding: `reg->bcf_expr` starts at -1 and is set on first use via `bcf_reg_expr()`. Binding leverages existing range info: if a reg fits in u32/s32 ranges, allocate a 32-bit var, add range constraints (u/s min/max= ), then extend to 64-bit with zero/sign extension; otherwise allocate a 64-b= it var and emit 64-bit bounds. `__check_reg_arg()` binds on first read when tracking. This allows later instruction-specific tracking to refer to previously bound register expressions and accumulate path constraints. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 280 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3ecee219605f..7b6d509c773a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -4,6 +4,7 @@ * Copyright (c) 2018 Covalent IO, Inc. http://covalent.io */ #include +#include #include #include #include @@ -625,6 +626,275 @@ static void mark_bcf_requested(struct bpf_verifier_en= v *env) env->prog->aux->bcf_requested =3D true; } =20 +static int bcf_alloc_expr(struct bpf_verifier_env *env, u32 cnt) +{ + struct bcf_refine_state *bcf =3D &env->bcf; + int idx =3D bcf->expr_cnt; + struct bcf_expr *exprs; + u32 size, alloc_cnt; + + bcf->expr_cnt +=3D cnt; + if (bcf->expr_cnt <=3D bcf->expr_size) + return idx; + + alloc_cnt =3D max_t(u32, 256, cnt); + size =3D size_mul(bcf->expr_size + alloc_cnt, sizeof(struct bcf_expr)); + exprs =3D kvrealloc(bcf->exprs, size, GFP_KERNEL_ACCOUNT); + if (!exprs) { + kvfree(bcf->exprs); + bcf->exprs =3D NULL; + return -ENOMEM; + } + bcf->exprs =3D exprs; + bcf->expr_size +=3D alloc_cnt; + + return idx; +} + +static int bcf_add_expr(struct bpf_verifier_env *env, struct bcf_expr *exp= r) +{ + u32 cnt =3D (u32)expr->vlen + 1; + int idx; + + idx =3D bcf_alloc_expr(env, cnt); + if (idx >=3D 0) + memcpy(env->bcf.exprs + idx, expr, + sizeof(struct bcf_expr) * cnt); + return idx; +} + +static int bcf_build_expr(struct bpf_verifier_env *env, u8 code, u16 param= s, + u32 vlen, ...) +{ + DEFINE_RAW_FLEX(struct bcf_expr, expr, args, U8_MAX); + va_list args; + u32 i; + + if (vlen > U8_MAX) + return -EFAULT; + + expr->code =3D code; + expr->vlen =3D vlen; + expr->params =3D params; + + va_start(args, vlen); + for (i =3D 0; i < vlen; i++) { + int arg =3D va_arg(args, int); + + if (arg < 0) + return arg; + expr->args[i] =3D arg; + } + return bcf_add_expr(env, expr); +} + +static int bcf_add_cond(struct bpf_verifier_env *env, int expr) +{ + struct bcf_refine_state *bcf =3D &env->bcf; + u32 cnt =3D bcf->br_cond_cnt; + size_t size; + u32 *conds; + + if (expr < 0) + return expr; + if (cnt >=3D U8_MAX) + return -E2BIG; + + cnt++; + size =3D kmalloc_size_roundup(cnt * sizeof(u32)); + conds =3D krealloc(bcf->br_conds, size, GFP_KERNEL_ACCOUNT); + if (!conds) { + kfree(bcf->br_conds); + bcf->br_conds =3D NULL; + return -ENOMEM; + } + bcf->br_conds =3D conds; + conds[bcf->br_cond_cnt++] =3D expr; + return 0; +} + +static int bcf_val(struct bpf_verifier_env *env, u64 val, bool bit32) +{ + DEFINE_RAW_FLEX(struct bcf_expr, expr, args, 2); + + expr->code =3D BCF_BV | BCF_VAL; + expr->vlen =3D bit32 ? 1 : 2; + expr->params =3D bit32 ? 32 : 64; + expr->args[0] =3D val; + if (!bit32) + expr->args[1] =3D val >> 32; + return bcf_add_expr(env, expr); +} + +static int bcf_var(struct bpf_verifier_env *env, bool bit32) +{ + return bcf_build_expr(env, BCF_BV | BCF_VAR, bit32 ? 32 : 64, 0); +} + +static int bcf_extend(struct bpf_verifier_env *env, u16 ext_sz, u16 bit_sz, + bool sign_ext, int expr) +{ + u8 op =3D sign_ext ? BCF_SIGN_EXTEND : BCF_ZERO_EXTEND; + + return bcf_build_expr(env, BCF_BV | op, (ext_sz << 8) | bit_sz, 1, + expr); +} + +static int bcf_extract(struct bpf_verifier_env *env, u16 sz, int expr) +{ + return bcf_build_expr(env, BCF_BV | BCF_EXTRACT, (sz - 1) << 8, 1, + expr); +} + +static int bcf_zext_32_to_64(struct bpf_verifier_env *env, + struct bpf_reg_state *reg) +{ + reg->bcf_expr =3D bcf_extend(env, 32, 64, false, reg->bcf_expr); + return reg->bcf_expr; +} + +static int bcf_sext_32_to_64(struct bpf_verifier_env *env, + struct bpf_reg_state *reg) +{ + reg->bcf_expr =3D bcf_extend(env, 32, 64, true, reg->bcf_expr); + return reg->bcf_expr; +} + +static bool is_zext_32_to_64(struct bcf_expr *expr) +{ + return expr->code =3D=3D (BCF_BV | BCF_ZERO_EXTEND) && + expr->params =3D=3D (((u16)32 << 8) | 64); +} + +static bool is_sext_32_to_64(struct bcf_expr *expr) +{ + return expr->code =3D=3D (BCF_BV | BCF_SIGN_EXTEND) && + expr->params =3D=3D (((u16)32 << 8) | 64); +} + +static int bcf_expr32(struct bpf_verifier_env *env, int expr_idx) +{ + struct bcf_expr *expr; + + if (expr_idx < 0) + return expr_idx; + + expr =3D env->bcf.exprs + expr_idx; + if (is_zext_32_to_64(expr) || is_sext_32_to_64(expr)) + return expr->args[0]; + + if (expr->code =3D=3D (BCF_BV | BCF_VAL)) + return bcf_val(env, expr->args[0], true); + + return bcf_extract(env, 32, expr_idx); +} + +static int bcf_add_pred(struct bpf_verifier_env *env, u8 op, int lhs, u64 = imm, + bool bit32) +{ + int rhs; + + if (lhs < 0) + return lhs; + + rhs =3D bcf_val(env, imm, bit32); + return bcf_build_expr(env, BCF_BOOL | op, 0, 2, lhs, rhs); +} + +static bool fit_u32(struct bpf_reg_state *reg) +{ + return reg->umin_value =3D=3D reg->u32_min_value && + reg->umax_value =3D=3D reg->u32_max_value; +} + +static bool fit_s32(struct bpf_reg_state *reg) +{ + return reg->smin_value =3D=3D reg->s32_min_value && + reg->smax_value =3D=3D reg->s32_max_value; +} + +static int __bcf_bound_reg(struct bpf_verifier_env *env, int op, int expr, + u64 imm, bool bit32) +{ + return bcf_add_cond(env, bcf_add_pred(env, op, expr, imm, bit32)); +} + +static int bcf_bound_reg32(struct bpf_verifier_env *env, + struct bpf_reg_state *reg) +{ + u32 umin =3D reg->u32_min_value, umax =3D reg->u32_max_value; + s32 smin =3D reg->s32_min_value, smax =3D reg->s32_max_value; + int expr =3D reg->bcf_expr; + int ret =3D 0; + + if (umin !=3D 0) + ret =3D __bcf_bound_reg(env, BPF_JGE, expr, umin, true); + if (ret >=3D 0 && umax !=3D U32_MAX) + ret =3D __bcf_bound_reg(env, BPF_JLE, expr, umax, true); + + if (ret >=3D 0 && smin !=3D S32_MIN && smin !=3D umin) + ret =3D __bcf_bound_reg(env, BPF_JSGE, expr, smin, true); + if (ret >=3D 0 && smax !=3D S32_MAX && smax !=3D umax) + ret =3D __bcf_bound_reg(env, BPF_JSLE, expr, smax, true); + + return ret; +} + +static int bcf_bound_reg(struct bpf_verifier_env *env, + struct bpf_reg_state *reg) +{ + u64 umin =3D reg->umin_value, umax =3D reg->umax_value; + s64 smin =3D reg->smin_value, smax =3D reg->smax_value; + int expr =3D reg->bcf_expr; + int ret =3D 0; + + if (umin !=3D 0) + ret =3D __bcf_bound_reg(env, BPF_JGE, expr, umin, false); + if (ret >=3D 0 && umax !=3D U64_MAX) + ret =3D __bcf_bound_reg(env, BPF_JLE, expr, umax, false); + + if (ret >=3D 0 && smin !=3D S64_MIN && smin !=3D umin) + ret =3D __bcf_bound_reg(env, BPF_JSGE, expr, smin, false); + if (ret >=3D 0 && smax !=3D S64_MAX && smax !=3D umax) + ret =3D __bcf_bound_reg(env, BPF_JSLE, expr, smax, false); + + return ret; +} + +static int bcf_reg_expr(struct bpf_verifier_env *env, struct bpf_reg_state= *reg, + bool subreg) +{ + int err; + + if (reg->bcf_expr >=3D 0) + goto out; + + if (tnum_is_const(reg->var_off)) { + reg->bcf_expr =3D bcf_val(env, reg->var_off.value, false); + } else if (fit_u32(reg)) { + reg->bcf_expr =3D bcf_var(env, true); + err =3D bcf_bound_reg32(env, reg); + if (err < 0) + return err; + bcf_zext_32_to_64(env, reg); + } else if (fit_s32(reg)) { + reg->bcf_expr =3D bcf_var(env, true); + err =3D bcf_bound_reg32(env, reg); + if (err < 0) + return err; + bcf_sext_32_to_64(env, reg); + } else { + reg->bcf_expr =3D bcf_var(env, false); + err =3D bcf_bound_reg(env, reg); + if (err < 0) + return err; + } +out: + if (!subreg) + return reg->bcf_expr; + return bcf_expr32(env, reg->bcf_expr); +} + static int __get_spi(s32 off) { return (-off - 1) / BPF_REG_SIZE; @@ -2187,6 +2457,8 @@ static void ___mark_reg_known(struct bpf_reg_state *r= eg, u64 imm) reg->s32_max_value =3D (s32)imm; reg->u32_min_value =3D (u32)imm; reg->u32_max_value =3D (u32)imm; + + reg->bcf_expr =3D -1; } =20 /* Mark the unknown part of a register (variable offset or scalar value) as @@ -2807,6 +3079,7 @@ static void __mark_reg_unknown_imprecise(struct bpf_r= eg_state *reg) reg->var_off =3D tnum_unknown; reg->frameno =3D 0; reg->precise =3D false; + reg->bcf_expr =3D -1; __mark_reg_unbounded(reg); } =20 @@ -3790,6 +4063,12 @@ static int __check_reg_arg(struct bpf_verifier_env *= env, struct bpf_reg_state *r if (rw64) mark_insn_zext(env, reg); =20 + /* Bind an expr for non-constants when it's first used. */ + if (env->bcf.tracking && !tnum_is_const(reg->var_off)) { + bcf_reg_expr(env, reg, false); + if (reg->bcf_expr < 0) + return reg->bcf_expr; + } return 0; } else { /* check whether register used as dest operand can be written to */ @@ -25244,6 +25523,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_att= r *attr, bpfptr_t uattr, __u3 kvfree(env->cfg.insn_postorder); kvfree(env->scc_info); kvfree(env->succ); + kvfree(env->bcf.exprs); kvfree(env); return ret; } --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f51.google.com (mail-wr1-f51.google.com [209.85.221.51]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F1D632ED3C for ; Thu, 6 Nov 2025 12:53:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.51 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433624; cv=none; b=VUkXpF/SOgXO2IILq8wUtmea7HQxWVUQ5eYSKXvQUqUOXQRgvUP4iUx2v6yIpxt+67nc23ckil8dCv+K0NevPzRAvichMEL8rUmbzToA0ls/Spz0K/CzfNK1ngdDvPG3kK37XfANMq/taSG6ZYt+64EiA6loBxL9DNJUb7UmHeA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433624; c=relaxed/simple; bh=yJ5nI4Pj/e2l438vacBXP4YBnLRHauWN7FXlZehjs1U=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Uflxk0WZ83sU7AF7Jz2sJR5C5KbC9D2z+Cc9oGWs6L3tK6n97b1PKeZ2qay91oK7VRTNM5CJm8NvraqJ3pkfYJhSJlQVWNIEznDbOdFNMidgxRwDpXoY2EvzRMb21jxtqgGlgshbQNAwVReuVzprO6nk4O4fcwcJzntU9Aum7jc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=R1M8DFEq; arc=none smtp.client-ip=209.85.221.51 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.b="R1M8DFEq" Received: by mail-wr1-f51.google.com with SMTP id ffacd0b85a97d-429bcddad32so702271f8f.3 for ; Thu, 06 Nov 2025 04:53:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433621; x=1763038421; 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; bh=TeQ1qQssRNVDy6SgVbDGPxE08ddX4PJBvQHFLhXifeI=; b=R1M8DFEqJCZQNRUjCVDryhHFk+KbSPp/UeHeOYMK/0OApOx3+K6WAuh0SAFowvafHM 9y3tkfibNU8uQVw/GqI2XYG/V+CViA4IBiwns9LtV0ehBY3P99bSOL+9QPbAGi5tq/up +srnFNoutTJtEzRGmcbjoWEgCRglm/0IuTH4Iom4yKIfFjqFGs6pOX8miioZsCaxWNQf y9MzTneQQLrePWQRulg3HRtNvtJBrmzvzvooObwDssixwjbulUvnG0TJMrtgTCED3qKX COA4EmpyiprnpDYKLpys4vb1dW4q3pGV1OXcFH0AYrKEJx9NjccyXnd+Zs+1/Gn1agn/ uraA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433621; x=1763038421; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TeQ1qQssRNVDy6SgVbDGPxE08ddX4PJBvQHFLhXifeI=; b=JTIrVBKQvZeIV0hANR1r60g3wf3esmLI6hayXWI40wCBaeb+kHaNOGI+kHwIjk//Fb OYBAqfJOxY45C64x3GU95zh64JDG9HMZU1UWi6lj/SQ48aUFU16o50Oe+u2wekKEAtHt +Xo29z50/UxSBb2bK1qrgiofl+SAZAurHytfnWXO12F7HNteBrVY/I/iNTMR4SyYdtOT l40wH5JE1sPJZ6lPwbXZyIR5gOqPGkIjVICbblCQ9YRvSNCTKcE56sJtA0uWotA86V5m F3w84o7uUEyyKG6FIu4d31VNTsu8EdEWsAJwkVDD38lppDi3m2/CdnMpQIptDM1w51uu XIQQ== X-Forwarded-Encrypted: i=1; AJvYcCWHRiMnxpESKGjuCkan0YgNJwMIecRtnfyv+uShkRrs/6HwghGxz07Xtpvm1kgoeBI+Vaptqe3hZgn5AF0=@vger.kernel.org X-Gm-Message-State: AOJu0YztUxfp//+YzSmVZXIpDPrel9FOpXVLafz3pGK7PxYpq43U+rqd fvJ6t9Gh/j15NofmWkORA4UeXEPFDWSkS6r5lV2NQkJTNqPj+JiERDc= X-Gm-Gg: ASbGncuncQu1EdcVbuZ4gSJa9bLxmlGStmRg6Kt7LC5NRu9YjRMYEkDIAt617ZP4xp6 UfGvIUYBKEuYfGE0YIJkRuIrxa43wWNgu7pAsEjVExdlzHHWmQ64ye8hgNljg/HVDn0OxBiKk4g vK63NbmOoSVaUG27V8f/hWLfGgw5mIwI39hJYaL6Rt/+dySTrK61dRx8aZpzo0ZbZX/yMDMY+sW +eHDC9Dvmy9R6J5HYDRrcIcQhjvGAzkii54P4xQEKDU4ITZYHeLglN5hUiqa+WL2piPNFYkBcrZ zVg/1hfUe1F5GKyQvOUdKtYcGtOBOnxi2QbY+nz3QHrzLmRlBDGKjqPATYqjAqITZvCc2Eu+8CW 3B4W57qFIxXzlw6kWp8j+kkoyadWTJFEnc9xHezl3sceTZWPGTKIlozV+tlmxMI6m8KXqozFGzl iysvdM/lgI4I8mrdJHvs5SbFb6ohC8/EJ0WIur7BaCTSlaKXvLRqsnJJwNAO0w7uRGCg== X-Google-Smtp-Source: AGHT+IGmXXB3sYTtNVijmZK/dE1I0XwRMENPcN7ucBxdXqW1KCxYeITYB9d4TFOdh1oLXlDVTdzeLg== X-Received: by 2002:a05:6000:2304:b0:429:c505:8cb1 with SMTP id ffacd0b85a97d-429e3333c80mr6684600f8f.52.1762433620557; Thu, 06 Nov 2025 04:53:40 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:40 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 08/17] bpf: Track mov and signed extension Date: Thu, 6 Nov 2025 13:52:46 +0100 Message-Id: <20251106125255.1969938-9-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add `bcf_mov()` helpers to model MOV/cast operations in `bcf_track()` and a= dd=20 calls into ALU processing paths. The helpers extract the source to the targ= et width and apply zero/sign extension to the final width as needed. - For MOV32, build a 32-bit expr for src and zero-extend it. - For sign-extending MOVs (s8/s16 to W, or s8/s16 to R), extract the sub-wi= dth and sign-extend to 32 or 64. - If the destination is known-constant, clear `dst_reg->bcf_expr` (-1) to a= void carrying redundant symbolic nodes. These routines are only active when `env->bcf.tracking` is set. They are ca= lled from `check_alu_op()` in the relevant MOV/SEXT cases. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 69 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7b6d509c773a..4491d665cc49 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15959,6 +15959,57 @@ static int adjust_reg_min_max_vals(struct bpf_veri= fier_env *env, return 0; } =20 +static int bcf_mov(struct bpf_verifier_env *env, struct bpf_reg_state *dst= _reg, + struct bpf_reg_state *src_reg, u32 sz, bool bit32, bool sext) +{ + int src_expr, ext_sz, bitsz =3D bit32 ? 32 : 64; + + if (!env->bcf.tracking) + return 0; + if (tnum_is_const(dst_reg->var_off)) { + dst_reg->bcf_expr =3D -1; + return 0; + } + + src_expr =3D bcf_reg_expr(env, src_reg, bit32 || sz =3D=3D 32); + if (sz !=3D 32) /* u/s16 u/s8 */ + src_expr =3D bcf_extract(env, sz, src_expr); + + if (sext) { + ext_sz =3D bitsz - sz; + dst_reg->bcf_expr =3D + bcf_extend(env, ext_sz, bitsz, true, src_expr); + if (bit32) + bcf_zext_32_to_64(env, dst_reg); + } else { + ext_sz =3D 64 - sz; + dst_reg->bcf_expr =3D + bcf_extend(env, ext_sz, 64, false, src_expr); + } + if (dst_reg->bcf_expr < 0) + return dst_reg->bcf_expr; + + return 0; +} + +static int bcf_mov32(struct bpf_verifier_env *env, struct bpf_reg_state *d= st, + struct bpf_reg_state *src) +{ + return bcf_mov(env, dst, src, 32, true, false); +} + +static int bcf_sext32(struct bpf_verifier_env *env, struct bpf_reg_state *= dst, + struct bpf_reg_state *src, u32 sz) +{ + return bcf_mov(env, dst, src, sz, true, true); +} + +static int bcf_sext64(struct bpf_verifier_env *env, struct bpf_reg_state *= dst, + struct bpf_reg_state *src, u32 sz) +{ + return bcf_mov(env, dst, src, sz, false, true); +} + /* check validity of 32-bit and 64-bit arithmetic operations */ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *ins= n) { @@ -16084,8 +16135,12 @@ static int check_alu_op(struct bpf_verifier_env *e= nv, struct bpf_insn *insn) if (no_sext) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); - if (!no_sext) + if (!no_sext) { dst_reg->id =3D 0; + err =3D bcf_sext64(env, dst_reg, src_reg, insn->off); + if (err) + return err; + } coerce_reg_to_size_sx(dst_reg, insn->off >> 3); dst_reg->subreg_def =3D DEF_NOT_SUBREG; } else { @@ -16110,8 +16165,12 @@ static int check_alu_op(struct bpf_verifier_env *e= nv, struct bpf_insn *insn) * range otherwise dst_reg min/max could be incorrectly * propagated into src_reg by sync_linked_regs() */ - if (!is_src_reg_u32) + if (!is_src_reg_u32) { dst_reg->id =3D 0; + err =3D bcf_mov32(env, dst_reg, src_reg); + if (err) + return err; + } dst_reg->subreg_def =3D env->insn_idx + 1; } else { /* case: W1 =3D (s8, s16)W2 */ @@ -16120,8 +16179,12 @@ static int check_alu_op(struct bpf_verifier_env *e= nv, struct bpf_insn *insn) if (no_sext) assign_scalar_id_before_mov(env, src_reg); copy_register_state(dst_reg, src_reg); - if (!no_sext) + if (!no_sext) { dst_reg->id =3D 0; + err =3D bcf_sext32(env, dst_reg, src_reg, insn->off); + if (err) + return err; + } dst_reg->subreg_def =3D env->insn_idx + 1; coerce_subreg_to_size_sx(dst_reg, insn->off >> 3); } --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 028F92E8B6C for ; Thu, 6 Nov 2025 12:53:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433625; cv=none; b=p+lbbDfeDP+6ZGCS4+5b3qK0VcJD4diZFqlxqA0KYFbT1xntIQso+vbw/nUOYlN6ojDu0VKVo4zZeR0BEIkTThoV4qBtP0prxgDAN1bVMNobrnvir0+ndorTt86g6rt4P5jO6IM+cpoG2VAXlIIY4/3+CO7zN2HWYUksm67CTuQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433625; c=relaxed/simple; bh=+3bu+o7yrXof/2MI+bET1ey025h5hY27tO76ys+rXo8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=RdgD0bz0HWdbZMWwjSUPIHwKFVVaScQQ75jVNePR8Thh2ymtok3y064IG4lieazg9oanOJTstTghXtVOISBkUOzktYfd9DarqAjt2PoeiDlbmeUSqTy1DDrWR0l0L8ZYTpUepkSxKzmeO3Rj16hCXzR+C813Jmi5Q4qDLtPCW8A= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=P9pv/ail; arc=none smtp.client-ip=209.85.221.43 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.b="P9pv/ail" Received: by mail-wr1-f43.google.com with SMTP id ffacd0b85a97d-429eb7fafc7so651305f8f.2 for ; Thu, 06 Nov 2025 04:53:42 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433621; x=1763038421; 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; bh=SgIvWdywaJTgIXUxCBq7xXfTtWMf1izHesmgmA/pMTw=; b=P9pv/ail5t9GJeLjOLMdRswZwIyt/H58+iG9PAic1EBwaU4MhQTR7Hey5cMYdgNHic qlgSvkuFxBNNX0IIYGza3rMcqE/wXdIR3HiIhnbzTmWmqLkA4ZRRjho8eCg2+XLCowGN tCmmJ5uZ6JQ294ZpN5c8IGpUovVMKAKgo2Jtzh7h1UUBrNOyloAqNgm1NdYVbjJWyAYH d6vO8pPi8+JNuYDqNX0od5TFvZG41hvbEQFmTaL27opZso69yHsFxTjWezRmi1l8I0OP NZmCj6I1lHnk5Vt69edm3can3A7ihsgl9t1aGCw5ErLn8fGZJsl1Eq0xTNuGbAGOAwkh Gfdg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433621; x=1763038421; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SgIvWdywaJTgIXUxCBq7xXfTtWMf1izHesmgmA/pMTw=; b=WVthF+rywy5OoSjI1reh5iarVhgyFrsro7ubn77voUoEN7Smu+mW4dBjaKEtBNxm5h eEvr8kyYzhT3IDl1WCfugJ/QJa1v5T10EvXK0QpVoJAIjB4IYgatumNvQOiXJ+9dmCe6 SMfq0x2F3eZP1ruprtlkdmS7UdGDxMcDZonis6VZMFFertnLgp9sxbZyODJ2kJKO74AP D0Wj32usCRpHHuSZvTQatZWsqh+byzq/5k3Ku5ispPXk3G+jMYOBWO6F4tsVs2m4PvNC sTRHIk32/glsHMCZOFpwehlTy4efUHjENhxO1xjA8p3ZznQ63+m/aJYv0olJGpv638hW dE2g== X-Forwarded-Encrypted: i=1; AJvYcCUVdBu90+153XnKYEjdXpaYun3rQb5KS+p+JuvEcFFlZzpHVroH+H+MLh8Jp9NK8E90WqLsuti79BmuUZ8=@vger.kernel.org X-Gm-Message-State: AOJu0YxgMRC+BvawWXp9NBq+a7yZLnJN8VfrCwW7br0JIdDa93z6HEqj UV941JvBrT0QKssdHAcbYfjj6BuEagPjiAZrG7p9PfgjA/RM3lMX9OQ= X-Gm-Gg: ASbGncvFauIyhP4x40iAYLm+qq86xAK0gGgZGq/feRwDljhHsOXMDcwEPXoAYV4co7c 0hrdyKJMKFpJd717KINGNtEvJqKVZCKlu6nmZ1pG0osMy/Lrsm93O4IgNKZwrKGmDRwWxCvZKnG mqnDNJLXwDFhTANPUkidKWC/mBmViUzrSn0E+An+IvzlFNXUro3ILR9FVzeqRsyJkGm1wolFXZt P/lnSkLYJzWcPxSnUImncYXC9778joRtvhFzQJnybQ1F9bqGhB6FtF9KqFfHrX1XH0w3/AvREPG jrjqVs+KKV6KE5wxEx+WbbRS+Wx5nNg6aI/vJ9Xc6u32XQGl/Zxj7cFtPUuOYl8YI4Id8HywQfd /zHhamqZDql9/d8jASnWjxZQm0VlRjPrJ4Q24phhtMQRXpNIYxBplIz3o5UKb6MsPwAyuyVKtAp ZLkklIx+TZzKRTmvitHmNWAoB2+S1Jx4SklJFUAuMNBrZ22xb4dkukiBM= X-Google-Smtp-Source: AGHT+IGl6C6kdPOlCbRiSJYBIio8cezmiils5dPZ8ddwCv4SAqIpv33ItVTQPophXlCcShFEyPG7Bw== X-Received: by 2002:a05:6000:210c:b0:3e1:2d70:673e with SMTP id ffacd0b85a97d-429e3308422mr4649504f8f.37.1762433621174; Thu, 06 Nov 2025 04:53:41 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:40 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 09/17] bpf: Track alu operations in bcf_track() Date: Thu, 6 Nov 2025 13:52:47 +0100 Message-Id: <20251106125255.1969938-10-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Model scalar and pointer ALU operations in the symbolic tracking. - Scalar ALU: when either operand is non-constant, lazily bind `dst_reg` to= a symbolic expr and emit the BV op; If both operands are constants, rely on the verifier=E2=80=99s result and skip emitting a symbolic node. Achieved by hooking `adjust_scalar_min_max_vals()`, - Pointer ALU: follow verifier logic in `adjust_ptr_min_max_vals()` and rec= ord only the variable part into `dst_reg->bcf_expr`, carrying constants in `reg->off`. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 80 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4491d665cc49..66682d365e5e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14865,6 +14865,41 @@ static int sanitize_check_bounds(struct bpf_verifi= er_env *env, return 0; } =20 +static int bcf_alu(struct bpf_verifier_env *env, struct bpf_reg_state *dst= _reg, + struct bpf_reg_state *src_reg, u8 op, bool alu32) +{ + DEFINE_RAW_FLEX(struct bcf_expr, alu_expr, args, 2); + bool unary =3D (op =3D=3D BPF_NEG); + int dst, src =3D 0, bits; + + if (!env->bcf.tracking) + return 0; + if (tnum_is_const(dst_reg->var_off)) { + dst_reg->bcf_expr =3D -1; + return 0; + } + + dst =3D bcf_reg_expr(env, dst_reg, alu32); + if (!unary) + src =3D bcf_reg_expr(env, src_reg, alu32); + if (dst < 0 || src < 0) + return -ENOMEM; + + bits =3D alu32 ? 32 : 64; + alu_expr->code =3D BCF_BV | op; + alu_expr->vlen =3D unary ? 1 : 2; + alu_expr->params =3D bits; + alu_expr->args[0] =3D dst; + alu_expr->args[1] =3D src; + dst_reg->bcf_expr =3D bcf_add_expr(env, alu_expr); + if (alu32) + bcf_zext_32_to_64(env, dst_reg); + if (dst_reg->bcf_expr < 0) + return dst_reg->bcf_expr; + + return 0; +} + /* Handles arithmetic on a pointer and a scalar: computes new min/max and = var_off. * Caller should also handle BPF_MOV case separately. * If we return -EACCES, caller may want to try again treating pointer as a @@ -14872,12 +14907,12 @@ static int sanitize_check_bounds(struct bpf_verif= ier_env *env, */ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn *insn, - const struct bpf_reg_state *ptr_reg, - const struct bpf_reg_state *off_reg) + struct bpf_reg_state *ptr_reg, + struct bpf_reg_state *off_reg) { struct bpf_verifier_state *vstate =3D env->cur_state; struct bpf_func_state *state =3D vstate->frame[vstate->curframe]; - struct bpf_reg_state *regs =3D state->regs, *dst_reg; + struct bpf_reg_state *regs =3D state->regs, *dst_reg, *src_reg; bool known =3D tnum_is_const(off_reg->var_off); s64 smin_val =3D off_reg->smin_value, smax_val =3D off_reg->smax_value, smin_ptr =3D ptr_reg->smin_value, smax_ptr =3D ptr_reg->smax_value; @@ -14889,6 +14924,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, int ret, bounds_ret; =20 dst_reg =3D ®s[dst]; + src_reg =3D dst_reg =3D=3D ptr_reg ? off_reg : ptr_reg; =20 if ((known && (smin_val !=3D smax_val || umin_val !=3D umax_val)) || smin_val > smax_val || umin_val > umax_val) { @@ -14989,8 +15025,15 @@ static int adjust_ptr_min_max_vals(struct bpf_veri= fier_env *env, dst_reg->var_off =3D ptr_reg->var_off; dst_reg->off =3D ptr_reg->off + smin_val; dst_reg->raw =3D ptr_reg->raw; + dst_reg->bcf_expr =3D ptr_reg->bcf_expr; break; } + + if (env->bcf.tracking) { + bcf_reg_expr(env, dst_reg, false); + if (dst_reg->bcf_expr < 0) + return dst_reg->bcf_expr; + } /* A new variable offset is created. Note that off_reg->off * =3D=3D 0, since it's a scalar. * dst_reg gets the pointer type and since some positive @@ -15018,6 +15061,10 @@ static int adjust_ptr_min_max_vals(struct bpf_veri= fier_env *env, /* something was added to pkt_ptr, set range to zero */ memset(&dst_reg->raw, 0, sizeof(dst_reg->raw)); } + + ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false); + if (ret) + return ret; break; case BPF_SUB: if (dst_reg =3D=3D off_reg) { @@ -15046,8 +15093,15 @@ static int adjust_ptr_min_max_vals(struct bpf_veri= fier_env *env, dst_reg->id =3D ptr_reg->id; dst_reg->off =3D ptr_reg->off - smin_val; dst_reg->raw =3D ptr_reg->raw; + dst_reg->bcf_expr =3D ptr_reg->bcf_expr; break; } + + if (env->bcf.tracking) { + bcf_reg_expr(env, dst_reg, false); + if (dst_reg->bcf_expr < 0) + return dst_reg->bcf_expr; + } /* A new variable offset is created. If the subtrahend is known * nonnegative, then any reg->range we had before is still good. */ @@ -15075,6 +15129,10 @@ static int adjust_ptr_min_max_vals(struct bpf_veri= fier_env *env, if (smin_val < 0) memset(&dst_reg->raw, 0, sizeof(dst_reg->raw)); } + + ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false); + if (ret) + return ret; break; case BPF_AND: case BPF_OR: @@ -15728,7 +15786,7 @@ static int adjust_scalar_min_max_vals(struct bpf_ve= rifier_env *env, { u8 opcode =3D BPF_OP(insn->code); bool alu32 =3D (BPF_CLASS(insn->code) !=3D BPF_ALU64); - int ret; + int ret, dst_expr =3D dst_reg->bcf_expr; =20 if (!is_safe_to_compute_dst_reg_range(insn, &src_reg)) { __mark_reg_unknown(env, dst_reg); @@ -15741,6 +15799,14 @@ static int adjust_scalar_min_max_vals(struct bpf_v= erifier_env *env, return sanitize_err(env, insn, ret, NULL, NULL); } =20 + /* Constants alu produces constant, skip it; otherwise, bind expr. */ + if (env->bcf.tracking && (!tnum_is_const(dst_reg->var_off) || + !tnum_is_const(src_reg.var_off))) { + dst_expr =3D bcf_reg_expr(env, dst_reg, false); + if (dst_expr < 0) + return dst_expr; + } + /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops. * There are two classes of instructions: The first class we track both * alu32 and alu64 sign/unsigned bounds independently this provides the @@ -15819,6 +15885,12 @@ static int adjust_scalar_min_max_vals(struct bpf_v= erifier_env *env, if (alu32) zext_32_to_64(dst_reg); reg_bounds_sync(dst_reg); + + dst_reg->bcf_expr =3D dst_expr; + ret =3D bcf_alu(env, dst_reg, &src_reg, opcode, alu32); + if (ret) + return ret; + return 0; } =20 --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BB75F330335 for ; Thu, 6 Nov 2025 12:53:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433626; cv=none; b=j2OOCzP0PVidCA74tbs+4Y3RX+JY6mR9nVx5aXdlLT9GnTTSoTC50QlE9ed0Yc5kUcNK21u44dP2jkqa8zsIQ34IqeVRi+dYOXguwp217O62ubmqgCKqTMwtO4oNXbCa7RTNCn9dolTIXi+jyb7KFSeTKtdSrlab6/ZjhFQUamE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433626; c=relaxed/simple; bh=bcHRGOqbqzzhPFu+ojNR6wxaw6xjv4kXNLSndDc36ME=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Ua2oyyLnnojIuDbLqAc4FcCTuj6TM4+wKmjQtxmVEXTxoqvyF9hI4Hfn/R/NcTZ5Vv/+CoNd9Qs1yKppjyWRscRwCwfpMg1UfaK4cJIvnW3o1ALFrsIYeuazozsu3j1Uyo7rbaSBhcSXDszK7SphXP+3w5lBR4GfABu6YWejbVw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=f85kQUa5; arc=none smtp.client-ip=209.85.221.49 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.b="f85kQUa5" Received: by mail-wr1-f49.google.com with SMTP id ffacd0b85a97d-429eb7fafc7so651313f8f.2 for ; Thu, 06 Nov 2025 04:53:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433622; x=1763038422; 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; bh=DjCst0RmwR8vxuLUXqKmfS8QPtIrCS1B+n9Hqasdjnw=; b=f85kQUa5iJSxf0Et/ff+m7JLvsgQzLhg2mQongDbZuAGXkCAUW//misPdEBKj4hOes kc5h4kTctIZBVISZ2MY7y2EOQLe0AAnu/VFSXlQhISObgOIdQt9V52zxAzVXrmJm+/Gv y2dlS/LSBB6SqaQhvJpd72gfY037Zc5oksQhJy+SdvOS53Fvoq4y6SFnkVHtw14lYFvd 6ONOUcRXdLlWUrC/yWZhdbob5iaMBl+xDR7D/sh8hzR7gu2lMo8z3HVXI0yaQm3BW36z wiurrTWyqwzSBsQFZZP0f9lVxBM/iFDGN3Ca7BRw0yyMIhRDNLSdTAYvcWsx36c1gWCI 1xWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433622; x=1763038422; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DjCst0RmwR8vxuLUXqKmfS8QPtIrCS1B+n9Hqasdjnw=; b=atY4In1PmojG+kQmoF6ze5Nxs1GJ2mj/9R+/EGEhpgbvs7d6ENg3tzqAo/g/85cJq8 EWvFD2nc3iRX3BN+fSQ+cPvbG/B4Ikwl9V0kPVqzqr5yWeWouJSL5YjAIGzrulb03/oo 0uEHKQLtI2tO/pKkMeuQ27XN+4nE2FLSaKLyWkcVh6bVTDFmgcdJDCF3C9Z1bBpcAVH9 fg0rgw9YXY+nFS15IWeXcnFiXceKZwG2g7ndYc2EMfWCnQxWj/glPw3y/A9rK3buPb1+ MmIWmU2mFbIczTyPne5KkkznzuNNUgodR1IWTBVdg6tm+5tU66yeNyAiEByQpmHXeZA1 syzw== X-Forwarded-Encrypted: i=1; AJvYcCUJCiSqUVXSzYU08PVslMDvM6+GLUmoAKGIXhQFG5pZKP+/kqyrUs7j/fMqrNqsDXA9J3mffKO0SEJsgPo=@vger.kernel.org X-Gm-Message-State: AOJu0YwQafqgcqt5Y9r2TTK+4QC/NSxCno4EyuRPgiO5IfIdgyxvWwYW 1R+1Qd4xh5Dw3doIppYvoFx03y39IrJeXTLlwWqX1jnv7IO6/w27GKY= X-Gm-Gg: ASbGncsdyuHwh6YX7Y243aBcDRZ2+ulXQxPLgSuK/uy1dEnj1SfmeOTm2+sFjBz66G3 wWgoaNIo2bXOeJsSfrGrBzY6dlpG1zBn1nlsG/r5YFEEKeToZnlaVan+C3+KwkslqqjnbOr+Yq8 XnNiYtaTJqK2+tak0gGmpBWrypAow3gQjx0HEvixMx3DK0v+BZE2/k6c1DyYeoNPz9+nPTcKsF2 oULJIw3Hzl3BApBaMbKb8Hsbb6+QpBDoRayccIFAuPYeF0LhF/wi37Q+1XlYzXUZfnyoc4RqifY d6/4icDbzkCBCQFPoSjiw/KbRI/V7qEi16pOQmwoP+nZV34AwtrCHH6d+dZfFu95jXTqUqLT03e NyCW6tW163wSkmGTdae5kpCF9POI3CNUtp43wa83wkUQgHyPcAarwwPr5+lBKboktO2nmPRmaWY molIWsLO9WpbrzYHGuqCMU+LACP03lQbYekUUzd/EviloC2G5fpO7jv+Q= X-Google-Smtp-Source: AGHT+IF59d5GYYpx4VOKuwUgzJ5BNLyqt4IadFejfqDnuxk1Y2oRGUxA032fi9ljWUpPNLgprK4b7A== X-Received: by 2002:a05:6000:220d:b0:426:ff2f:9c15 with SMTP id ffacd0b85a97d-429e32c8784mr7110253f8f.5.1762433621744; Thu, 06 Nov 2025 04:53:41 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:41 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 10/17] bpf: Add bcf_alu() 32bits optimization Date: Thu, 6 Nov 2025 13:52:48 +0100 Message-Id: <20251106125255.1969938-11-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Lower symbolic ALU nodes to 32-bit when both operands and the resulting dst fit in 32 bits, to reduce solver/proof complexity. - Extend `bcf_alu()` with `op_u32`/`op_s32` hints and derive a `zext` decis= ion: when ALU32 or both operands/results fit u32, emit a 32-bit op and zero-ex= tend to 64; when signed-32 is in effect, sign-extend to 64 after the op. - Compute `op_u32`/`op_s32` for pointer and scalar ALUs (using fit_u32/fit_= s32) before emitting the node, then mask them again with the post-ALU dst rang= e so the final node width reflects the verifier=E2=80=99s bounds. This shrinks many BV nodes and helps keep per-node vlen within limits (U8_M= AX), reducing proof size. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 66682d365e5e..df6d16a1c6f6 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -14866,11 +14866,13 @@ static int sanitize_check_bounds(struct bpf_verif= ier_env *env, } =20 static int bcf_alu(struct bpf_verifier_env *env, struct bpf_reg_state *dst= _reg, - struct bpf_reg_state *src_reg, u8 op, bool alu32) + struct bpf_reg_state *src_reg, u8 op, bool alu32, + bool op_u32, bool op_s32) { DEFINE_RAW_FLEX(struct bcf_expr, alu_expr, args, 2); bool unary =3D (op =3D=3D BPF_NEG); int dst, src =3D 0, bits; + bool zext =3D alu32 || op_u32; =20 if (!env->bcf.tracking) return 0; @@ -14879,6 +14881,7 @@ static int bcf_alu(struct bpf_verifier_env *env, st= ruct bpf_reg_state *dst_reg, return 0; } =20 + alu32 |=3D (op_u32 || op_s32); dst =3D bcf_reg_expr(env, dst_reg, alu32); if (!unary) src =3D bcf_reg_expr(env, src_reg, alu32); @@ -14892,8 +14895,11 @@ static int bcf_alu(struct bpf_verifier_env *env, s= truct bpf_reg_state *dst_reg, alu_expr->args[0] =3D dst; alu_expr->args[1] =3D src; dst_reg->bcf_expr =3D bcf_add_expr(env, alu_expr); - if (alu32) + if (zext) bcf_zext_32_to_64(env, dst_reg); + else if (op_s32) + bcf_sext_32_to_64(env, dst_reg); + if (dst_reg->bcf_expr < 0) return dst_reg->bcf_expr; =20 @@ -14922,6 +14928,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, u8 opcode =3D BPF_OP(insn->code); u32 dst =3D insn->dst_reg; int ret, bounds_ret; + bool op_u32, op_s32; =20 dst_reg =3D ®s[dst]; src_reg =3D dst_reg =3D=3D ptr_reg ? off_reg : ptr_reg; @@ -15034,6 +15041,8 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, if (dst_reg->bcf_expr < 0) return dst_reg->bcf_expr; } + op_u32 =3D fit_u32(dst_reg) && fit_u32(src_reg); + op_s32 =3D fit_s32(dst_reg) && fit_s32(src_reg); /* A new variable offset is created. Note that off_reg->off * =3D=3D 0, since it's a scalar. * dst_reg gets the pointer type and since some positive @@ -15062,7 +15071,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, memset(&dst_reg->raw, 0, sizeof(dst_reg->raw)); } =20 - ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false); + op_u32 &=3D fit_u32(dst_reg); + op_s32 &=3D fit_s32(dst_reg); + ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false, op_u32, op_s32); if (ret) return ret; break; @@ -15102,6 +15113,8 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, if (dst_reg->bcf_expr < 0) return dst_reg->bcf_expr; } + op_u32 =3D fit_u32(dst_reg) && fit_u32(src_reg); + op_s32 =3D fit_s32(dst_reg) && fit_s32(src_reg); /* A new variable offset is created. If the subtrahend is known * nonnegative, then any reg->range we had before is still good. */ @@ -15130,7 +15143,9 @@ static int adjust_ptr_min_max_vals(struct bpf_verif= ier_env *env, memset(&dst_reg->raw, 0, sizeof(dst_reg->raw)); } =20 - ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false); + op_u32 &=3D fit_u32(dst_reg); + op_s32 &=3D fit_s32(dst_reg); + ret =3D bcf_alu(env, dst_reg, src_reg, opcode, false, op_u32, op_s32); if (ret) return ret; break; @@ -15787,6 +15802,7 @@ static int adjust_scalar_min_max_vals(struct bpf_ve= rifier_env *env, u8 opcode =3D BPF_OP(insn->code); bool alu32 =3D (BPF_CLASS(insn->code) !=3D BPF_ALU64); int ret, dst_expr =3D dst_reg->bcf_expr; + bool op_u32, op_s32; =20 if (!is_safe_to_compute_dst_reg_range(insn, &src_reg)) { __mark_reg_unknown(env, dst_reg); @@ -15806,6 +15822,8 @@ static int adjust_scalar_min_max_vals(struct bpf_ve= rifier_env *env, if (dst_expr < 0) return dst_expr; } + op_u32 =3D fit_u32(dst_reg) && fit_u32(&src_reg); + op_s32 =3D fit_s32(dst_reg) && fit_s32(&src_reg); =20 /* Calculate sign/unsigned bounds and tnum for alu32 and alu64 bit ops. * There are two classes of instructions: The first class we track both @@ -15887,7 +15905,9 @@ static int adjust_scalar_min_max_vals(struct bpf_ve= rifier_env *env, reg_bounds_sync(dst_reg); =20 dst_reg->bcf_expr =3D dst_expr; - ret =3D bcf_alu(env, dst_reg, &src_reg, opcode, alu32); + op_u32 &=3D fit_u32(dst_reg); + op_s32 &=3D fit_s32(dst_reg); + ret =3D bcf_alu(env, dst_reg, &src_reg, opcode, alu32, op_u32, op_s32); if (ret) return ret; =20 --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 66604337B8A for ; Thu, 6 Nov 2025 12:53:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433626; cv=none; b=NEn79sqrGRtmgLgjpzrHb7po6qny/q47B3P1CJgQmIAKEJGInSn9j9VSeVbmc7jI42bXyd6mZ9aq2grRWVXK8rttR6SvGYPHZOrhIb4y4KbzFaikezeU8tkAGg9rhOyP0lNXtiQGKwAJ4ABcFiFEzd9l8qOw4Ood7cUmajy0krA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433626; c=relaxed/simple; bh=7aRplSUW6kRxQPV1HlG/HN4UwlXrr6wHRd070gm8Gy4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tOhuQ9eaUZSfYpaqEaXYnw4A7m8uiA7WE7QWTFGVUV4FBxP6RL+P15GDYbZ+INTyR+e/cnCXumYpGXvcq2lfpl0uQ3LlNUCvuh4wrwQ8Vg9G17kPnyMcOHoFoG3+i4o8yxVpz8njy0k1gY+fOnkOaSmsqkdKXdLBcArvE/dZGGc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FIC/eUSF; arc=none smtp.client-ip=209.85.221.47 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.b="FIC/eUSF" Received: by mail-wr1-f47.google.com with SMTP id ffacd0b85a97d-3ecdf2b1751so643270f8f.0 for ; Thu, 06 Nov 2025 04:53:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433622; x=1763038422; 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; bh=o3F/6Pnt47lpVjXos7n4gvnzZgK+tzG4a7nXCFmIfgo=; b=FIC/eUSFRkr68T3/WrgHjrvM1/YhYI/DYHAjvefGMgJktSb/zwNXg+7BQjHvkYKWqq 1JYxNOpTzZm7I00RSs1qYUSFv51TdDK6cutj0x3HW6SdQmd+9LVWwHCnoFTR+OgKHGfJ EOywxGWAYX5JEdRgNapQbhpld0zFbKEaXPm24jBs5+fi0cO3umLwP6P3FZIO+syfrNhm ScxEh6PKz5e/C83KXYwhHsrxotC1//MLpF+Tbn4JNmVLKdUc6FXEhL5QYz7G3FIlUP7s uhrZRzfC20KDWdw5Yvw21iTo/CxTNCFX2PI94C29ryCQ9peC+u/x0KJ9dLZcrip2N4Uw 6uzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433622; x=1763038422; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=o3F/6Pnt47lpVjXos7n4gvnzZgK+tzG4a7nXCFmIfgo=; b=mXds5BNVqX/WHZ/cHHdgzm70ubnxoN5H7FadZoXVNa+yTD6ZcORTB5vXmZPtlfrV2W 5/qeoxMur1kZuOJ4xMRY1T3i9J6M4PqwXhE8VW7Zi0OYG9rDUBIMpRDDwnatrVjlW++X sP7FQqYZ0PFtJG4/2D+Ce8DepOjcmz2EBIdDVpCCKl5dS2+FVAzyBB8nKKwPjWu0r0PB eWi2WmwaOzl7Wb0hylg4EPCFcBvF4S0BoJvSlnK6SvkBolzJwfdp4+yoJ0/RxXC56e6b k5XsGJmXDvLp1uUPmhX/00JH1mRi/ziSVKBBhvo5dCAbCnUokXlhRRg7VuVDttK4T11P 8QKQ== X-Forwarded-Encrypted: i=1; AJvYcCXrJU65k5P/XP5axYL5ZTKq9qLcyCJW6+HRxNBEMMkUtdqpjfH1sClxbuv9BUtSQoownbwJoig3MxOaKKs=@vger.kernel.org X-Gm-Message-State: AOJu0YwHVcWsU9ctzcH9oYSOW3h29veyah8789a/gzUShx0eaRRd+Gq7 LIybDzZBRAbDXe/q3HveldqzzwHBuzl8OuIdUTOjCanspwbkdwkWqSM= X-Gm-Gg: ASbGncvqCmOXhBD9GlZ/iEwWI69rlpLM+tCk+QR5pPTKZhw3ie8UdEufOvw5yZ6qrOx IiTWQjsLHRzouo1hYVeLsoeD8waV3Q4VSjw66ExRt3Juntn3oAhnzfpOXW0hYANeZTbidDPHwDR BY52wJWeE4vO2ALM95Q9JQdbRk/q8iVl07fr5UmlipMl1vaV9UTNJucsTGK6PwEeSXaJ3HDA/We TuxliXL8RZoa1TnvNqe0AP3rgsDfhamdpM2pvCstui7sNDOmxSFqyiGeRjB7nAxCYg9OiGq3LPX o8lhgZw9Pmy0/bPpmKAEXIISRGtt7prZRqZQ257z5jlXKAKd0/GDx9TjBhQ7rLDUIuoYvySw4R0 JrEF9L2H0vmWkIuxW6/RPQ//LP31LVC+2lyA7L+D5qUdM88VCIwJHyTL6NZWqFKz2NfPyZuUdfy FkJP7hr38t5c8MNS7TIr2/JQnAYTpbfom37JSWbBaa5gnjBRhM7N2Kl/Q= X-Google-Smtp-Source: AGHT+IE5q5ICdDDBK7g4wnCXX0jnjR/QAHVpDJVspbKXccSBPyw3RSDN+ilC4Bc2cp04IBzrsLidPg== X-Received: by 2002:a05:6000:615:b0:425:7cf6:5b9e with SMTP id ffacd0b85a97d-429e32c831amr6689558f8f.3.1762433622302; Thu, 06 Nov 2025 04:53:42 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:42 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 11/17] bpf: Track stack spill/fill in bcf_track() Date: Thu, 6 Nov 2025 13:52:49 +0100 Message-Id: <20251106125255.1969938-12-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Track symbolic values across stack spills/fills. - On spill (`save_register_state()`), if tracking and the source is non-constant and the spill is narrower than a full register, derive the appropriate-width for the spill slot using `bcf_mov()`. - On data writes to stack slots (`check_stack_write_*`), invalidate any previously spilled pointer symbolic value by setting `spilled_ptr.bcf_exp= r` to -1 when the slot is clobbered. - On loads that coerce value register sizes (`check_mem_access()`), if the destination already has a symbolic expr, cast it to the loaded size via `bcf_mov()`. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index df6d16a1c6f6..3f2981db1d40 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5286,6 +5286,10 @@ static void copy_register_state(struct bpf_reg_state= *dst, const struct bpf_reg_ *dst =3D *src; } =20 +static int bcf_mov(struct bpf_verifier_env *env, struct bpf_reg_state *dst= _reg, + struct bpf_reg_state *src_reg, u32 sz, bool bit32, + bool sext); + static void save_register_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int spi, struct bpf_reg_state *reg, @@ -5295,6 +5299,11 @@ static void save_register_state(struct bpf_verifier_= env *env, =20 copy_register_state(&state->stack[spi].spilled_ptr, reg); =20 + if (env->bcf.tracking && !tnum_is_const(reg->var_off) && + size !=3D BPF_REG_SIZE) + bcf_mov(env, &state->stack[spi].spilled_ptr, reg, size * 8, + false, false); + for (i =3D BPF_REG_SIZE; i > BPF_REG_SIZE - size; i--) state->stack[spi].slot_type[i - 1] =3D STACK_SPILL; =20 @@ -5437,6 +5446,7 @@ static int check_stack_write_fixed_off(struct bpf_ver= ifier_env *env, =20 /* regular write of data into stack destroys any spilled ptr */ state->stack[spi].spilled_ptr.type =3D NOT_INIT; + state->stack[spi].spilled_ptr.bcf_expr =3D -1; /* Mark slots as STACK_MISC if they belonged to spilled ptr/dynptr/iter.= */ if (is_stack_slot_special(&state->stack[spi])) for (i =3D 0; i < BPF_REG_SIZE; i++) @@ -5566,6 +5576,7 @@ static int check_stack_write_var_off(struct bpf_verif= ier_env *env, =20 /* Erase all other spilled pointers. */ state->stack[spi].spilled_ptr.type =3D NOT_INIT; + state->stack[spi].spilled_ptr.bcf_expr =3D -1; =20 /* Update the slot type. */ new_type =3D STACK_MISC; @@ -8025,6 +8036,11 @@ static int check_mem_access(struct bpf_verifier_env = *env, int insn_idx, u32 regn coerce_reg_to_size(®s[value_regno], size); else coerce_reg_to_size_sx(®s[value_regno], size); + + if (env->bcf.tracking && regs[value_regno].bcf_expr >=3D 0) + err =3D bcf_mov(env, ®s[value_regno], + ®s[value_regno], size * 8, false, + is_ldsx); } return err; } --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f41.google.com (mail-wr1-f41.google.com [209.85.221.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A49A0337BBA for ; Thu, 6 Nov 2025 12:53:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433627; cv=none; b=nrBNFhYpg7N0gj8sZyN6i/xnoZ7NGkHXBb8SN0jr1m7AN2+5suxxJVpoUKJr5/x/zYEPyKWaJNt9DU8sl61ccyyFQPtt/nD+ftaAubHamZAQ6JTFFAI8mT9qISj76MpgxM3VDA9mdTpenVMLp0y8GrV8FZjFBYdjEebMwOSyh2I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433627; c=relaxed/simple; bh=VcMIYDcgbIp7gzck2lmzT1DdYedQDnPc1OGCfRsQ8Rw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TtDtnNRQwOy5o3tAVpgkZyQJo0zzyRxnJ89+Xb57/kEhi4b/42OmebJyBTh5UQLdMV5jXMGeEDn5VMd2wA9kS1yFLxqr4tMG3ZPvo9jo/tpx8zo2WRPn5bEaXdAOpbmwiEb24i0x9AAsFk4E4TKSJ2J/h7YXEdA6dfPoVS+eq5E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Wg1bueW+; arc=none smtp.client-ip=209.85.221.41 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.b="Wg1bueW+" Received: by mail-wr1-f41.google.com with SMTP id ffacd0b85a97d-42421b1514fso516602f8f.2 for ; Thu, 06 Nov 2025 04:53:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433623; x=1763038423; 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; bh=uAHXlerzYNMFVMdi3HCj0A57dbSsWmdWDXdM/zHlcgQ=; b=Wg1bueW+3QVsiD1y43hZKpA9SiKfrNpOG1yhK41STqCEKrv/lZ1/Vp9hEeze79znuh tox5FTE52Z3H3aQKgwQtr/r6WG/cqLzzu8tu4JdzP/XnBglw4uVWy3E4pQI4tWiwKiN7 2ny7j7ZT1Sg4hmag9Q2WRSFu9EPCcN6l1QQWnzBHHO4Y0RYqiM3JNP+LkurMTuKSZetH j25fKmYZIB/Sd/96xt7sFDwZ/Gen0fWfABbi9A3psimBIH/wZpuNFzSr44pHks9Kg2wk ESOLWWravQ3QhbgmhGKF0QmsbFVSA3e7zjcbxUr59K5p74Emv9lTOfecbF8WtPEfs3rE U+oQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433623; x=1763038423; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uAHXlerzYNMFVMdi3HCj0A57dbSsWmdWDXdM/zHlcgQ=; b=AtiWiCHl7d+SQmUAshCXwDM0p1wgvMnwMqcJ4MrGgqYLqyGjCuv8GbKTmW+DxUO3h7 y7lQAmvCs0kdamHuezFMUk1HclYo3lcrTzV8pegZCRkLz1tNZd9PXV3V8OavHKODNxjC Ns4AoJ2jZX32i2EHNf0gAMvCsis0Nv9635KshO0oemUTOZb/K4d4xj+sjKTON09biyHC 0nuqtFqnofwR9Jke8fajcjTvJT0kcvLr3SeVxkb1sDsAYxarblcNb/yxvpFivgno4Q1l c3UKBD87SBH8DgtZIjGNOsAw4cNX40eoIhsRmhmWrPaSOC4nZ8J7KfNzBtsVjRV1xaWE Tsjw== X-Forwarded-Encrypted: i=1; AJvYcCUDLoFtN1rq+ibuZM1624PMzjatQLjwv0dYjee1epAq15+mL/1xYKjz2FVrRpZ/iRCpBbiwt3O2snvHeRQ=@vger.kernel.org X-Gm-Message-State: AOJu0YxbdLLsRKoejtbnJNLRtoZj1JciK/jdkM9qEEGAhGFGgoY0EyGF PRrzFf0xHZR4wzD91Y8DO52JONVRdHJu429VFDce6SpfOm+v0iFcFe0= X-Gm-Gg: ASbGncupIfq9bcPlUvotMfrdnF6N71nU28bWMbn9STsm7/GPzrmS9uO4kszHmPIkOw7 vnuieV6KykZ6P16fF5+rVcUhHcQg+lXZh71j7EIY51oCMIhgWJ4bkiQHCEc1Wn4eAww8Kfxf2/t 5f2LjXpyFZxT86qOadv3LdkfzWzuFt/rBse7KFpHFGqr+fl3n30YUk7Y9+rn90LTtSndBshQem9 6H1wBS94N80+UKbRjhvIryxmcj34TQwQ6SrEEdw1mTO4d0Y0jmk6ar6WeMX8GNmKODgAMs5Kz2j mLKf798qGZb5+bi8DCEPXRX4nJeQhRRgmUg/buekmbkq3ULsFpZ4S5GQcJPIlqNzE9JnhAxLYUJ J8K4TOee7t3+TB9d/bIvGYPJEG/StVB0VxEzWBci3sWhylElgYSBB6RcFql3N2sa6qwObVaW8LG hYwq4zzm8AnVbXO76ODYEg+A40OUhmDnAAkq6xapIvjCZwnUlQWqo0SuU= X-Google-Smtp-Source: AGHT+IEpSkzz80Ete4RN99YnH1gQLlKEKMFwhAKzS6oQ8PF5tpjR7FJdfvzr60LpAUuxBGi+08V9xA== X-Received: by 2002:a5d:5f87:0:b0:429:bc93:9d8a with SMTP id ffacd0b85a97d-429e3307958mr6150736f8f.37.1762433622868; Thu, 06 Nov 2025 04:53:42 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:42 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 12/17] bpf: Track path constraint Date: Thu, 6 Nov 2025 13:52:50 +0100 Message-Id: <20251106125255.1969938-13-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Record per-branch conditions during `bcf_track()` and build a single conjunction to represent the path suffix constraint. - Add `record_path_cond()`: after processing each instruction under trackin= g, examine the previous instruction; if it is a conditional jump over scalar= s, construct a boolean condition that matches the taken/not-taken edge, and = then append the condition id to `env->bcf.br_conds`. - When tracking completes, if there are recorded conditions, build `env->bcf.path_cond` as either the single condition or a BCF_BOOL|BCF_CONJ of all collected conditions. - In `bcf_refine()`, if both `path_cond` and a refinement-specific `refine_cond` exist, combine them via a 2-ary conjunction so userspace pr= oves exactly the path-specific condition. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 113 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3f2981db1d40..f1e8e70f9f61 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -20589,6 +20589,70 @@ static int bcf_match_path(struct bpf_verifier_env = *env) return PATH_MATCH; } =20 +static int record_path_cond(struct bpf_verifier_env *env) +{ + int prev_insn_idx =3D env->prev_insn_idx; + struct bpf_reg_state *regs =3D cur_regs(env); + struct bpf_reg_state *dst, *src; + int dst_expr, src_expr; + struct bpf_insn *insn; + u8 class, op, bits; + bool jmp32, non_taken; + int cond_expr; + + if (prev_insn_idx < 0) + return 0; + + insn =3D &env->prog->insnsi[prev_insn_idx]; + class =3D BPF_CLASS(insn->code); + op =3D BPF_OP(insn->code); + if (class !=3D BPF_JMP && class !=3D BPF_JMP32) + return 0; + if (op =3D=3D BPF_CALL || op =3D=3D BPF_EXIT || op =3D=3D BPF_JA || op = =3D=3D BPF_JCOND) + return 0; + if (insn->off =3D=3D 0) + return 0; + + dst =3D regs + insn->dst_reg; + src =3D regs + insn->src_reg; + if (BPF_SRC(insn->code) =3D=3D BPF_K) { + src =3D &env->fake_reg[0]; + memset(src, 0, sizeof(*src)); + src->type =3D SCALAR_VALUE; + __mark_reg_known(src, insn->imm); + } + if (dst->type !=3D SCALAR_VALUE || src->type !=3D SCALAR_VALUE) + return 0; + + jmp32 =3D (class =3D=3D BPF_JMP32); + bits =3D jmp32 ? 32 : 64; + dst_expr =3D bcf_reg_expr(env, dst, jmp32); + src_expr =3D bcf_reg_expr(env, src, jmp32); + if (dst_expr < 0 || src_expr < 0) + return -ENOMEM; + + non_taken =3D (prev_insn_idx + 1 =3D=3D env->insn_idx); + if (op =3D=3D BPF_JSET) { + int and_expr, zero_expr; + + and_expr =3D bcf_build_expr(env, BCF_BV | BPF_AND, bits, 2, + dst_expr, src_expr); + zero_expr =3D bcf_val(env, 0, jmp32); + op =3D BPF_JNE; + if (non_taken) + op =3D BPF_JEQ; + cond_expr =3D bcf_build_expr(env, BCF_BOOL | op, 0, 2, + and_expr, zero_expr); + } else { + if (non_taken) + op =3D rev_opcode(op); + cond_expr =3D bcf_build_expr(env, BCF_BOOL | op, 0, 2, dst_expr, + src_expr); + } + + return bcf_add_cond(env, cond_expr); +} + static int do_check(struct bpf_verifier_env *env) { bool pop_log =3D !(env->log.level & BPF_LOG_LEVEL2); @@ -20656,8 +20720,9 @@ static int do_check(struct bpf_verifier_env *env) =20 if (path =3D=3D PATH_MISMATCH) goto process_bpf_exit; - else if (path =3D=3D PATH_DONE) - return 0; + err =3D record_path_cond(env); + if (err || path =3D=3D PATH_DONE) + return err; } =20 if (signal_pending(current)) @@ -24023,11 +24088,37 @@ static int bcf_track(struct bpf_verifier_env *env, if (!err && !same_callsites(env->cur_state, st)) err =3D -EFAULT; =20 - if (!err) { - tracked_regs =3D cur_regs(env); - for (i =3D 0; i < BPF_REG_FP; i++) - regs[i].bcf_expr =3D tracked_regs[i].bcf_expr; + if (err) + goto out; + + tracked_regs =3D cur_regs(env); + for (i =3D 0; i < BPF_REG_FP; i++) + regs[i].bcf_expr =3D tracked_regs[i].bcf_expr; + + /* Build the path constraint. */ + if (bcf->br_cond_cnt =3D=3D 1) { + bcf->path_cond =3D *bcf->br_conds; + } else if (bcf->br_cond_cnt > 1) { + struct bcf_expr *cond_expr; + int cond; + + cond =3D bcf_alloc_expr(env, bcf->br_cond_cnt + 1); + if (cond < 0) { + err =3D cond; + goto out; + } + cond_expr =3D bcf->exprs + cond; + cond_expr->code =3D BCF_BOOL | BCF_CONJ; + cond_expr->params =3D 0; + cond_expr->vlen =3D bcf->br_cond_cnt; + memcpy(cond_expr->args, bcf->br_conds, + sizeof(u32) * bcf->br_cond_cnt); + bcf->path_cond =3D cond; } +out: + kfree(bcf->br_conds); + bcf->br_conds =3D NULL; + bcf->br_cond_cnt =3D 0; =20 free_verifier_state(env->cur_state, true); env->cur_state =3D NULL; @@ -24134,6 +24225,7 @@ static int __used bcf_refine(struct bpf_verifier_en= v *env, refine_state_fn refine_cb, void *ctx) { struct bpf_reg_state *regs =3D st->frame[st->curframe]->regs; + struct bcf_refine_state *bcf =3D &env->bcf; struct bpf_verifier_state *base; int i, err; =20 @@ -24168,6 +24260,15 @@ static int __used bcf_refine(struct bpf_verifier_e= nv *env, if (!err && refine_cb) err =3D refine_cb(env, st, ctx); =20 + /* The final condition is the conj of path_cond and refine_cond. */ + if (!err && bcf->refine_cond >=3D 0 && bcf->path_cond >=3D 0) { + bcf->refine_cond =3D bcf_build_expr(env, BCF_BOOL | BCF_CONJ, 0, + 2, bcf->path_cond, + bcf->refine_cond); + if (bcf->refine_cond < 0) + err =3D bcf->refine_cond; + } + if (!err && (env->bcf.refine_cond >=3D 0 || env->bcf.path_cond >=3D 0)) mark_bcf_requested(env); =20 --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f53.google.com (mail-wr1-f53.google.com [209.85.221.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 792A2338F26 for ; Thu, 6 Nov 2025 12:53:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433627; cv=none; b=BxTcUe9CUKXxmKt2uzp4K+Hb4IOPnmboZ5YSAW67qmJRH1276XEGnT8MdWJj9HxC0bgOI9xy+itA/yDWSZfxgWiHVDvu6tRfg3jFyuSA9n9SNsrBemcdjZwYx1mY+L32uddDNEkihnFxLKA8+oFAzQvwG/JlYsVZf77PY7LI25I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433627; c=relaxed/simple; bh=ic+gOBYrHkh39eOmG4GQwcvbU3ifr1hE78B2se07zwc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=OtcaFT+ZoQxX09eb8Q9qsEhQ+AC/rBzN45d1GPh7WS8yTjPhN1FkUVG+IQTet/0T5hm8rYPvwoxvcqvJToWxTmtnLa4Ju/JjAmS+dg5qtaL255OVf6TndQyTKD6x+eF+luBy7MdvfFA+Xcb1wWSl7M0i8kX1yV1lEfTOqwmGtY0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nfbcxNqy; arc=none smtp.client-ip=209.85.221.53 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.b="nfbcxNqy" Received: by mail-wr1-f53.google.com with SMTP id ffacd0b85a97d-421851bca51so768837f8f.1 for ; Thu, 06 Nov 2025 04:53:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433623; x=1763038423; 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; bh=8BVecavUPRiHXmHkxwJOxXNf9imfUNAzPBNe3t0kkm0=; b=nfbcxNqyWvXuIrAjO8EePloZWZV6MsKkuS/ZeMGAkF1/D06PnkqKC3LNwC8zZI4Ccc PvvWlPieXlBArGq5dVl2OjRNraIZHb+z6JbslivOJb95VJOAtRxyCepZ4GGZC2hYkdtC Rhlbva5ZjJ1T290lpl4mFaIQbYI9HhgQQM71c6j5S2ab/aFyL/uVFCxZYakXgIg36QL0 yNvMX1lMpcSbWG3+zsw1ISFlXsGcMDD/mq96ih/nkQrP00AN+lyzxBKJfnXr30aQ+ukW cjx5vbD64IAvjC2dxuSLv5B5o/266trnOrvRiKGYMy9+9hPOJQLS/49i7zGfqNpggcDS vIbA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433623; x=1763038423; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=8BVecavUPRiHXmHkxwJOxXNf9imfUNAzPBNe3t0kkm0=; b=LO0luoHPJWYeqM/EQCG96KcZk2VWvAj+HqmFtRtXse/kZe3Yl2E/Micyuem3YCsdI7 K+2llgkQF4pIMzZOE6M0s6Z3h1+0cSB7K+Nu/30JOgrCiNr1DvE34VbigtaygNMTvoc8 mMmg9tBIo7Kod2jzKv4g9RBlHdLobKF1mR0EMMQYGf/BF54ItpXZrrHmtxAaHisVgj+V 4hc4eAhO2Yc9MKfNpZj1/3Kk9ugUt7VIGrM9sDguqTPxYr7cu+puaeI3f4ZE7NlJQcRT xbgDnjHM0PKjkOCwMhIO2g4eAVgEYTBRi1Mx3x/E7nKmPK31eW9ePyTGAaU6wr1uAEMY m+GA== X-Forwarded-Encrypted: i=1; AJvYcCVRG94ZTwN5UYU/cKA4GqnPotV+J2nRgskUftrUGEO2drRl6FwPMdk5uoA9joYbO8QeeDPXVTKP9iuToJE=@vger.kernel.org X-Gm-Message-State: AOJu0YzKLyNO7Ij6tBUx1kSST2/S3O0Ot85z0Zs0lT9hOixxZPUlUKaB 5P7wuvZmPiksiRvgElwpiEN4GA/4Ng3JAvnTUuwONtvXGvC5j6uitBM= X-Gm-Gg: ASbGncuCjFe1vl76RqKH1OmGGiY2ocHPOKqf3eLVnfVNAB26lrWBN7Qy2DA8r4zfmZM NUr1HOb4iJIXXTVehqdV0VySibRnJ3U19ttkenHibyKje4NY4jy0s3yLPaKZ9xs2odhoekvESXG WKjSlbBgWN1C/ToRcO7BC7Zis4TAJvQvSFNH7ZDWMQUE2EonTxFtXwyRXBq5LheZYPqJ9WFIxVR 22lDtjwoRo62Q5LDK8Ehj9IFTgg6KgaDMYL30K557r038paafxScghDhLQ4ZlSysRkTRvPdgT3k ZVljn0NnmqEe5DAUNEqH6zH/DqnC6pbG55WxFXCWS7lHkDM6cDHXo88F+DLxBfycaDj3mpEA7H3 KruZeW3axpPJ2F6vilGICC7E3GVoqg5ZFysKw9JATzAWuvicc4pxytjhNHxRUde3n1J1LV/eC1B IbN2BJX10kvrlzM0BKGugg+bMaBguoTrKBShmRtMSYvHOhJGeqzwBjJl/QuaP2sURA9w== X-Google-Smtp-Source: AGHT+IHyCbG6Ib5B/hxvhvU1M9zTmkq15fseYuID4g2BHydDK2L/8J6Y6qTfp+dgmtEKmD6926vZsg== X-Received: by 2002:a05:6000:2502:b0:427:454:43b4 with SMTP id ffacd0b85a97d-429e330ab16mr6604348f8f.48.1762433623418; Thu, 06 Nov 2025 04:53:43 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:43 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 13/17] bpf: Skip state pruning for the parent states Date: Thu, 6 Nov 2025 13:52:51 +0100 Message-Id: <20251106125255.1969938-14-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Different incoming paths to the same instruction may yield different path conditions; pruning by containment would drop paths with different constrai= nts. - Add `children_unsafe` flag to `struct bpf_verifier_state`. - In `is_state_visited()`, if a visited candidate has `children_unsafe`, tr= eat it as a miss. - Propagate `children_unsafe` to the next state on split and clear it in the current state when pushing a new parent. - After a refinement request, clear all `bcf_expr` in registers and mark all collected parents (except the base) as `children_unsafe` to avoid pruning alternative suffixes that may build different path conditions. Signed-off-by: Hao Sun --- include/linux/bpf_verifier.h | 1 + kernel/bpf/verifier.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index b430702784e2..9b91353a86d7 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -422,6 +422,7 @@ struct bpf_verifier_state { bool speculative; bool in_sleepable; bool cleaned; + bool children_unsafe; =20 /* first and last insn idx of this verifier state */ u32 first_insn_idx; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index f1e8e70f9f61..ec0e736f39c5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -19904,6 +19904,8 @@ static int is_state_visited(struct bpf_verifier_env= *env, int insn_idx) states_cnt++; if (sl->state.insn_idx !=3D insn_idx) continue; + if (sl->state.children_unsafe) + goto miss; =20 if (sl->state.branches) { struct bpf_func_state *frame =3D sl->state.frame[sl->state.curframe]; @@ -20216,6 +20218,8 @@ static int is_state_visited(struct bpf_verifier_env= *env, int insn_idx) return err; } =20 + new->children_unsafe =3D cur->children_unsafe; + cur->children_unsafe =3D false; cur->parent =3D new; cur->first_insn_idx =3D insn_idx; cur->dfs_depth =3D new->dfs_depth + 1; @@ -24272,6 +24276,20 @@ static int __used bcf_refine(struct bpf_verifier_e= nv *env, if (!err && (env->bcf.refine_cond >=3D 0 || env->bcf.path_cond >=3D 0)) mark_bcf_requested(env); =20 + for (i =3D 0; i < MAX_BPF_REG; i++) + regs[i].bcf_expr =3D -1; + + /* + * Mark the parents as children_unsafe, i.e., they are not safe for + * state pruning anymore. Say s0 is contained in s1 (marked), then + * exploring s0 will reach the same error state that triggers the + * refinement. However, since the path they reach the pruning point + * can be different, the path condition collected for s0 can be + * different from s1's. Hence, pruning is not safe. + */ + for (i =3D 0; i < bcf->vstate_cnt - 1; i++) + bcf->parents[i]->children_unsafe =3D true; + kfree(env->bcf.parents); return err ?: 1; } --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F0FC33033D for ; Thu, 6 Nov 2025 12:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433630; cv=none; b=LPTPzr38IbB5aAPL9FiT3jXtXj9XWwZHoav9sd37928kph/ocjJXf/VRDDM3I4/y3Ghi5HTJXxxAxLUVo89kLoatqzI7nsn6/YW9LlsIIo+ILcCGyxJh5G7jgYQGAPvM2YzuV2rwq21opLWkHZsEgyC+uMBULAmb3LwXelqqzjU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433630; c=relaxed/simple; bh=Jcs/EeETuzWN7pCMx7VDBkqgjWPCz6UkIhKwoJ0gHSE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=Iy7YqIm9LtMiQJn7jsemPjrbPlvXdY2pl3UA5hUg8vunItWqcSsAkFHVGiaYa2Kptqa4F13lNnwOQQboZqXofxfuXoYnZ8kO90qpCG/YTYAOvspbR5s3IiqTg+TmvANj2KFPMHHRVCC4v+zkf+tJ8qO8n/eE7wE5S8ECNjhJerI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=T3tRNzt2; arc=none smtp.client-ip=209.85.221.45 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.b="T3tRNzt2" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-3ecdf2b1751so643291f8f.0 for ; Thu, 06 Nov 2025 04:53:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433624; x=1763038424; 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; bh=lnAgtUMJwOc4rjloS+eD4txeDjMJcwa78KfLJ1BQCwk=; b=T3tRNzt2CcklvgVFhWCJRQ1ObimJMIlX6reRG8EyNtvkRNHuAro9XKbFY49hlY6KTd 9Z8PiJcb4j7UDahr6GtH8bGozJSMrTOLyhvmyLrPIGQZYxNfvd4wkmykr/erV84L64rm aSqGl1Wkx5HeABBVEaw/6RDg6TF1pz1Q/Gw3dMxCI2adHnjE8mJnu8m7QjO7c3SaTgPK v8l3cVqNSxeOyasxd1iw7HKB++wL8bMVsimT6107f1pWBn/6mxTFyw6+iDIO3K4cdgZL yGt+Mtn/c6Pv9wOeUSNGfVKYXBuIE3Z/gsZEy03a7m/1l80J8iYYt7cpqUcoYXfOvQw8 DM/A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433624; x=1763038424; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lnAgtUMJwOc4rjloS+eD4txeDjMJcwa78KfLJ1BQCwk=; b=T8XC/7TGMUmyOGYPVmg1uN/e8GXB67ormznzV/kLfKK3x3tDBXd7jsYKGphhg+ymj2 PSAqwUro47n+agClB3qosnDR4D8WO36pDxQXcWLTKI1+Ryu/9EEeJCuJNsiuzzODOGtO MEkd4KysRvdMWPigd60hV+F/JeLYpljdOCAIMxsM/Q/n55BGx/wx3Gm47/aeGfswSv6S 8wP52nX0ysiQHtUPyXFA5NkAM34K4/b4jT0w+qKgQFL0Kmv2STAR7nMag4Zqee1ms2hE yz/UIX0ma5pBCv9r9HrT2Gv80XRh/ais57CbxfFB7ubfoJBIj9BPSVFiigwvgDg8G3gS WmOw== X-Forwarded-Encrypted: i=1; AJvYcCWuXzLqdfXYaAiU9QLGANBHIttquBX3Z0IiM/xuZrMGA12ChpWnfkEYeAAYvJGN73KAmSPVZTWNiRRM1d4=@vger.kernel.org X-Gm-Message-State: AOJu0YxzM6G8hJ9O1S4hHsy05TKJ38LYMDySzC9Gqs33ayB5mQIHbGq1 NtZDNw7N88eX0a2eT7kxDw0newGwDt18+w77FSTtr6PA4nM1wgahm7g= X-Gm-Gg: ASbGnctIFTTvhea4CIWT8JumdZv2c/VRsZ6sCV3F7XC737j4DvrCz9j1mCqJplFdnk3 GD0qpdhsPBzhYBKoSzyh31doXMxNUqKl4gny3EX1ZA6uqhF2HY2s3QT9f+qLfLAbC6BbHifnKcW 9obHyfI9XvoK9EOr1GiMp5IbyjYbIPnl7WGncFhIkuKK0LxmAYl3tiF38Mt/DZ1MELn62a0RTR4 JP79eMx/zU7h7BgqOheLsdDmmWY6CV7hiLl/0mbxQI4LeQ2g9Bhh4oM9JD9sxCPyzNSiDJmDZ+0 Dm7QfNIVfBc2B2ghCA4hc7H5d9O41hH2X2bsVZhVKI5146ebxSuugyXff8NT35CUAPr8IVPpr8X sUNhh33Gp1gBsXKuMwd8I8P+sgZ6ZTGeMg13OPusoDhrUt+LqPSLe5C/SVji3H6baXMFDMpsJ7w 4LnV8YdO40Ohw8ifp5cXWEHSwacCSB553LrHGNgj78gRFDHD7DWRRIPWc= X-Google-Smtp-Source: AGHT+IHO9Y865RnKqwcQvNsaYqBCQdn4w1DxC2oH7NFH7f8XcVitcqMdXr6+EAIHeK9b0by1bRC/cQ== X-Received: by 2002:a05:6000:220e:b0:3ee:d165:2edd with SMTP id ffacd0b85a97d-429e32f4852mr6104960f8f.28.1762433624020; Thu, 06 Nov 2025 04:53:44 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:43 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 14/17] bpf: Add mem access bound refinement Date: Thu, 6 Nov 2025 13:52:52 +0100 Message-Id: <20251106125255.1969938-15-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Implement on-demand refinement for memory access bound checks and add=20 fallback to path-unreachable when refinement is not applicable. When common bound checks (stack/map/packet) fail (min/max outside bounds) or certain helper memory checks fail with -EACCES, call into `bcf_refine()` to prove either: (a) tighter bounds on pointer offset or size make the access safe, or (b) the current path is unreachable. For -EACCES on other sites, t= ry path-unreachable directly (`bcf_prove_unreachable()`). If the same error po= int is re-encountered under tracking, refinement is applied directly without a = new proof since they are known safe. - __bcf_refine_access_bound(): * const ptr + var size: prove `off + size_expr <=3D high` and refine size upper bound accordingly; the condition emitted is JGT, expects the solver to prove it unsat. * var ptr + const size: prove `fix_off + off_expr + sz <=3D high` and if = needed, `off + off_expr >=3D low`; refine pointer smin/smax accordingly. * var ptr + var size: emit two constraints (sum <=3D high and ptr_off >= =3D low), and if proved, treat the access as safe for range [smin, high) without changing either reg as the source of imprecision is unknown. Mark current state=E2=80=99s children as unsafe for pruning. * Split `check_mem_access()` into `do_check_mem_access()` and a wrapper t= hat triggers path-unreachable on -EACCES when no request was made yet. * Wrap `check_helper_mem_access()` similarly and integrate `check_mem_siz= e_reg()` with `access_checked` fast path to re-check a proven safe range by temporarily constraining the pointer reg. Signed-off-by: Hao Sun --- include/linux/bpf_verifier.h | 6 + kernel/bpf/verifier.c | 262 +++++++++++++++++++++++++++++++++-- 2 files changed, 255 insertions(+), 13 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 9b91353a86d7..05e8e3feea30 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -752,6 +752,12 @@ struct bcf_refine_state { u32 br_cond_cnt; int path_cond; /* conjunction of br_conds */ int refine_cond; /* refinement condition */ + + /* Refinement specific */ + u32 size_regno; + int checked_off; + int checked_sz; + bool access_checked; }; =20 /* single container for all structs diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index ec0e736f39c5..22a068bfd0f2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -621,6 +621,16 @@ static bool bcf_requested(const struct bpf_verifier_en= v *env) return env->prog->aux->bcf_requested; } =20 +static void bcf_prove_unreachable(struct bpf_verifier_env *env) +{ + int err; + + err =3D bcf_refine(env, env->cur_state, 0, NULL, NULL); + if (!err) + verbose(env, + "bcf requested, try to prove the path unreachable\n"); +} + static void mark_bcf_requested(struct bpf_verifier_env *env) { env->prog->aux->bcf_requested =3D true; @@ -4088,8 +4098,12 @@ static int check_reg_arg(struct bpf_verifier_env *en= v, u32 regno, { struct bpf_verifier_state *vstate =3D env->cur_state; struct bpf_func_state *state =3D vstate->frame[vstate->curframe]; + int err; =20 - return __check_reg_arg(env, state->regs, regno, t); + err =3D __check_reg_arg(env, state->regs, regno, t); + if (err =3D=3D -EACCES && !bcf_requested(env)) + bcf_prove_unreachable(env); + return err; } =20 static int insn_stack_access_flags(int frameno, int spi) @@ -5256,6 +5270,163 @@ static bool __is_pointer_value(bool allow_ptr_leaks, return reg->type !=3D SCALAR_VALUE; } =20 +struct bcf_mem_access_refine_ctx { + struct bpf_reg_state *ptr_reg; + struct bpf_reg_state *size_reg; + s32 off; + s32 lower_bound; + s32 higher_bound; +}; + +static int __bcf_refine_access_bound(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, + void *access) +{ + struct bcf_mem_access_refine_ctx *ctx =3D access; + struct bpf_reg_state *size_reg =3D ctx->size_reg, *ptr_reg =3D ctx->ptr_r= eg; + u32 mem_sz =3D ctx->higher_bound - ctx->lower_bound; + int size_expr, off_expr, low_expr, high_expr; + struct bcf_refine_state *bcf =3D &env->bcf; + s32 off =3D ctx->off; + s64 min_off, max_off; + bool bit32 =3D false; + + off_expr =3D ptr_reg->bcf_expr; + size_expr =3D size_reg->bcf_expr; + if (fit_s32(ptr_reg) && fit_s32(size_reg)) { + off_expr =3D bcf_expr32(env, off_expr); + size_expr =3D bcf_expr32(env, size_expr); + bit32 =3D true; + } + + min_off =3D ptr_reg->smin_value + off; + max_off =3D ptr_reg->smax_value + off; + + if (tnum_is_const(ptr_reg->var_off)) { /* Refine the size range */ + off +=3D ptr_reg->var_off.value; + + /* Prove `off + size > higher_bound` unsatisfiable */ + bcf->refine_cond =3D bcf_add_pred(env, BPF_JGT, size_expr, + ctx->higher_bound - off, bit32); + + /* size->umax + off <=3D higher holds after proof */ + size_reg->umax_value =3D ctx->higher_bound - off; + size_reg->umin_value =3D + min_t(u64, size_reg->umax_value, size_reg->umin_value); + reg_bounds_sync(size_reg); + + } else if (tnum_is_const(size_reg->var_off)) { /* Refine the ptr off */ + u32 sz =3D size_reg->var_off.value; + + /* Prove `off + off_expr + sz > higher_bound` unsatisfiable */ + high_expr =3D bcf_add_pred(env, BPF_JSGT, off_expr, + ctx->higher_bound - sz - off, bit32); + /* + * If the verifier already knows the lower bound is safe, then + * don't emit this in the formula. + */ + bcf->refine_cond =3D high_expr; + if (min_off < ctx->lower_bound) { + low_expr =3D bcf_add_pred(env, BPF_JSLT, off_expr, + ctx->lower_bound - off, bit32); + bcf->refine_cond =3D + bcf_build_expr(env, BCF_BOOL | BCF_DISJ, 0, 2, + low_expr, high_expr); + } + + if (min_off < ctx->lower_bound) + ptr_reg->smin_value =3D ctx->lower_bound - off; + if (max_off + sz > ctx->higher_bound) + ptr_reg->smax_value =3D ctx->higher_bound - off - sz; + reg_bounds_sync(ptr_reg); + } else { /* Prove var off with var size is safe */ + u32 bitsz =3D bit32 ? 32 : 64; + + high_expr =3D bcf_build_expr(env, BCF_BV | BPF_ADD, bitsz, 2, + off_expr, size_expr); + high_expr =3D bcf_add_pred(env, BPF_JSGT, high_expr, + ctx->higher_bound - off, bit32); + bcf->refine_cond =3D high_expr; + if (min_off < ctx->lower_bound) { + low_expr =3D bcf_add_pred(env, BPF_JSLT, off_expr, + ctx->lower_bound - off, bit32); + bcf->refine_cond =3D + bcf_build_expr(env, BCF_BOOL | BCF_DISJ, 0, 2, + low_expr, high_expr); + } + + if (min_off < ctx->lower_bound) + ptr_reg->smin_value =3D ctx->lower_bound - off; + if (max_off > ctx->higher_bound) + ptr_reg->smax_value =3D ctx->higher_bound - off; + if (size_reg->umax_value > mem_sz) + size_reg->umax_value =3D mem_sz; + reg_bounds_sync(ptr_reg); + reg_bounds_sync(size_reg); + + /* + * off+off_expr+sz_expr <=3D high && off+off_expr>=3Dlow proved, + * but it's not clear which regs are the source of imprecision; + * hence don't refine, but remember the access is safe and + * treat this access as [smin, higher) range access. + */ + min_off =3D ptr_reg->smin_value + off; + bcf->checked_off =3D ptr_reg->smin_value; + bcf->checked_sz =3D ctx->higher_bound - min_off; + bcf->access_checked =3D true; + st->children_unsafe =3D true; + } + + return bcf->refine_cond < 0 ? bcf->refine_cond : 0; +} + +static void bcf_refine_access_bound(struct bpf_verifier_env *env, int regn= o, + s32 off, s32 low, s32 high, u32 access_size) +{ + struct bcf_mem_access_refine_ctx ctx =3D { + .off =3D off, + .lower_bound =3D low, + .higher_bound =3D high, + }; + struct bpf_reg_state *regs =3D cur_regs(env), tmp_reg; + struct bpf_reg_state *ptr_reg, *size_reg; + struct bcf_refine_state *bcf =3D &env->bcf; + u32 reg_masks =3D 0, mem_sz =3D high - low; + bool ptr_const, size_const; + s64 ptr_off; + int err; + + ptr_reg =3D regs + regno; + ptr_const =3D tnum_is_const(ptr_reg->var_off); + if (!ptr_const) + reg_masks |=3D (1 << regno); + + __mark_reg_known(&tmp_reg, access_size); + tmp_reg.type =3D SCALAR_VALUE; + size_reg =3D &tmp_reg; + if (bcf->size_regno > 0) { + size_reg =3D regs + bcf->size_regno; + if (!tnum_is_const(size_reg->var_off)) + reg_masks |=3D (1 << bcf->size_regno); + } + size_const =3D tnum_is_const(size_reg->var_off); + ctx.size_reg =3D size_reg; + ctx.ptr_reg =3D ptr_reg; + + /* Prove unreachable. */ + ptr_off =3D ptr_reg->smin_value + off; + if (!reg_masks || !mem_sz || + (ptr_const && (ptr_off < low || ptr_off >=3D high)) || + (size_const && size_reg->var_off.value > mem_sz)) + return bcf_prove_unreachable(env); + + err =3D bcf_refine(env, env->cur_state, reg_masks, + __bcf_refine_access_bound, &ctx); + if (!err) + verbose(env, "bcf requested, try to refine R%d access\n", + regno); +} + static void assign_scalar_id_before_mov(struct bpf_verifier_env *env, struct bpf_reg_state *src_reg) { @@ -6016,6 +6187,7 @@ static int check_mem_region_access(struct bpf_verifie= r_env *env, u32 regno, if (err) { verbose(env, "R%d min value is outside of the allowed memory range\n", regno); + bcf_refine_access_bound(env, regno, off, 0, mem_size, size); return err; } =20 @@ -6033,6 +6205,7 @@ static int check_mem_region_access(struct bpf_verifie= r_env *env, u32 regno, if (err) { verbose(env, "R%d max value is outside of the allowed memory range\n", regno); + bcf_refine_access_bound(env, regno, off, 0, mem_size, size); return err; } =20 @@ -7680,6 +7853,15 @@ static int check_ptr_to_map_access(struct bpf_verifi= er_env *env, return 0; } =20 +static int stack_min_off(struct bpf_verifier_env *env, struct bpf_func_sta= te *state, + enum bpf_access_type t) +{ + if (t =3D=3D BPF_WRITE || env->allow_uninit_stack) + return -MAX_BPF_STACK; + else + return -state->allocated_stack; +} + /* Check that the stack access at the given offset is within bounds. The * maximum valid offset is -1. * @@ -7693,11 +7875,7 @@ static int check_stack_slot_within_bounds(struct bpf= _verifier_env *env, { int min_valid_off; =20 - if (t =3D=3D BPF_WRITE || env->allow_uninit_stack) - min_valid_off =3D -MAX_BPF_STACK; - else - min_valid_off =3D -state->allocated_stack; - + min_valid_off =3D stack_min_off(env, state, t); if (off < min_valid_off || off > -1) return -EACCES; return 0; @@ -7759,6 +7937,11 @@ static int check_stack_access_within_bounds( verbose(env, "invalid variable-offset%s stack R%d var_off=3D%s off=3D%d= size=3D%d\n", err_extra, regno, tn_buf, off, access_size); } + + if (err !=3D -EFAULT) + bcf_refine_access_bound(env, regno, off, + stack_min_off(env, state, type), + 0, access_size); return err; } =20 @@ -7785,7 +7968,7 @@ static bool get_func_retval_range(struct bpf_prog *pr= og, * if t=3D=3Dwrite && value_regno=3D=3D-1, some unknown value is stored in= to memory * if t=3D=3Dread && value_regno=3D=3D-1, don't care what we read from mem= ory */ -static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u3= 2 regno, +static int do_check_mem_access(struct bpf_verifier_env *env, int insn_idx,= u32 regno, int off, int bpf_size, enum bpf_access_type t, int value_regno, bool strict_alignment_once, bool is_ldsx) { @@ -8045,6 +8228,20 @@ static int check_mem_access(struct bpf_verifier_env = *env, int insn_idx, u32 regn return err; } =20 +static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, + u32 regno, int off, int bpf_size, + enum bpf_access_type t, int value_regno, + bool strict_alignment_once, bool is_ldsx) +{ + int err; + + err =3D do_check_mem_access(env, insn_idx, regno, off, bpf_size, t, + value_regno, strict_alignment_once, is_ldsx); + if (err =3D=3D -EACCES && !bcf_requested(env)) + bcf_prove_unreachable(env); + return err; +} + static int save_aux_ptr_type(struct bpf_verifier_env *env, enum bpf_reg_ty= pe type, bool allow_trust_mismatch); =20 @@ -8427,7 +8624,7 @@ static int check_stack_range_initialized( return 0; } =20 -static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, +static int __check_helper_mem_access(struct bpf_verifier_env *env, int reg= no, int access_size, enum bpf_access_type access_type, bool zero_size_allowed, struct bpf_call_arg_meta *meta) @@ -8518,6 +8715,21 @@ static int check_helper_mem_access(struct bpf_verifi= er_env *env, int regno, } } =20 +static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, + int access_size, + enum bpf_access_type access_type, + bool zero_size_allowed, + struct bpf_call_arg_meta *meta) +{ + int err; + + err =3D __check_helper_mem_access(env, regno, access_size, access_type, + zero_size_allowed, meta); + if (err =3D=3D -EACCES && !bcf_requested(env)) + bcf_prove_unreachable(env); + return err; +} + /* verify arguments to helpers or kfuncs consisting of a pointer and an ac= cess * size. * @@ -8530,6 +8742,7 @@ static int check_mem_size_reg(struct bpf_verifier_env= *env, bool zero_size_allowed, struct bpf_call_arg_meta *meta) { + struct bcf_refine_state *bcf =3D &env->bcf; int err; =20 /* This is used to refine r0 return value bounds for helpers @@ -8553,22 +8766,40 @@ static int check_mem_size_reg(struct bpf_verifier_e= nv *env, if (reg->smin_value < 0) { verbose(env, "R%d min value is negative, either use unsigned or 'var &= =3D const'\n", regno); + bcf_prove_unreachable(env); return -EACCES; } =20 if (reg->umin_value =3D=3D 0 && !zero_size_allowed) { verbose(env, "R%d invalid zero-sized read: u64=3D[%lld,%lld]\n", regno, reg->umin_value, reg->umax_value); + bcf_prove_unreachable(env); return -EACCES; } =20 if (reg->umax_value >=3D BPF_MAX_VAR_SIZ) { verbose(env, "R%d unbounded memory access, use 'var &=3D const' or 'if (= var < const)'\n", regno); + bcf_prove_unreachable(env); return -EACCES; } - err =3D check_helper_mem_access(env, regno - 1, reg->umax_value, + + if (bcf->access_checked) { + struct bpf_reg_state *ptr_reg =3D &cur_regs(env)[regno - 1]; + struct bpf_reg_state tmp =3D *ptr_reg; + + ___mark_reg_known(ptr_reg, bcf->checked_off); + err =3D check_helper_mem_access(env, regno - 1, bcf->checked_sz, + access_type, zero_size_allowed, meta); + *ptr_reg =3D tmp; + bcf->access_checked =3D false; + } else { + bcf->size_regno =3D regno; + err =3D check_helper_mem_access(env, regno - 1, reg->umax_value, access_type, zero_size_allowed, meta); + bcf->size_regno =3D 0; + } + if (!err) err =3D mark_chain_precision(env, regno); return err; @@ -16367,8 +16598,11 @@ static int check_alu_op(struct bpf_verifier_env *e= nv, struct bpf_insn *insn) /* check dest operand */ err =3D check_reg_arg(env, insn->dst_reg, DST_OP_NO_MARK); err =3D err ?: adjust_reg_min_max_vals(env, insn); - if (err) + if (err) { + if (!bcf_requested(env)) + bcf_prove_unreachable(env); return err; + } } =20 return reg_bounds_sanity_check(env, ®s[insn->dst_reg], "alu"); @@ -20474,6 +20708,8 @@ static int do_check_insn(struct bpf_verifier_env *e= nv, bool *do_print_state) } else { err =3D check_helper_call(env, insn, &env->insn_idx); } + if (err =3D=3D -EACCES && !bcf_requested(env)) + bcf_prove_unreachable(env); if (err) return err; =20 @@ -24224,9 +24460,9 @@ backtrack_states(struct bpf_verifier_env *env, stru= ct bpf_verifier_state *cur, return base; } =20 -static int __used bcf_refine(struct bpf_verifier_env *env, - struct bpf_verifier_state *st, u32 reg_masks, - refine_state_fn refine_cb, void *ctx) +static int bcf_refine(struct bpf_verifier_env *env, + struct bpf_verifier_state *st, u32 reg_masks, + refine_state_fn refine_cb, void *ctx) { struct bpf_reg_state *regs =3D st->frame[st->curframe]->regs; struct bcf_refine_state *bcf =3D &env->bcf; --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f46.google.com (mail-wr1-f46.google.com [209.85.221.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 58198339B5E for ; Thu, 6 Nov 2025 12:53:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433629; cv=none; b=otsYnUp4tvWE13K+Pfh7s2zTSLxsDKJxy8xgN5/wdL0zZk8o6j4qHi0nWVlzYHd553d7mWSxMvX1cp5TM91nKaFhICRGDXDhvlUQG6OddQhqaN7bcguiA7Lqll+JFl8u8Qo37CzF7+ZWj2CHCOyUM0L0dYP+W9duCATx/s/mkGM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433629; c=relaxed/simple; bh=iirOyCTuTQLJ3WXnG2LSSYaVBZRAqVL6cetgZbBefoQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ATC5cmjSH2T32aHCfKzlq1Z+gBhEWIbn3otfBmw3uTW550v00ombnOn4SjUHfli12biGTDK6wk19cQe0I1mUn4sadhImQoEpWgC4ogvyr8RMKDlA5Z7TxwieZ/quRdXdt4y4jDf1Ko5tiSFEAyToay/UjZeGM1WKpF1+13x5LiQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=F9peU7j/; arc=none smtp.client-ip=209.85.221.46 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.b="F9peU7j/" Received: by mail-wr1-f46.google.com with SMTP id ffacd0b85a97d-4298b865f84so477500f8f.3 for ; Thu, 06 Nov 2025 04:53:46 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433625; x=1763038425; 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; bh=JHdOj7ulNF+ppmOqwLHhoqhx0zKTa30/mx/RkkRF4T4=; b=F9peU7j/LJf12xpr7FY/mnI179c3tq01yD3RhwaksDOaKZor8nmpRw1krEUm9AEexq iToYd8b58+9+lE8T5DEEFbp+nNxD5PirlXivmm4neGNwFBHnTlrkwqQ7jbTEt7nhLs8P g2WRG8nDXQvjPVckzUnVZ9uwLd96pcZQQ7iXRSkvGNHquhan50arh1znNxP0lSKlS+zD E8L/Vtes4357TAn1I+6nT1AUHDAJOLCwL787bhNwvmao9827ZeS3qqxcnEt0Lg7pixXf vCHLoPnr3UX4RF0zcbvDt0bH6z2rSsToPuecTWozAqqWY7c4ebfIWhwoY8YEKBm1SkeH IPdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433625; x=1763038425; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JHdOj7ulNF+ppmOqwLHhoqhx0zKTa30/mx/RkkRF4T4=; b=XLt7w2mJg9ejbnyFvw2j7UayOpDF401uhh4XxgSS6Q4phblx6qgl2llz/udRS+8InF hTandx5qyPcuH/+6/LULNchl22GZepm9HPtZLFRFDv7sCXOKMCCJS6Eelg0eVlI4zyxH JTRbBe0buzkQ98Hra/qIxyykHqL9y0SvQtct31WRi7fjLHoYK5ID0hMaZDh7ad3BUvhd fZA2CFhuos9R2yWFkwCml4WhfvwLgWRpG5j2sF09kAVNzJWa9Jj6vfBgeOZyBQh/OgSQ UyruF2HoEg+dYjZKwkAYV8syy7KhtzW8Z5uED3flHTJN6O8C9LumlOOmi/FQKGkN8M38 fwqA== X-Forwarded-Encrypted: i=1; AJvYcCVKfalD+kDMFhzFR8Nk1ou7ALHnsYSzcOYovBQZNd4PP/RTIRx2Djwfxx9k8jvsqtZ66YLpxJWm1oRwMdw=@vger.kernel.org X-Gm-Message-State: AOJu0Yzg9yJSqKkPVNb7Fo3ZjeC4xSt/NN6DfykLwsobwAJKDru93GtK Bnb/AJ6+I1dUd/NneCxw+rsKaxntE4xlWxN2wupklx6qkMQ7SZeuzl0= X-Gm-Gg: ASbGnctXlTsvg5L115lG17vBBdArOeVM7M71Bo7QHBrf1YaoyCcVurEXqEQeUfSBW1C dftykWEC29+s88fSQ2MCErUBxnvBhw8pMtiQz3I+Ze4H+/adgDVdzG+8fl6LTKKjVbC2OyHtRq1 eGDIdtzNgsTb1MpiCf3mE+N1CQQReZxMhbt027xYIY+iBJlVrKb+rlhBHmB8xQ9oqv5n+nvFkzy OEaQk3sDoQMWT0qw7k350n95j/V2k+gYWP4i6V5rpdQJJpUO5Unc9OIIIPZ4jNAuFt1hpnCVuX+ gFde2fb8hei1BJJSpqlOQeC2s0QQ6vaE201K3k5qQu7y9d7W8jQHvBBXhRORSfpf9Z2vKAqTETH WJpphToMO74puTv9QVbi30ySeZYGFtpO/4O2ndpl48s2b+DimHaraWnaPp9SqKrHmDorgzA3wg+ oPFAbs1JG1W65qe5agu4ciG674C1NklrF9SFPTD6Ush/rd0fs4d+1bYbM= X-Google-Smtp-Source: AGHT+IGbcSvgyDIFX104hFsyRqUmMxP5IxutnYiC8uPKnRyoOUjx0mknhZI8QlEUnUDIp6u8s+HmRQ== X-Received: by 2002:a05:6000:25c2:b0:429:cda2:a01d with SMTP id ffacd0b85a97d-429e32edf87mr7231081f8f.26.1762433624675; Thu, 06 Nov 2025 04:53:44 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:44 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 15/17] bpf: Preserve verifier_env and request BCF Date: Thu, 6 Nov 2025 13:52:53 +0100 Message-Id: <20251106125255.1969938-16-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Preserve the verifier environment when a refinement request is issued and export the expression arena and condition to userspace, so that the analysis can resume later with a proof. - Track the currently analyzed subprog (`env->subprog`) so resume can pick = up where the request occurred.=20 - In `do_check_common()`, if a request is pending, return early without freeing states (bcf/tracking-aware). In `bpf_prog_load()`, if the request= was issued, return the verifier error immediately to userspace. - Add anon-fd : create an anon inode (`bcf_fops`) that owns the `verifier_e= nv`. On fd release, free verifier state, release maps/btfs/prog. - Implement `do_request_bcf()`: copy conditions into `bcf_buf`; set `bcf_flags =3D BCF_F_PROOF_REQUESTED`; allocate and return `bcf_fd` if this is the first request. - Adjust verifier time accounting to multi-rounds analysis. - Minor cleanups: make zext/hi32 optimization read flags from `env`. Signed-off-by: Hao Sun --- include/linux/bpf_verifier.h | 3 + kernel/bpf/syscall.c | 2 + kernel/bpf/verifier.c | 113 +++++++++++++++++++++++++++++++++-- 3 files changed, 112 insertions(+), 6 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 05e8e3feea30..67eac7b2778e 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -774,6 +774,7 @@ struct bpf_verifier_env { bool strict_alignment; /* perform strict pointer alignment checks */ bool test_state_freq; /* test verifier with different pruning frequency = */ bool test_reg_invariants; /* fail verification on register invariants vio= lations */ + bool test_rnd_hi32; /* randomize high 32-bit to test subreg def/use */ struct bpf_verifier_state *cur_state; /* current verifier state */ /* Search pruning optimization, array of list_heads for * lists of struct bpf_verifier_state_list. @@ -867,6 +868,8 @@ struct bpf_verifier_env { u32 scc_cnt; struct bpf_iarray *succ; struct bcf_refine_state bcf; + /* The subprog being verified. */ + u32 subprog; }; =20 static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_en= v *env, int subprog) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5968b74ed7b2..97914494bd18 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -3096,6 +3096,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr= _t uattr, u32 uattr_size) =20 /* run eBPF verifier */ err =3D bpf_check(&prog, attr, uattr, uattr_size); + if (prog->aux->bcf_requested) + return err; if (err < 0) goto free_used_maps; =20 diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 22a068bfd0f2..3b631cea827e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -636,6 +638,11 @@ static void mark_bcf_requested(struct bpf_verifier_env= *env) env->prog->aux->bcf_requested =3D true; } =20 +static void unmark_bcf_requested(struct bpf_verifier_env *env) +{ + env->prog->aux->bcf_requested =3D false; +} + static int bcf_alloc_expr(struct bpf_verifier_env *env, u32 cnt) { struct bcf_refine_state *bcf =3D &env->bcf; @@ -22036,8 +22043,7 @@ static int opt_remove_nops(struct bpf_verifier_env = *env) return 0; } =20 -static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env, - const union bpf_attr *attr) +static int opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env) { struct bpf_insn *patch; /* use env->insn_buf as two independent buffers */ @@ -22049,7 +22055,7 @@ static int opt_subreg_zext_lo32_rnd_hi32(struct bpf= _verifier_env *env, struct bpf_prog *new_prog; bool rnd_hi32; =20 - rnd_hi32 =3D attr->prog_flags & BPF_F_TEST_RND_HI32; + rnd_hi32 =3D env->test_rnd_hi32; zext_patch[1] =3D BPF_ZEXT_REG(0); rnd_hi32_patch[1] =3D BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, 0); rnd_hi32_patch[2] =3D BPF_ALU64_IMM(BPF_LSH, BPF_REG_AX, 32); @@ -24233,7 +24239,7 @@ static int do_check_common(struct bpf_verifier_env = *env, int subprog) ret =3D do_check(env); =20 /* Invoked by bcf_track(), just return. */ - if (env->bcf.tracking) + if (env->bcf.tracking || bcf_requested(env)) return ret; out: if (!ret && pop_log) @@ -24573,6 +24579,7 @@ static int do_check_subprogs(struct bpf_verifier_en= v *env) if (!sub_aux->called || sub_aux->verified) continue; =20 + env->subprog =3D i; env->insn_idx =3D env->subprog_info[i].start; WARN_ON_ONCE(env->insn_idx =3D=3D 0); ret =3D do_check_common(env, i); @@ -25766,6 +25773,85 @@ static int compute_scc(struct bpf_verifier_env *en= v) return err; } =20 +static int bcf_release(struct inode *inode, struct file *filp) +{ + struct bpf_verifier_env *env =3D filp->private_data; + + if (!env) + return 0; + + free_states(env); + kvfree(env->explored_states); + + if (!env->prog->aux->used_maps) + release_maps(env); + if (!env->prog->aux->used_btfs) + release_btfs(env); + bpf_prog_put(env->prog); + + module_put(env->attach_btf_mod); + vfree(env->insn_aux_data); + bpf_stack_liveness_free(env); + kvfree(env->cfg.insn_postorder); + kvfree(env->scc_info); + kvfree(env->succ); + kvfree(env->bcf.exprs); + kvfree(env); + filp->private_data =3D NULL; + return 0; +} + +static const struct file_operations bcf_fops =3D { + .release =3D bcf_release, +}; + +static int do_request_bcf(struct bpf_verifier_env *env, union bpf_attr *at= tr, + bpfptr_t uattr) +{ + bpfptr_t bcf_buf =3D make_bpfptr(attr->bcf_buf, uattr.is_kernel); + int ret, bcf_fd =3D -1, flags =3D BCF_F_PROOF_REQUESTED; + struct bcf_refine_state *bcf =3D &env->bcf; + u32 sz, sz_off, expr_sz; + + /* All exprs, followed by path_cond and refine_cond. */ + expr_sz =3D bcf->expr_cnt * sizeof(struct bcf_expr); + sz =3D expr_sz + sizeof(bcf->path_cond) + sizeof(bcf->refine_cond); + sz_off =3D offsetof(union bpf_attr, bcf_buf_true_size); + + ret =3D -EFAULT; + if (copy_to_bpfptr_offset(uattr, sz_off, &sz, sizeof(sz))) + return ret; + if (sz > attr->bcf_buf_size) + return -ENOSPC; + + if (copy_to_bpfptr_offset(bcf_buf, 0, bcf->exprs, expr_sz) || + copy_to_bpfptr_offset(bcf_buf, expr_sz, &bcf->path_cond, + sizeof(u32)) || + copy_to_bpfptr_offset(bcf_buf, expr_sz + sizeof(u32), + &bcf->refine_cond, sizeof(u32))) + return ret; + + if (copy_to_bpfptr_offset(uattr, offsetof(union bpf_attr, bcf_flags), + &flags, sizeof(flags))) + return ret; + + /* Allocate fd if first request. */ + if (!(attr->bcf_flags & BCF_F_PROOF_PROVIDED)) { + bcf_fd =3D anon_inode_getfd("bcf", &bcf_fops, env, + O_RDONLY | O_CLOEXEC); + if (bcf_fd < 0) + return bcf_fd; + if (copy_to_bpfptr_offset(uattr, + offsetof(union bpf_attr, bcf_fd), + &bcf_fd, sizeof(bcf_fd))) { + close_fd(bcf_fd); + return ret; + } + } + + return 0; +} + int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr= , __u32 uattr_size) { u64 start_time =3D ktime_get_ns(); @@ -25846,6 +25932,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_att= r *attr, bpfptr_t uattr, __u3 if (is_priv) env->test_state_freq =3D attr->prog_flags & BPF_F_TEST_STATE_FREQ; env->test_reg_invariants =3D attr->prog_flags & BPF_F_TEST_REG_INVARIANTS; + env->test_rnd_hi32 =3D attr->prog_flags & BPF_F_TEST_RND_HI32; =20 env->explored_states =3D kvcalloc(state_htab_size(env), sizeof(struct list_head), @@ -25915,10 +26002,24 @@ int bpf_check(struct bpf_prog **prog, union bpf_a= ttr *attr, bpfptr_t uattr, __u3 ret =3D do_check_main(env); ret =3D ret ?: do_check_subprogs(env); =20 + if (ret && bcf_requested(env)) { + u64 vtime =3D ktime_get_ns() - start_time; + + env->verification_time +=3D vtime; + if (do_request_bcf(env, attr, uattr) =3D=3D 0) + return ret; + + unmark_bcf_requested(env); + env->verification_time -=3D vtime; + } + if (ret =3D=3D 0 && bpf_prog_is_offloaded(env->prog->aux)) ret =3D bpf_prog_offload_finalize(env); =20 skip_full_check: + /* If bcf_requested(), the last state is preserved, free now. */ + if (env->cur_state) + free_states(env); kvfree(env->explored_states); =20 /* might decrease stack depth, keep it before passes that @@ -25957,7 +26058,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_att= r *attr, bpfptr_t uattr, __u3 * insns could be handled correctly. */ if (ret =3D=3D 0 && !bpf_prog_is_offloaded(env->prog->aux)) { - ret =3D opt_subreg_zext_lo32_rnd_hi32(env, attr); + ret =3D opt_subreg_zext_lo32_rnd_hi32(env); env->prog->aux->verifier_zext =3D bpf_jit_needs_zext() ? !ret : false; } @@ -25965,7 +26066,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_att= r *attr, bpfptr_t uattr, __u3 if (ret =3D=3D 0) ret =3D fixup_call_args(env); =20 - env->verification_time =3D ktime_get_ns() - start_time; + env->verification_time +=3D ktime_get_ns() - start_time; print_verification_stats(env); env->prog->aux->verified_insns =3D env->insn_processed; =20 --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6A26933BBBE for ; Thu, 6 Nov 2025 12:53:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433631; cv=none; b=A0uKs3fv/iaX2TQDwoATpjeFom6dyyS0f952fd0hNSx+GE8AYfy+C5FgUyOcRlYDjLSaG+cGJQHub9aBPr093APBFveEVOltECX0mfFllHUHh0cYU8eT6LY4uCOIKR2TglXYZQ79AQgNGRgsO7aERL0l7cnSSI3EMbNRZptTxQI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433631; c=relaxed/simple; bh=qInMAmfp5FBsELWSL3ofmHPfHebykt5zIrz8u5DETCw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=j6mr+mG0ifUM+KGygCFVRyQCvUWA0C9fxTr1eLyAU9ScnhywsdydM1ANodiC297CGVP8zsaGQwfuX6LalccveaA+6Em3wwnaVkltwLZX0z5lqd5X2S7NscrqftA2dDHO3Hb0mmtPWlwiDdwJQ4bQn85c8CKnXBvwcjDplEPyC9g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=izDvscUI; arc=none smtp.client-ip=209.85.221.45 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.b="izDvscUI" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-3ecdf2b1751so643313f8f.0 for ; Thu, 06 Nov 2025 04:53:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433626; x=1763038426; 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; bh=CmD8sr/SvUW/y+PfwDxdl2359E+UebPVfjOgIaOsoAE=; b=izDvscUIo7bl/OvIVq/CQzteAy/I4XXrsIU5zZKdo9LVpayA0NYJetHIJprSXniQMZ X6NfApGAcX9u55wS+08N36FvXQysFcKD4ImiInlGxBUCH3n/9+I9YLP1yyC0za8E7yDA zQPevBPSrw6C1yRtNK258Q+vyWdN0tQb4WIVP0doh5fze7hjY8QyTr4Wblvj3S7U5L0U DoXWQnKIaVzu3mJ48fcdppxJk61aY6wJifjYPaZA+r7T9JNuCpcD8USZ/qhNxFwnqtRP utf4jyYVyt+MoaQjLfTyCSbe6o92ByJBRaKUb/4rFCuCZeJnrvHUpff6V2FY4ZDZplPA yBjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433626; x=1763038426; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CmD8sr/SvUW/y+PfwDxdl2359E+UebPVfjOgIaOsoAE=; b=ta0CisTQrwtCgMrY+TIjaMXZ8SmqvCKlxIhxUcOl5ueVQ3+w7iljGdoENy8XejyhFd Y7WbehfWq/0WQ/xi5q45posmnggTEUraxE0sLJ33+bL1b4oqOpxBAIP5bGppMaS82pjO uhbU1WG07S8L86gZ/Xmdknc6P5/Eop14HdSZCUepePd3vif1BUGmNAp/OfqEMz7ghG8o WNgisWxkj11/PYKop5ztGhj+14uBqae6N6BPqzl6YjTizdbwH2nzsHXEJQbzaODQAF15 b8anNM4NoiqnwpEOHKVZjbPm1vjWiPscXIoqTmC7wpqtRyAPa7L4vn/FYpxTlsekcoEw 1fbw== X-Forwarded-Encrypted: i=1; AJvYcCUpgKT5RBqELLdfXDUxURjckyAtfIWaTDDcYNu3U/8Y7j/f9Op9Ljlv4lMVkfEe9OQw/NpdALVwXaOpuwI=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7gVLSDtChOrqvmVo2F7zxxgGWQTOH7h4RCMZ6BJzW9uxhzS38 O+ZMdtljLF/hH5+yW4LifFHOyvTNxHnB1XMAFgKQxG+VEKhuzqt5/FA= X-Gm-Gg: ASbGnctMsOTNVUxAtjE8YPrpLkObS83D2oKga8Cz+6kOLxlpkOYUPfynaBPt/D+feeN I/z4mDsslT5kRc6v6VPc07rJeApihhmMw5aEpU6yDS+Yq4/OiyG8AbjJnJxXz2INzF/b6W4iRZH rXI3YgVIIP8/FOizXaJtSFcG5HH6xyMSpH66FkWNDoz/LgjCCy4akQGiZ4CGH5zNDgmzbx/s7w5 D1DhfHKNui8J42OtJ9h86L38NYKo7Pgf5MTO9wJNrsDdbD/1jMzIsmsws4KDkrzdwM7BnZLeJSk U8gDB0JQYeabC2dx39AlDhMnqcWClP8VVKaocipGoL6Ts1UbOBgW0B47a6JGnkWzh8lCHb0hHeB UESxdrM2TmkW0byEWK+yYpKo2QDT3T7+jK7vNa3qNZw9ETOzEhTENMdcWvqjFochYKpplS9ykub 2zRnFHt47DW4IfxA3KvVIsfsB/ZFY85kw3FxERsOb9wfzknG4DnPvVMlo= X-Google-Smtp-Source: AGHT+IEmfj4wy0P0dsEkb1239creqUmNtDLxyKLwH37BcSlr9kI7/6yx6uoWgXO96nP471KsPLuTeA== X-Received: by 2002:a05:6000:2083:b0:429:c4bb:fbbb with SMTP id ffacd0b85a97d-429e32c82bdmr7170308f8f.13.1762433625515; Thu, 06 Nov 2025 04:53:45 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:45 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 16/17] bpf: Resume verifier env and check proof Date: Thu, 6 Nov 2025 13:52:54 +0100 Message-Id: <20251106125255.1969938-17-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add the resume path for proof-based refinement and integrate proof check into the verifier flow. - Userspace sets `BCF_F_PROOF_PROVIDED`, passes the saved `bcf_fd` and a pr= oof in `bcf_buf`. bpf_prog_load() detects resume, swaps to verifier path with= out building a new prog, and calls bpf_check() with `prog =3D NULL`. - In bpf_check(), fetch the saved env from `bcf_fd`, ensure it=E2=80=99s a = BCF file and not already in use (`bcf.in_use`), and mark it in-use. - `resume_env()`: determine which condition to check (default `refine_cond`= ; if `BCF_F_PROOF_PATH_UNREACHABLE` is set, use `path_cond` and mark `path_unreachable` to skip to exit on the next insn). Copy the proof from userspace and call `bcf_check_proof()` with the saved expression arena and condition id. - Resume verification: skip re-initialization if `env->cur_state` exists; analyze only the subprog recorded at request time; continue with the refi= ned `cur_state` and finish normal verification. Signed-off-by: Hao Sun --- include/linux/bpf_verifier.h | 2 + kernel/bpf/syscall.c | 6 ++- kernel/bpf/verifier.c | 94 +++++++++++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 67eac7b2778e..219e211195fc 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -745,6 +745,7 @@ struct bcf_refine_state { =20 bool available; /* if bcf_buf is provided. */ bool tracking; /* In bcf_track(). */ + atomic_t in_use; /* The current env is in use. */ struct bcf_expr *exprs; u32 expr_size; u32 expr_cnt; @@ -758,6 +759,7 @@ struct bcf_refine_state { int checked_off; int checked_sz; bool access_checked; + bool path_unreachable; }; =20 /* single container for all structs diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 97914494bd18..53ed868aa20c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2881,7 +2881,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr= _t uattr, u32 uattr_size) return -EINVAL; /* The resumed analysis must only uses the old, first attr. */ memset(attr, 0, offsetof(union bpf_attr, bcf_buf)); - return -ENOTSUPP; + prog =3D NULL; + goto verifier_check; } =20 if (attr->bcf_fd || attr->bcf_buf_true_size || attr->bcf_flags) @@ -3094,9 +3095,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfpt= r_t uattr, u32 uattr_size) if (err) goto free_prog_sec; =20 +verifier_check: /* run eBPF verifier */ err =3D bpf_check(&prog, attr, uattr, uattr_size); - if (prog->aux->bcf_requested) + if (!prog || prog->aux->bcf_requested) return err; if (err < 0) goto free_used_maps; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3b631cea827e..fb672c9cc7cd 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -33,6 +33,7 @@ #include #include #include +#include =20 #include "disasm.h" =20 @@ -20927,6 +20928,11 @@ static int do_check(struct bpf_verifier_env *env) insn =3D &insns[env->insn_idx]; insn_aux =3D &env->insn_aux_data[env->insn_idx]; =20 + if (env->bcf.path_unreachable) { + env->bcf.path_unreachable =3D false; + goto process_bpf_exit; + } + if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { verbose(env, "BPF program is too large. Processed %d insn\n", @@ -24123,6 +24129,9 @@ static int do_check_common(struct bpf_verifier_env = *env, int subprog) struct bpf_reg_state *regs; int ret, i; =20 + if (env->cur_state) + goto skip_init; + env->prev_insn_idx =3D -1; env->prev_linfo =3D NULL; env->pass_cnt++; @@ -24236,6 +24245,7 @@ static int do_check_common(struct bpf_verifier_env = *env, int subprog) acquire_reference(env, 0) : 0; } =20 +skip_init: ret =3D do_check(env); =20 /* Invoked by bcf_track(), just return. */ @@ -24571,7 +24581,9 @@ static int do_check_subprogs(struct bpf_verifier_en= v *env) =20 again: new_cnt =3D 0; - for (i =3D 1; i < env->subprog_cnt; i++) { + /* env->cur_state indicates the resume mode, check the last subprog */ + i =3D env->cur_state ? env->subprog : 1; + for (; i < env->subprog_cnt; i++) { if (!subprog_is_global(env, i)) continue; =20 @@ -24580,8 +24592,10 @@ static int do_check_subprogs(struct bpf_verifier_e= nv *env) continue; =20 env->subprog =3D i; - env->insn_idx =3D env->subprog_info[i].start; - WARN_ON_ONCE(env->insn_idx =3D=3D 0); + if (!env->cur_state) { + env->insn_idx =3D env->subprog_info[i].start; + WARN_ON_ONCE(env->insn_idx =3D=3D 0); + } ret =3D do_check_common(env, i); if (ret) { return ret; @@ -24611,7 +24625,10 @@ static int do_check_main(struct bpf_verifier_env *= env) { int ret; =20 - env->insn_idx =3D 0; + if (env->subprog) + return 0; + if (!env->cur_state) + env->insn_idx =3D 0; ret =3D do_check_common(env, 0); if (!ret) env->prog->aux->stack_depth =3D env->subprog_info[0].stack_depth; @@ -25852,13 +25869,45 @@ static int do_request_bcf(struct bpf_verifier_env= *env, union bpf_attr *attr, return 0; } =20 +static int resume_env(struct bpf_verifier_env *env, union bpf_attr *attr, + bpfptr_t uattr) +{ + bpfptr_t proof; + int cond, err; + + unmark_bcf_requested(env); + + cond =3D env->bcf.refine_cond; + if (attr->bcf_flags & BCF_F_PROOF_PATH_UNREACHABLE) { + cond =3D env->bcf.path_cond; + env->bcf.path_unreachable =3D true; + } + if (cond < 0) + return -EINVAL; + + proof =3D make_bpfptr(attr->bcf_buf, uattr.is_kernel); + err =3D bcf_check_proof(env->bcf.exprs, cond, proof, + attr->bcf_buf_true_size, + (void *)bpf_verifier_vlog, env->log.level, + &env->log); + if (err) + return err; + + /* Drop the last history entry */ + if (is_jmp_point(env, env->insn_idx)) + env->cur_state->jmp_history_cnt--; + + return 0; +} + int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr= , __u32 uattr_size) { u64 start_time =3D ktime_get_ns(); struct bpf_verifier_env *env; int i, len, ret =3D -EINVAL, err; u32 log_true_size; - bool is_priv; + bool is_priv, resume; + struct fd bcf_fd; =20 BTF_TYPE_EMIT(enum bpf_features); =20 @@ -25866,6 +25915,24 @@ int bpf_check(struct bpf_prog **prog, union bpf_at= tr *attr, bpfptr_t uattr, __u3 if (ARRAY_SIZE(bpf_verifier_ops) =3D=3D 0) return -EINVAL; =20 + resume =3D !!(attr->bcf_flags & BCF_F_PROOF_PROVIDED); + if (resume) { + struct file *f; + + bcf_fd =3D fdget(attr->bcf_fd); + f =3D fd_file(bcf_fd); + if (!f) + return -EBADF; + env =3D f->private_data; + if (f->f_op !=3D &bcf_fops || + atomic_cmpxchg(&env->bcf.in_use, 0, 1)) { + fdput(bcf_fd); + return -EINVAL; + } + is_priv =3D env->bpf_capable; + goto verifier_check; + } + /* 'struct bpf_verifier_env' can be global, but since it's not small, * allocate/free it every time bpf_check() is called */ @@ -25999,6 +26066,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_at= tr *attr, bpfptr_t uattr, __u3 if (ret < 0) goto skip_full_check; =20 +verifier_check: + if (resume) { + ret =3D resume_env(env, attr, uattr); + if (ret) + goto skip_full_check; + } ret =3D do_check_main(env); ret =3D ret ?: do_check_subprogs(env); =20 @@ -26006,8 +26079,13 @@ int bpf_check(struct bpf_prog **prog, union bpf_at= tr *attr, bpfptr_t uattr, __u3 u64 vtime =3D ktime_get_ns() - start_time; =20 env->verification_time +=3D vtime; - if (do_request_bcf(env, attr, uattr) =3D=3D 0) + if (do_request_bcf(env, attr, uattr) =3D=3D 0) { + if (resume) { + atomic_set(&env->bcf.in_use, 0); + fdput(bcf_fd); + } return ret; + } =20 unmark_bcf_requested(env); env->verification_time -=3D vtime; @@ -26017,6 +26095,10 @@ int bpf_check(struct bpf_prog **prog, union bpf_at= tr *attr, bpfptr_t uattr, __u3 ret =3D bpf_prog_offload_finalize(env); =20 skip_full_check: + if (resume) { + fd_file(bcf_fd)->private_data =3D NULL; + fdput(bcf_fd); + } /* If bcf_requested(), the last state is preserved, free now. */ if (env->cur_state) free_states(env); --=20 2.34.1 From nobody Fri Dec 19 16:01:03 2025 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2348633C531 for ; Thu, 6 Nov 2025 12:53:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433630; cv=none; b=gxhOELFrwbSe8IY1FnHgSjcW5DiBT8Mc5QBFOsStpn8AWJF+VBeM4fP6t8ZK5S03uQ+sa1SXIBvgZZPk+i80VvTBDs+iLsrs+DehT7A/54RsiqZD2Cv/hWK4Xm4I77rqve640NTfnVuxh8Sep9HtMRC/2P9h4Bsp+czSKjVDyqg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1762433630; c=relaxed/simple; bh=Q+gPCrBvqV9eKYawaaZc594PBuJnFIBv2mWZKk6piR8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uAxyceVyo05Zt0cKmg+YfcrlUp6CW+6meRW+ywT41kyM8bwhve0ikT13LY9zjg58pxiA+p1e3FyBh0tLNkHfRjtaRXGhYxr240JbmtKOeQ4xXFLxG2P85v7hE+UxZxXAxbhWHuvUJPKzQfqCP6eZf41Fx6VzmzoTI4lcI5URBsI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=gCH6GiVv; arc=none smtp.client-ip=209.85.221.54 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.b="gCH6GiVv" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-429bf011e6cso943932f8f.1 for ; Thu, 06 Nov 2025 04:53:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762433626; x=1763038426; 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; bh=K34fjCYCGoSJdwCxfEsku+tPUzx/FlDGUOaZ5xDsmCE=; b=gCH6GiVveRDMs7EoevQ8mPsqJ7GoXyb7Wzlx3oNyF9R+WxBEHvZXnzB7QkdG5qG+4a PJMamgzZIM6+1CBE2P48CI6tGnlOGztEz/MC0pCChRh+SK4yCIFFB6J4pXyqGzUmS2Ix 1lqX7n4vNdkcqm1FXrdKJ7W6VCLvypcb3hn6IxbORgHFKBUzR0XAkILT4D1Ys8F3EMGC i7rtN98eCRHHco2k5nb33pfJmC2i6F40OGI5m5/gC21Oe1Ji3AOcTZ85SaEsqOeyERYF d66+Q66n3m6GR/TGNjob0Pailh+lECMQLBxFO0tj7lyGVG/cAzii1u5ZDFXnsmh2O1Pu l47Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762433626; x=1763038426; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=K34fjCYCGoSJdwCxfEsku+tPUzx/FlDGUOaZ5xDsmCE=; b=BkkyCLgzZOSJ3/n3T7NpeifaqaO2J25X/VS6EnJjx3EXdfcZnRCIYmy7BSmFukAoAM lA0bGmoZAgEi9s7qdVRJ0ipPk+Gax2b8CqG1iRp5fEh7EiQ3theD+z+yGNHgXVTQ9WI+ IN/Q+OnxlC37F0NVIpICNbUFssunFEXP4N7Xbuh6PsB8/5oxujB/NbU12r5FOi+BJGfU HxOOvEQmwD7eE+wL4B4Q/pM0WWDqn9kjFBn3SVXyLyuhE5/b0MOMvKFv7Reo8WjUOVgp roagPN7UHE1S9HuFoxRwKwzxAtcUraITkGGlc5Mv/kHGBKCT0rAQ+UoMU4Fz07iHPqW+ 3fwA== X-Forwarded-Encrypted: i=1; AJvYcCWe/7T48VWs/QlJdN6flMZX1jDf3ifWfu35P+cw7ohiD9+RsIhaMSpmcpISTCcq9zwwy8fNf6IZ9qPYOVg=@vger.kernel.org X-Gm-Message-State: AOJu0YwinB5S/4DUzOX2YeS4jQSW5Pf3VfVf7Pbq0dNGbFpAWl0XevhD iFJ61gagfxCocgaGT8SXpWJ2ORP8ABMaA3fwyARAtGGE6wuNcqbUzhNqlI6a X-Gm-Gg: ASbGncuxqH7jvhNhLPHBNn/1yZU6pw7V5srh7i62GyJdMgmdwLfNDPLE7VEZnL6MZO1 UDbJ+uO8fVtROPKi71Qj8B0JWH3SiaBN43z9ZvbY8gR0EYZhQBkW6UEf3UEuBX+MbD9nE/7X1Jb IXY+E7Bj8oDrVYmEC1NpxzsqR4cUeLZa2SEgAEwyVrRpghFpsg5teZKBRcUWvgE4FmoUivvMIMp RteiGKBIE97hJH19bQ7nGRaN8vNf+fdtRP3QvFVNxKnHgnHpoc1SNN1kSXCwlQx/0nQu2xTWciC 4OFMHApfnW5a3b+WZjb3vzcUPlcACktMpofzxpyWpVyhougMJ+SPzJj8r47AbpoAYOaCa/NyYYX C0d5DSYVZ/D0+b0DiDviWls4nv67xqZAYhk1lAhxPmHtvP1WfNggERpQhPHKrNGbmQzCHTjqBcP OdqKe/oxY24zQcRmRXw7A5GH0JfHxDz76TR4BUELG+zgf1PeNHwa4itsk= X-Google-Smtp-Source: AGHT+IEsHuLNMGEHDB74IdUzyIrXs5wkTefXkfvvLRH9gq2+IppFGexW3BvrP1HGL9GdfsxYTxaF9Q== X-Received: by 2002:a05:6000:64b:b0:429:dde3:6576 with SMTP id ffacd0b85a97d-429e32e6c08mr5782407f8f.16.1762433626184; Thu, 06 Nov 2025 04:53:46 -0800 (PST) Received: from ast-epyc5.inf.ethz.ch (ast-epyc5.inf.ethz.ch. [129.132.161.180]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-429eb40379esm4788856f8f.9.2025.11.06.04.53.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 06 Nov 2025 04:53:45 -0800 (PST) From: Hao Sun X-Google-Original-From: Hao Sun To: bpf@vger.kernel.org Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org, eddyz87@gmail.com, john.fastabend@gmail.com, martin.lau@linux.dev, song@kernel.org, yonghong.song@linux.dev, linux-kernel@vger.kernel.org, sunhao.th@gmail.com, Hao Sun Subject: [PATCH RFC 17/17] bpf: Enable bcf for priv users Date: Thu, 6 Nov 2025 13:52:55 +0100 Message-Id: <20251106125255.1969938-18-hao.sun@inf.ethz.ch> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> References: <20251106125255.1969938-1-hao.sun@inf.ethz.ch> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Enable bcf when bcf_buf is provided and is_priv. Signed-off-by: Hao Sun --- kernel/bpf/verifier.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index fb672c9cc7cd..d48357bac9f3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -25961,6 +25961,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_att= r *attr, bpfptr_t uattr, __u3 env->bypass_spec_v1 =3D bpf_bypass_spec_v1(env->prog->aux->token); env->bypass_spec_v4 =3D bpf_bypass_spec_v4(env->prog->aux->token); env->bpf_capable =3D is_priv =3D bpf_token_capable(env->prog->aux->token,= CAP_BPF); + env->bcf.available =3D is_priv && attr->bcf_buf_size; =20 bpf_get_btf_vmlinux(); =20 --=20 2.34.1