Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion crates/rk/src/sections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> {
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
Expand Down Expand Up @@ -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)))
})
}

Expand Down Expand Up @@ -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();
Expand Down
13 changes: 13 additions & 0 deletions sections-divergences.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading