diff --git a/crates/rk/src/sections.rs b/crates/rk/src/sections.rs index 33e4be1..e7dfc26 100644 --- a/crates/rk/src/sections.rs +++ b/crates/rk/src/sections.rs @@ -643,6 +643,17 @@ fn matches(pattern: &str, s: &str) -> bool { } } +/// A bug entry's site without the condition text of its WARN. +/// +/// With `DEBUG_BUGVERBOSE_DETAILED` a `WARN_ON(cond)` puts `#cond` where the format of a `WARN` +/// goes, so allmodconfig prints `4="!p" 8="mm/x.c" 1c000309` for the site defconfig prints as +/// `4="" 8="mm/x.c" 1c000309`. The file, line and flags still name it. +fn uncondition(item: &str) -> Option { + let rest = item.strip_prefix("4=\"")?; + let at = rest.find("\" 8=\"")?; + (at > 0).then(|| format!("4=\"\"{}", &rest[at + 1..])) +} + /// Whether a rule for `object` follows `item` into the module `object` is linked into. /// /// A bug entry names the file of its WARN or BUG. One that names `fs/btrfs/volumes.c` was compiled @@ -689,14 +700,17 @@ impl Divergences { } /// The rule that explains one item of a difference, if there is one. + /// + /// A bug entry with the condition text of its WARN is the site a rule names without it. fn rule(&self, d: &Difference, only: Only, item: &str) -> Option<&Divergence> { + let bare = uncondition(item); self.divergences.iter().find(|r| { r.what == d.what && (r.only == only || r.only == Only::Either) && r.object .as_deref() .is_none_or(|o| matches(o, &d.object) || linked(o, item)) - && matches(&r.item, item) + && (matches(&r.item, item) || bare.as_deref().is_some_and(|b| matches(&r.item, b))) }) } @@ -1591,6 +1605,21 @@ reason = "checked in a.c" std::fs::remove_dir_all(&dir).unwrap(); } + #[test] + fn a_site_with_its_condition_is_the_site_without_it() { + let rules = Divergences::parse( + "[[divergence]]\nwhat = \"__bug_table\"\nonly = \"reference\"\n\ + item = '4=\"\" 8=\"mm/x.c\" 1c000309'\nreason = \"r\"\n", + ) + .unwrap(); + let d = plain("mm/x.o", "__bug_table", String::new(), String::new()); + let at = |item: &str| rules.rule(&d, Only::Reference, item).is_some(); + assert!(at("4=\"\" 8=\"mm/x.c\" 1c000309")); + assert!(at("4=\"!p\" 8=\"mm/x.c\" 1c000309")); + assert!(!at("4=\"!p\" 8=\"mm/x.c\" 1d000309")); + assert_eq!(uncondition("4=\"\" 8=\"mm/x.c\" 1c000309"), None); + } + #[test] fn the_divergences_file_parses_and_every_rule_has_a_reason() { let rules = Divergences::parse(include_str!("../../../sections-divergences.toml")).unwrap(); diff --git a/sections-divergences.toml b/sections-divergences.toml index 4c11dca..b4e844e 100644 --- a/sections-divergences.toml +++ b/sections-divergences.toml @@ -2315,6 +2315,19 @@ only = "reference" item = '4="net/bluetooth/hci_event.c" f5170909' reason = "the field spanning WARN of memcpy(conn->le_adv_data, data, len) in process_adv_report, which runs under len <= max_adv_len(hdev), 31 or 251, the size of le_adv_data. rucc carries the bound" +[[divergence]] +what = "__bug_table" +only = "reference" +item = '4="" 8="include/linux/netfilter.h" *' +reason = "the two WARN_ON_ONCEs in nf_hook, the hook >= ARRAY_SIZE(hooks_arp) for NFPROTO_ARP and the one in the default case. Every NF_HOOK and nf_hook call passes pf and hook as constants, so rucc inlines nf_hook into each caller and folds the switch, where gcc keeps one copy with both checks" + +[[divergence]] +what = "__bug_table" +object = "drivers/net/ethernet/mellanox/mlxsw/*" +only = "reference" +item = '4="" 8="drivers/net/ethernet/mellanox/mlxsw/item.h" *' +reason = "the BUG_ON(index && !item->step) and the alignment BUG() in __mlxsw_item_offset on 7.2. The items are const file scope MLXSW_ITEMs, so once rucc folds them each check is false, and gcc keeps the checks in its out of line copies such as __mlxsw_item_get32.constprop.0" + [[divergence]] what = "__bug_table" object = "net/bridge/br_netfilter.o"