Auto merge of #149022 - RalfJung:miri, r=RalfJung
miri subtree update
Contains the fix for https://github.com/rust-lang/miri/issues/4698, which we should get shipped ASAP.
Subtree update of `miri` to 0fea24aee0.
Created using https://github.com/rust-lang/josh-sync.
r? `@ghost`
This commit is contained in:
commit
42ebbd2356
58 changed files with 2584 additions and 1210 deletions
39
Cargo.lock
39
Cargo.lock
|
|
@ -396,7 +396,7 @@ dependencies = [
|
|||
name = "cargo-miri"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cargo_metadata 0.21.0",
|
||||
"cargo_metadata 0.23.1",
|
||||
"directories",
|
||||
"rustc-build-sysroot",
|
||||
"rustc_tools_util 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
@ -423,6 +423,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-util-schemas"
|
||||
version = "0.8.2"
|
||||
|
|
@ -468,6 +477,20 @@ dependencies = [
|
|||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform 0.3.1",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargotest2"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2157,6 +2180,16 @@ dependencies = [
|
|||
"windows-targets 0.53.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
|
|
@ -2404,7 +2437,7 @@ dependencies = [
|
|||
"ipc-channel",
|
||||
"libc",
|
||||
"libffi",
|
||||
"libloading",
|
||||
"libloading 0.9.0",
|
||||
"measureme",
|
||||
"nix",
|
||||
"rand 0.9.2",
|
||||
|
|
@ -4152,7 +4185,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libloading",
|
||||
"libloading 0.8.8",
|
||||
"odht",
|
||||
"rustc_abi",
|
||||
"rustc_ast",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -40,7 +40,7 @@ features = ['unprefixed_malloc_on_supported_platforms']
|
|||
libc = "0.2"
|
||||
# native-lib dependencies
|
||||
libffi = { version = "5.0.0", optional = true }
|
||||
libloading = { version = "0.8", optional = true }
|
||||
libloading = { version = "0.9", optional = true }
|
||||
serde = { version = "1.0.219", features = ["derive"], optional = true }
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
|
|
@ -72,7 +72,7 @@ harness = false
|
|||
default = ["stack-cache", "native-lib"]
|
||||
genmc = ["dep:genmc-sys"]
|
||||
stack-cache = []
|
||||
stack-cache-consistency-check = ["stack-cache"]
|
||||
expensive-consistency-checks = ["stack-cache"]
|
||||
tracing = ["serde_json"]
|
||||
native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel", "dep:nix", "dep:serde"]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,29 +4,23 @@ version = 4
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.1"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.10"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
|
||||
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -44,38 +38,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.2.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4"
|
||||
checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-util-schemas"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
|
||||
dependencies = [
|
||||
"semver",
|
||||
"serde",
|
||||
"serde-untagged",
|
||||
"serde-value",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"unicode-xid",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.21.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"cargo-util-schemas",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -84,9 +61,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "directories"
|
||||
|
|
@ -106,18 +83,7 @@ dependencies = [
|
|||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -126,24 +92,14 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -152,15 +108,6 @@ version = "2.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
|
|
@ -169,139 +116,32 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.4"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"potential_utf",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.10.0"
|
||||
version = "2.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
|
||||
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
|
|
@ -315,15 +155,15 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.4"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
|
|
@ -331,30 +171,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
|
|
@ -368,44 +193,20 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-float"
|
||||
version = "2.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -418,9 +219,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.0"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
|
|
@ -429,9 +230,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc-build-sysroot"
|
||||
version = "0.5.10"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd41ead66a69880951b2f7df3139db401d44451b4da123344d27eaa791b89c95"
|
||||
checksum = "3b881c015c729b43105bbd3702a9bdecee28fafaa21126d1d62e454ec011a4b7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rustc_version",
|
||||
|
|
@ -457,15 +258,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -485,48 +286,38 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-untagged"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e"
|
||||
dependencies = [
|
||||
"erased-serde",
|
||||
"serde",
|
||||
"typeid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-value"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
|
||||
dependencies = [
|
||||
"ordered-float",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -535,14 +326,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -554,83 +346,50 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
version = "2.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
version = "3.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.23"
|
||||
|
|
@ -673,40 +432,11 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "typeid"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
|
|
@ -725,267 +455,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.0",
|
||||
"windows_aarch64_msvc 0.53.0",
|
||||
"windows_i686_gnu 0.53.0",
|
||||
"windows_i686_gnullvm 0.53.0",
|
||||
"windows_i686_msvc 0.53.0",
|
||||
"windows_x86_64_gnu 0.53.0",
|
||||
"windows_x86_64_gnullvm 0.53.0",
|
||||
"windows_x86_64_msvc 0.53.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.12"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
|
||||
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ doctest = false # and no doc tests
|
|||
directories = "6"
|
||||
rustc_version = "0.4"
|
||||
serde_json = "1.0.40"
|
||||
cargo_metadata = "0.21"
|
||||
cargo_metadata = "0.23"
|
||||
rustc-build-sysroot = "0.5.10"
|
||||
|
||||
# Enable some feature flags that dev-dependencies need but dependencies
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ mod downloading {
|
|||
/// The GenMC repository the we get our commit from.
|
||||
pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git";
|
||||
/// The GenMC commit we depend on. It must be available on the specified GenMC repository.
|
||||
pub(crate) const GENMC_COMMIT: &str = "d9527280bb99f1cef64326b1803ffd952e3880df";
|
||||
pub(crate) const GENMC_COMMIT: &str = "aa10ed65117c3291524efc19253b5d443a4602ac";
|
||||
|
||||
/// Ensure that a local GenMC repo is present and set to the correct commit.
|
||||
/// Return the path of the GenMC repo and whether the checked out commit was changed.
|
||||
|
|
@ -140,51 +140,6 @@ mod downloading {
|
|||
}
|
||||
}
|
||||
|
||||
// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed.
|
||||
/// The linked LLVM version is in the generated `config.h`` file, which we parse and use to link to LLVM.
|
||||
/// Returns c++ compiler definitions required for building with/including LLVM, and the include path for LLVM headers.
|
||||
fn link_to_llvm(config_file: &Path) -> (String, String) {
|
||||
/// Search a string for a line matching `//@VARIABLE_NAME: VARIABLE CONTENT`
|
||||
fn extract_value<'a>(input: &'a str, name: &str) -> Option<&'a str> {
|
||||
input
|
||||
.lines()
|
||||
.find_map(|line| line.strip_prefix("//@")?.strip_prefix(name)?.strip_prefix(": "))
|
||||
}
|
||||
|
||||
let file_content = std::fs::read_to_string(&config_file).unwrap_or_else(|err| {
|
||||
panic!("GenMC config file ({}) should exist, but got errror {err:?}", config_file.display())
|
||||
});
|
||||
|
||||
let llvm_definitions = extract_value(&file_content, "LLVM_DEFINITIONS")
|
||||
.expect("Config file should contain LLVM_DEFINITIONS");
|
||||
let llvm_include_dirs = extract_value(&file_content, "LLVM_INCLUDE_DIRS")
|
||||
.expect("Config file should contain LLVM_INCLUDE_DIRS");
|
||||
let llvm_library_dir = extract_value(&file_content, "LLVM_LIBRARY_DIR")
|
||||
.expect("Config file should contain LLVM_LIBRARY_DIR");
|
||||
let llvm_config_path = extract_value(&file_content, "LLVM_CONFIG_PATH")
|
||||
.expect("Config file should contain LLVM_CONFIG_PATH");
|
||||
|
||||
// Add linker search path.
|
||||
let lib_dir = PathBuf::from_str(llvm_library_dir).unwrap();
|
||||
println!("cargo::rustc-link-search=native={}", lib_dir.display());
|
||||
|
||||
// Add libraries to link.
|
||||
let output = std::process::Command::new(llvm_config_path)
|
||||
.arg("--libs") // Print the libraries to link to (space-separated list)
|
||||
.output()
|
||||
.expect("failed to execute llvm-config");
|
||||
let llvm_link_libs =
|
||||
String::try_from(output.stdout).expect("llvm-config output should be a valid string");
|
||||
|
||||
for link_lib in llvm_link_libs.trim().split(" ") {
|
||||
let link_lib =
|
||||
link_lib.strip_prefix("-l").expect("Linker parameter should start with \"-l\"");
|
||||
println!("cargo::rustc-link-lib=dylib={link_lib}");
|
||||
}
|
||||
|
||||
(llvm_definitions.to_string(), llvm_include_dirs.to_string())
|
||||
}
|
||||
|
||||
/// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs
|
||||
fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
|
||||
// Give each step a separate build directory to prevent interference.
|
||||
|
|
@ -204,6 +159,7 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
|
|||
.always_configure(always_configure) // We force running the configure step when the GenMC commit changed.
|
||||
.out_dir(genmc_build_dir)
|
||||
.profile(GENMC_CMAKE_PROFILE)
|
||||
.define("BUILD_LLI", "OFF")
|
||||
.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" });
|
||||
|
||||
// The actual compilation happens here:
|
||||
|
|
@ -214,19 +170,11 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
|
|||
println!("cargo::rustc-link-search=native={}", cmake_lib_dir.display());
|
||||
println!("cargo::rustc-link-lib=static={GENMC_MODEL_CHECKER}");
|
||||
|
||||
// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed.
|
||||
let config_file = genmc_install_dir.join("include").join("genmc").join("config.h");
|
||||
let (llvm_definitions, llvm_include_dirs) = link_to_llvm(&config_file);
|
||||
|
||||
// Part 2:
|
||||
// Compile the cxx_bridge (the link between the Rust and C++ code).
|
||||
|
||||
let genmc_include_dir = genmc_install_dir.join("include").join("genmc");
|
||||
|
||||
// FIXME(genmc,llvm): remove once LLVM dependency is removed.
|
||||
// These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated.
|
||||
let definitions = llvm_definitions.split(";");
|
||||
|
||||
// These are all the C++ files we need to compile, which needs to be updated if more C++ files are added to Miri.
|
||||
// We use absolute paths since relative paths can confuse IDEs when attempting to go-to-source on a path in a compiler error.
|
||||
let cpp_files_base_path = Path::new("cpp/src/");
|
||||
|
|
@ -244,16 +192,12 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
|
|||
if enable_genmc_debug {
|
||||
bridge.define("ENABLE_GENMC_DEBUG", None);
|
||||
}
|
||||
for definition in definitions {
|
||||
bridge.flag(definition);
|
||||
}
|
||||
bridge
|
||||
.opt_level(2)
|
||||
.debug(true) // Same settings that GenMC uses (default for cmake `RelWithDebInfo`)
|
||||
.warnings(false) // NOTE: enabling this produces a lot of warnings.
|
||||
.std("c++23")
|
||||
.include(genmc_include_dir)
|
||||
.include(llvm_include_dirs)
|
||||
.include("./cpp/include")
|
||||
.files(&cpp_files)
|
||||
.out_dir(interface_build_dir)
|
||||
|
|
|
|||
|
|
@ -220,7 +220,8 @@ struct MiriGenmcShim : private GenMCDriver {
|
|||
auto handle_load_reset_if_none(ThreadId tid, std::optional<SVal> old_val, Ts&&... params)
|
||||
-> HandleResult<SVal> {
|
||||
const auto pos = inc_pos(tid);
|
||||
const auto ret = GenMCDriver::handleLoad<k>(pos, old_val, std::forward<Ts>(params)...);
|
||||
const auto ret =
|
||||
GenMCDriver::handleLoad<k>(nullptr, pos, old_val, std::forward<Ts>(params)...);
|
||||
// If we didn't get a value, we have to reset the index of the current thread.
|
||||
if (!std::holds_alternative<SVal>(ret)) {
|
||||
dec_pos(tid);
|
||||
|
|
|
|||
|
|
@ -7,14 +7,16 @@
|
|||
// GenMC headers:
|
||||
#include "Verification/VerificationError.hpp"
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
/** Information about an error, formatted as a string to avoid having to share an error enum and
|
||||
* printing functionality with the Rust side. */
|
||||
static auto format_error(VerificationError err) -> std::unique_ptr<std::string> {
|
||||
auto buf = std::string();
|
||||
auto s = llvm::raw_string_ostream(buf);
|
||||
s << err;
|
||||
std::stringstream s;
|
||||
s << std::format("{}", err);
|
||||
return std::make_unique<std::string>(s.str());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
void MiriGenmcShim::handle_assume_block(ThreadId thread_id, AssumeType assume_type) {
|
||||
BUG_ON(getExec().getGraph().isThreadBlocked(thread_id));
|
||||
GenMCDriver::handleAssume(inc_pos(thread_id), assume_type);
|
||||
GenMCDriver::handleAssume(nullptr, inc_pos(thread_id), assume_type);
|
||||
}
|
||||
|
||||
/**** Memory access handling ****/
|
||||
|
|
@ -76,6 +76,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id, AssumeType assume_ty
|
|||
) -> StoreResult {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
const auto ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::Write>(
|
||||
nullptr,
|
||||
pos,
|
||||
GenmcScalarExt::try_to_sval(old_val),
|
||||
ord,
|
||||
|
|
@ -100,7 +101,7 @@ void MiriGenmcShim::handle_assume_block(ThreadId thread_id, AssumeType assume_ty
|
|||
|
||||
void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
GenMCDriver::handleFence(pos, ord, EventDeps());
|
||||
GenMCDriver::handleFence(nullptr, pos, ord, EventDeps());
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MiriGenmcShim::handle_read_modify_write(
|
||||
|
|
@ -143,6 +144,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
|
|||
|
||||
const auto storePos = inc_pos(thread_id);
|
||||
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::FaiWrite>(
|
||||
nullptr,
|
||||
storePos,
|
||||
GenmcScalarExt::try_to_sval(old_val),
|
||||
ordering,
|
||||
|
|
@ -210,6 +212,7 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
|
|||
|
||||
const auto storePos = inc_pos(thread_id);
|
||||
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::CasWrite>(
|
||||
nullptr,
|
||||
storePos,
|
||||
GenmcScalarExt::try_to_sval(old_val),
|
||||
success_ordering,
|
||||
|
|
@ -242,6 +245,7 @@ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t al
|
|||
const auto address_space = AddressSpace::AS_User;
|
||||
|
||||
const SVal ret_val = GenMCDriver::handleMalloc(
|
||||
nullptr,
|
||||
pos,
|
||||
size,
|
||||
alignment,
|
||||
|
|
@ -255,7 +259,7 @@ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t al
|
|||
|
||||
auto MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) -> bool {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
GenMCDriver::handleFree(pos, SAddr(address), EventDeps());
|
||||
GenMCDriver::handleFree(nullptr, pos, SAddr(address), EventDeps());
|
||||
// FIXME(genmc): use returned error from `handleFree` once implemented in GenMC.
|
||||
return getResult().status.has_value();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,23 @@ auto MiriGenmcShim::schedule_next(
|
|||
// a scheduling decision.
|
||||
threads_action_[curr_thread_id].kind = curr_thread_next_instr_kind;
|
||||
|
||||
if (const auto result = GenMCDriver::scheduleNext(threads_action_))
|
||||
return SchedulingResult { ExecutionState::Ok, static_cast<int32_t>(result.value()) };
|
||||
if (getExec().getGraph().isBlocked())
|
||||
return SchedulingResult { ExecutionState::Blocked, 0 };
|
||||
if (getResult().status.has_value()) // the "value" here is a `VerificationError`
|
||||
return SchedulingResult { ExecutionState::Error, 0 };
|
||||
return SchedulingResult { ExecutionState::Finished, 0 };
|
||||
auto result = GenMCDriver::scheduleNext(threads_action_);
|
||||
return std::visit(
|
||||
[](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, int>)
|
||||
return SchedulingResult { ExecutionState::Ok, static_cast<int32_t>(arg) };
|
||||
else if constexpr (std::is_same_v<T, Blocked>)
|
||||
return SchedulingResult { ExecutionState::Blocked, 0 };
|
||||
else if constexpr (std::is_same_v<T, Error>)
|
||||
return SchedulingResult { ExecutionState::Error, 0 };
|
||||
else if constexpr (std::is_same_v<T, Finished>)
|
||||
return SchedulingResult { ExecutionState::Finished, 0 };
|
||||
else
|
||||
static_assert(false, "non-exhaustive visitor!");
|
||||
},
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
/**** Execution start/end handling ****/
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ auto MiriGenmcShim::handle_mutex_lock(ThreadId thread_id, uint64_t address, uint
|
|||
const bool is_lock_acquired = *ret_val == MUTEX_UNLOCKED;
|
||||
if (is_lock_acquired) {
|
||||
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::LockCasWrite>(
|
||||
nullptr,
|
||||
inc_pos(thread_id),
|
||||
old_val,
|
||||
address,
|
||||
|
|
@ -93,6 +94,7 @@ auto MiriGenmcShim::handle_mutex_try_lock(ThreadId thread_id, uint64_t address,
|
|||
// a mutex to be "unlocked".
|
||||
const auto old_val = MUTEX_UNLOCKED;
|
||||
const auto load_ret = GenMCDriver::handleLoad<EventLabel::EventLabelKind::TrylockCasRead>(
|
||||
nullptr,
|
||||
++currPos,
|
||||
old_val,
|
||||
SAddr(address),
|
||||
|
|
@ -115,6 +117,7 @@ auto MiriGenmcShim::handle_mutex_try_lock(ThreadId thread_id, uint64_t address,
|
|||
}
|
||||
|
||||
const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::TrylockCasWrite>(
|
||||
nullptr,
|
||||
++currPos,
|
||||
old_val,
|
||||
SAddr(address),
|
||||
|
|
@ -136,6 +139,7 @@ auto MiriGenmcShim::handle_mutex_unlock(ThreadId thread_id, uint64_t address, ui
|
|||
-> StoreResult {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
const auto ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::UnlockWrite>(
|
||||
nullptr,
|
||||
pos,
|
||||
// As usual, we need to tell GenMC which value was stored at this location before this
|
||||
// atomic access, if there previously was a non-atomic initializing access. We set the
|
||||
|
|
|
|||
|
|
@ -147,9 +147,16 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel
|
|||
// that is allowed to leak and memory that is not.
|
||||
conf->warnUnfreedMemory = false;
|
||||
|
||||
// FIXME(genmc,error handling): This function currently exits on error, but will return an
|
||||
// error value in the future. The return value should be checked once this change is made.
|
||||
checkConfig(*conf);
|
||||
// Validate the config and exit if there are any errors
|
||||
std::vector<std::string> warnings;
|
||||
auto config_valid = conf->validate(warnings);
|
||||
for (const auto& w : warnings)
|
||||
WARN("{}", w);
|
||||
if (auto* errors = std::get_if<ConfigErrorList>(&config_valid); errors) {
|
||||
for (const auto& e : *errors)
|
||||
LOG(VerbosityLevel::Error, "{}", e);
|
||||
exit(EUSER);
|
||||
}
|
||||
|
||||
// Create the actual driver and Miri-GenMC communication shim.
|
||||
auto driver = std::make_unique<MiriGenmcShim>(std::move(conf), mode);
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ void MiriGenmcShim::handle_thread_create(ThreadId thread_id, ThreadId parent_id)
|
|||
// FIXME(genmc): for supporting symmetry reduction, these will need to be properly set:
|
||||
const unsigned fun_id = 0;
|
||||
const SVal arg = SVal(0);
|
||||
const ThreadInfo child_info = ThreadInfo { thread_id, parent_id, fun_id, arg };
|
||||
const ThreadInfo child_info =
|
||||
ThreadInfo { thread_id, parent_id, fun_id, arg, "unknown thread" };
|
||||
|
||||
// NOTE: Default memory ordering (`Release`) used here.
|
||||
const auto child_tid = GenMCDriver::handleThreadCreate(pos, child_info, EventDeps());
|
||||
const auto child_tid = GenMCDriver::handleThreadCreate(nullptr, pos, child_info, EventDeps());
|
||||
// Sanity check the thread id, which is the index in the `threads_action_` array.
|
||||
BUG_ON(child_tid != thread_id || child_tid <= 0 || child_tid != threads_action_.size());
|
||||
threads_action_.push_back(Action(ActionKind::Load, Event(child_tid, 0)));
|
||||
|
|
@ -33,7 +34,7 @@ void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) {
|
|||
const auto pos = inc_pos(thread_id);
|
||||
|
||||
// NOTE: Default memory ordering (`Acquire`) used here.
|
||||
const auto ret = GenMCDriver::handleThreadJoin(pos, child_id, EventDeps());
|
||||
const auto ret = GenMCDriver::handleThreadJoin(nullptr, pos, child_id, EventDeps());
|
||||
// If the join failed, decrease the event index again:
|
||||
if (!std::holds_alternative<SVal>(ret)) {
|
||||
dec_pos(thread_id);
|
||||
|
|
@ -46,10 +47,10 @@ void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) {
|
|||
void MiriGenmcShim::handle_thread_finish(ThreadId thread_id, uint64_t ret_val) {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
// NOTE: Default memory ordering (`Release`) used here.
|
||||
GenMCDriver::handleThreadFinish(pos, SVal(ret_val));
|
||||
GenMCDriver::handleThreadFinish(nullptr, pos, SVal(ret_val));
|
||||
}
|
||||
|
||||
void MiriGenmcShim::handle_thread_kill(ThreadId thread_id) {
|
||||
const auto pos = inc_pos(thread_id);
|
||||
GenMCDriver::handleThreadKill(pos);
|
||||
GenMCDriver::handleThreadKill(nullptr, pos);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7a72c5459dd58f81b0e1a0e5436d145485889375
|
||||
69d4d5fc0e4db60272aac85ef27ecccef5764f3a
|
||||
|
|
|
|||
|
|
@ -158,19 +158,24 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||
_: &rustc_interface::interface::Compiler,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Compilation {
|
||||
// Compilation is done, interpretation is starting. Deal with diagnostics from the
|
||||
// compilation part. We cannot call `sess.finish_diagnostics()` as then "aborting due to
|
||||
// previous errors" gets printed twice.
|
||||
tcx.dcx().emit_stashed_diagnostics();
|
||||
tcx.dcx().abort_if_errors();
|
||||
tcx.dcx().flush_delayed();
|
||||
|
||||
// Miri is taking over. Start logging.
|
||||
init_late_loggers(&EarlyDiagCtxt::new(tcx.sess.opts.error_format), tcx);
|
||||
|
||||
// Find the entry point.
|
||||
if !tcx.crate_types().contains(&CrateType::Executable) {
|
||||
tcx.dcx().fatal("miri only makes sense on bin crates");
|
||||
}
|
||||
|
||||
let early_dcx = EarlyDiagCtxt::new(tcx.sess.opts.error_format);
|
||||
init_late_loggers(&early_dcx, tcx);
|
||||
|
||||
let (entry_def_id, entry_type) = entry_fn(tcx);
|
||||
let mut config = self.miri_config.take().expect("after_analysis must only be called once");
|
||||
|
||||
// Obtain and complete the Miri configuration.
|
||||
let mut config = self.miri_config.take().expect("after_analysis must only be called once");
|
||||
// Add filename to `miri` arguments.
|
||||
config.args.insert(0, tcx.sess.io.input.filestem().to_string());
|
||||
|
||||
|
|
@ -179,6 +184,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||
env::set_current_dir(cwd).unwrap();
|
||||
}
|
||||
|
||||
// Emit warnings for some unusual configurations.
|
||||
if tcx.sess.opts.optimize != OptLevel::No {
|
||||
tcx.dcx().warn("Miri does not support optimizations: the opt-level is ignored. The only effect \
|
||||
of selecting a Cargo profile that enables optimizations (such as --release) is to apply \
|
||||
|
|
@ -193,6 +199,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||
optimizations is usually marginal at best.");
|
||||
}
|
||||
|
||||
// Invoke the interpreter.
|
||||
let res = if config.genmc_config.is_some() {
|
||||
assert!(self.many_seeds.is_none());
|
||||
run_genmc_mode(tcx, &config, |genmc_ctx: Rc<GenmcCtx>| {
|
||||
|
|
@ -209,7 +216,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||
} else {
|
||||
miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
|
||||
};
|
||||
|
||||
// Process interpreter result.
|
||||
if let Err(return_code) = res {
|
||||
tcx.dcx().abort_if_errors();
|
||||
exit(return_code.get());
|
||||
|
|
@ -509,7 +516,6 @@ fn main() {
|
|||
Some(BorrowTrackerMethod::TreeBorrows(TreeBorrowsParams {
|
||||
precise_interior_mut: true,
|
||||
}));
|
||||
miri_config.provenance_mode = ProvenanceMode::Strict;
|
||||
} else if arg == "-Zmiri-tree-borrows-no-precise-interior-mut" {
|
||||
match &mut miri_config.borrow_tracker {
|
||||
Some(BorrowTrackerMethod::TreeBorrows(params)) => {
|
||||
|
|
@ -703,17 +709,6 @@ fn main() {
|
|||
rustc_args.push(arg);
|
||||
}
|
||||
}
|
||||
// Tree Borrows implies strict provenance, and is not compatible with native calls.
|
||||
if matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows { .. })) {
|
||||
if miri_config.provenance_mode != ProvenanceMode::Strict {
|
||||
fatal_error!(
|
||||
"Tree Borrows does not support integer-to-pointer casts, and hence requires strict provenance"
|
||||
);
|
||||
}
|
||||
if !miri_config.native_lib.is_empty() {
|
||||
fatal_error!("Tree Borrows is not compatible with calling native functions");
|
||||
}
|
||||
}
|
||||
|
||||
// Native calls and strict provenance are not compatible.
|
||||
if !miri_config.native_lib.is_empty() && miri_config.provenance_mode == ProvenanceMode::Strict {
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
|
||||
// The body of this loop needs `borrow_tracker` immutably
|
||||
// so we can't move this code inside the following `end_call`.
|
||||
|
||||
for (alloc_id, tag) in &frame
|
||||
.extra
|
||||
.borrow_tracker
|
||||
|
|
@ -378,6 +379,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
borrow_tracker.borrow_mut().end_call(&frame.extra);
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ impl<'tcx> Stack {
|
|||
/// Panics if any of the caching mechanisms have broken,
|
||||
/// - The StackCache indices don't refer to the parallel items,
|
||||
/// - There are no Unique items outside of first_unique..last_unique
|
||||
#[cfg(feature = "stack-cache-consistency-check")]
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
fn verify_cache_consistency(&self) {
|
||||
// Only a full cache needs to be valid. Also see the comments in find_granting_cache
|
||||
// and set_unknown_bottom.
|
||||
|
|
@ -197,7 +197,7 @@ impl<'tcx> Stack {
|
|||
tag: ProvenanceExtra,
|
||||
exposed_tags: &FxHashSet<BorTag>,
|
||||
) -> Result<Option<usize>, ()> {
|
||||
#[cfg(feature = "stack-cache-consistency-check")]
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
self.verify_cache_consistency();
|
||||
|
||||
let ProvenanceExtra::Concrete(tag) = tag else {
|
||||
|
|
@ -334,7 +334,7 @@ impl<'tcx> Stack {
|
|||
// This primes the cache for the next access, which is almost always the just-added tag.
|
||||
self.cache.add(new_idx, new);
|
||||
|
||||
#[cfg(feature = "stack-cache-consistency-check")]
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
self.verify_cache_consistency();
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +417,7 @@ impl<'tcx> Stack {
|
|||
self.unique_range.end = self.unique_range.end.min(disable_start);
|
||||
}
|
||||
|
||||
#[cfg(feature = "stack-cache-consistency-check")]
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
self.verify_cache_consistency();
|
||||
|
||||
interp_ok(())
|
||||
|
|
@ -472,7 +472,7 @@ impl<'tcx> Stack {
|
|||
self.unique_range = 0..0;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stack-cache-consistency-check")]
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
self.verify_cache_consistency();
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,9 +291,10 @@ pub(super) struct TbError<'node> {
|
|||
pub conflicting_info: &'node NodeDebugInfo,
|
||||
// What kind of access caused this error (read, write, reborrow, deallocation)
|
||||
pub access_cause: AccessCause,
|
||||
/// Which tag the access that caused this error was made through, i.e.
|
||||
/// Which tag, if any, the access that caused this error was made through, i.e.
|
||||
/// which tag was used to read/write/deallocate.
|
||||
pub accessed_info: &'node NodeDebugInfo,
|
||||
/// Not set on wildcard accesses.
|
||||
pub accessed_info: Option<&'node NodeDebugInfo>,
|
||||
}
|
||||
|
||||
impl TbError<'_> {
|
||||
|
|
@ -302,10 +303,20 @@ impl TbError<'_> {
|
|||
use TransitionError::*;
|
||||
let cause = self.access_cause;
|
||||
let accessed = self.accessed_info;
|
||||
let accessed_str =
|
||||
self.accessed_info.map(|v| format!("{v}")).unwrap_or_else(|| "<wildcard>".into());
|
||||
let conflicting = self.conflicting_info;
|
||||
let accessed_is_conflicting = accessed.tag == conflicting.tag;
|
||||
// An access is considered conflicting if it happened through a
|
||||
// different tag than the one who caused UB.
|
||||
// When doing a wildcard access (where `accessed` is `None`) we
|
||||
// do not know which precise tag the accessed happened from,
|
||||
// however we can be certain that it did not come from the
|
||||
// conflicting tag.
|
||||
// This is because the wildcard data structure already removes
|
||||
// all tags through which an access would cause UB.
|
||||
let accessed_is_conflicting = accessed.map(|a| a.tag) == Some(conflicting.tag);
|
||||
let title = format!(
|
||||
"{cause} through {accessed} at {alloc_id:?}[{offset:#x}] is forbidden",
|
||||
"{cause} through {accessed_str} at {alloc_id:?}[{offset:#x}] is forbidden",
|
||||
alloc_id = self.alloc_id,
|
||||
offset = self.error_offset
|
||||
);
|
||||
|
|
@ -316,7 +327,7 @@ impl TbError<'_> {
|
|||
let mut details = Vec::new();
|
||||
if !accessed_is_conflicting {
|
||||
details.push(format!(
|
||||
"the accessed tag {accessed} is a child of the conflicting tag {conflicting}"
|
||||
"the accessed tag {accessed_str} is a child of the conflicting tag {conflicting}"
|
||||
));
|
||||
}
|
||||
let access = cause.print_as_access(/* is_foreign */ false);
|
||||
|
|
@ -330,7 +341,7 @@ impl TbError<'_> {
|
|||
let access = cause.print_as_access(/* is_foreign */ true);
|
||||
let details = vec![
|
||||
format!(
|
||||
"the accessed tag {accessed} is foreign to the {conflicting_tag_name} tag {conflicting} (i.e., it is not a child)"
|
||||
"the accessed tag {accessed_str} is foreign to the {conflicting_tag_name} tag {conflicting} (i.e., it is not a child)"
|
||||
),
|
||||
format!(
|
||||
"this {access} would cause the {conflicting_tag_name} tag {conflicting} (currently {before_disabled}) to become Disabled"
|
||||
|
|
@ -343,7 +354,7 @@ impl TbError<'_> {
|
|||
let conflicting_tag_name = "strongly protected";
|
||||
let details = vec![
|
||||
format!(
|
||||
"the allocation of the accessed tag {accessed} also contains the {conflicting_tag_name} tag {conflicting}"
|
||||
"the allocation of the accessed tag {accessed_str} also contains the {conflicting_tag_name} tag {conflicting}"
|
||||
),
|
||||
format!("the {conflicting_tag_name} tag {conflicting} disallows deallocations"),
|
||||
];
|
||||
|
|
@ -351,8 +362,10 @@ impl TbError<'_> {
|
|||
}
|
||||
};
|
||||
let mut history = HistoryData::default();
|
||||
if !accessed_is_conflicting {
|
||||
history.extend(self.accessed_info.history.forget(), "accessed", false);
|
||||
if let Some(accessed_info) = self.accessed_info
|
||||
&& !accessed_is_conflicting
|
||||
{
|
||||
history.extend(accessed_info.history.forget(), "accessed", false);
|
||||
}
|
||||
history.extend(
|
||||
self.conflicting_info.history.extract_relevant(self.error_offset, self.error_kind),
|
||||
|
|
@ -363,6 +376,20 @@ impl TbError<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Cannot access this allocation with wildcard provenance, as there are no
|
||||
/// valid exposed references for this access kind.
|
||||
pub fn no_valid_exposed_references_error<'tcx>(
|
||||
alloc_id: AllocId,
|
||||
offset: u64,
|
||||
access_cause: AccessCause,
|
||||
) -> InterpErrorKind<'tcx> {
|
||||
let title =
|
||||
format!("{access_cause} through <wildcard> at {alloc_id:?}[{offset:#x}] is forbidden");
|
||||
let details = vec![format!("there are no exposed tags which may perform this access here")];
|
||||
let history = HistoryData::default();
|
||||
err_machine_stop!(TerminationInfo::TreeBorrowsUb { title, details, history })
|
||||
}
|
||||
|
||||
type S = &'static str;
|
||||
/// Pretty-printing details
|
||||
///
|
||||
|
|
@ -623,10 +650,10 @@ impl DisplayRepr {
|
|||
} else {
|
||||
// We take this node
|
||||
let rperm = tree
|
||||
.rperms
|
||||
.locations
|
||||
.iter_all()
|
||||
.map(move |(_offset, perms)| {
|
||||
let perm = perms.get(idx);
|
||||
.map(move |(_offset, loc)| {
|
||||
let perm = loc.perms.get(idx);
|
||||
perm.cloned()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
@ -788,7 +815,7 @@ impl<'tcx> Tree {
|
|||
show_unnamed: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
let mut indenter = DisplayIndent::new();
|
||||
let ranges = self.rperms.iter_all().map(|(range, _perms)| range).collect::<Vec<_>>();
|
||||
let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>();
|
||||
if let Some(repr) = DisplayRepr::from(self, show_unnamed) {
|
||||
repr.print(
|
||||
&DEFAULT_FORMATTER,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ mod foreign_access_skipping;
|
|||
mod perms;
|
||||
mod tree;
|
||||
mod unimap;
|
||||
mod wildcard;
|
||||
|
||||
#[cfg(test)]
|
||||
mod exhaustive;
|
||||
|
|
@ -54,16 +55,10 @@ impl<'tcx> Tree {
|
|||
interpret::Pointer::new(alloc_id, range.start),
|
||||
range.size.bytes(),
|
||||
);
|
||||
// TODO: for now we bail out on wildcard pointers. Eventually we should
|
||||
// handle them as much as we can.
|
||||
let tag = match prov {
|
||||
ProvenanceExtra::Concrete(tag) => tag,
|
||||
ProvenanceExtra::Wildcard => return interp_ok(()),
|
||||
};
|
||||
let global = machine.borrow_tracker.as_ref().unwrap();
|
||||
let span = machine.current_user_relevant_span();
|
||||
self.perform_access(
|
||||
tag,
|
||||
prov,
|
||||
Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))),
|
||||
global,
|
||||
alloc_id,
|
||||
|
|
@ -79,19 +74,9 @@ impl<'tcx> Tree {
|
|||
size: Size,
|
||||
machine: &MiriMachine<'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// TODO: for now we bail out on wildcard pointers. Eventually we should
|
||||
// handle them as much as we can.
|
||||
let tag = match prov {
|
||||
ProvenanceExtra::Concrete(tag) => tag,
|
||||
ProvenanceExtra::Wildcard => return interp_ok(()),
|
||||
};
|
||||
let global = machine.borrow_tracker.as_ref().unwrap();
|
||||
let span = machine.current_user_relevant_span();
|
||||
self.dealloc(tag, alloc_range(Size::ZERO, size), global, alloc_id, span)
|
||||
}
|
||||
|
||||
pub fn expose_tag(&mut self, _tag: BorTag) {
|
||||
// TODO
|
||||
self.dealloc(prov, alloc_range(Size::ZERO, size), global, alloc_id, span)
|
||||
}
|
||||
|
||||
/// A tag just lost its protector.
|
||||
|
|
@ -109,7 +94,11 @@ impl<'tcx> Tree {
|
|||
) -> InterpResult<'tcx> {
|
||||
let span = machine.current_user_relevant_span();
|
||||
// `None` makes it the magic on-protector-end operation
|
||||
self.perform_access(tag, None, global, alloc_id, span)
|
||||
self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?;
|
||||
|
||||
self.update_exposure_for_protector_release(tag);
|
||||
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -239,21 +228,22 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
|
||||
// This pointer doesn't come with an AllocId, so there's no
|
||||
// memory to do retagging in.
|
||||
let new_prov = place.ptr().provenance;
|
||||
trace!(
|
||||
"reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
|
||||
new_tag,
|
||||
"reborrow of size 0: reusing {:?} (pointee {})",
|
||||
place.ptr(),
|
||||
place.layout.ty,
|
||||
);
|
||||
log_creation(this, None)?;
|
||||
// Keep original provenance.
|
||||
return interp_ok(place.ptr().provenance);
|
||||
return interp_ok(new_prov);
|
||||
}
|
||||
};
|
||||
|
||||
log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
|
||||
|
||||
let orig_tag = match parent_prov {
|
||||
ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle wildcard pointers
|
||||
ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle retagging wildcard pointers
|
||||
ProvenanceExtra::Concrete(tag) => tag,
|
||||
};
|
||||
|
||||
|
|
@ -356,7 +346,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
};
|
||||
|
||||
tree_borrows.perform_access(
|
||||
orig_tag,
|
||||
parent_prov,
|
||||
Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
|
||||
this.machine.borrow_tracker.as_ref().unwrap(),
|
||||
alloc_id,
|
||||
|
|
@ -589,7 +579,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
// uncovers a non-supported `extern static`.
|
||||
let alloc_extra = this.get_alloc_extra(alloc_id)?;
|
||||
trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
|
||||
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
|
||||
|
||||
let global = this.machine.borrow_tracker.as_ref().unwrap();
|
||||
let protected_tags = &global.borrow().protected_tags;
|
||||
let protected = protected_tags.contains_key(&tag);
|
||||
alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag, protected);
|
||||
}
|
||||
AllocKind::Function | AllocKind::VTable | AllocKind::TypeId | AllocKind::Dead => {
|
||||
// No tree borrows on these allocations.
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ enum PermissionPriv {
|
|||
}
|
||||
use self::PermissionPriv::*;
|
||||
use super::foreign_access_skipping::IdempotentForeignAccess;
|
||||
use super::wildcard::WildcardAccessLevel;
|
||||
|
||||
impl PartialOrd for PermissionPriv {
|
||||
/// PermissionPriv is ordered by the reflexive transitive closure of
|
||||
|
|
@ -372,6 +373,23 @@ impl Permission {
|
|||
pub fn strongest_idempotent_foreign_access(&self, prot: bool) -> IdempotentForeignAccess {
|
||||
self.inner.strongest_idempotent_foreign_access(prot)
|
||||
}
|
||||
|
||||
/// Returns the strongest access allowed from a child to this node without
|
||||
/// causing UB (only considers possible transitions to this permission).
|
||||
pub fn strongest_allowed_child_access(&self, protected: bool) -> WildcardAccessLevel {
|
||||
match self.inner {
|
||||
// Everything except disabled can be accessed by read access.
|
||||
Disabled => WildcardAccessLevel::None,
|
||||
// Frozen references cannot be written to by a child.
|
||||
Frozen => WildcardAccessLevel::Read,
|
||||
// If the `conflicted` flag is set, then there was a foreign read
|
||||
// during the function call that is still ongoing (still `protected`),
|
||||
// this is UB (`noalias` violation).
|
||||
ReservedFrz { conflicted: true } if protected => WildcardAccessLevel::Read,
|
||||
// Everything else allows writes.
|
||||
_ => WildcardAccessLevel::Write,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PermTransition {
|
||||
|
|
@ -772,4 +790,32 @@ mod propagation_optimization_checks {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that `strongest_allowed_child_access` correctly
|
||||
/// represents which transitions are possible.
|
||||
#[test]
|
||||
fn strongest_allowed_child_access() {
|
||||
for (permission, protected) in <(Permission, bool)>::exhaustive() {
|
||||
let strongest_child_access = permission.strongest_allowed_child_access(protected);
|
||||
|
||||
let is_read_valid = Permission::perform_access(
|
||||
AccessKind::Read,
|
||||
AccessRelatedness::LocalAccess,
|
||||
permission,
|
||||
protected,
|
||||
)
|
||||
.is_some();
|
||||
|
||||
let is_write_valid = Permission::perform_access(
|
||||
AccessKind::Write,
|
||||
AccessRelatedness::LocalAccess,
|
||||
permission,
|
||||
protected,
|
||||
)
|
||||
.is_some();
|
||||
|
||||
assert_eq!(is_read_valid, strongest_child_access >= WildcardAccessLevel::Read);
|
||||
assert_eq!(is_write_valid, strongest_child_access >= WildcardAccessLevel::Write);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_span::Span;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::diagnostics::AccessCause;
|
||||
use super::wildcard::WildcardState;
|
||||
use crate::borrow_tracker::tree_borrows::Permission;
|
||||
use crate::borrow_tracker::tree_borrows::diagnostics::{
|
||||
self, NodeDebugInfo, TbError, TransitionError,
|
||||
self, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error,
|
||||
};
|
||||
use crate::borrow_tracker::tree_borrows::foreign_access_skipping::IdempotentForeignAccess;
|
||||
use crate::borrow_tracker::tree_borrows::perms::PermTransition;
|
||||
|
|
@ -30,7 +32,7 @@ use crate::*;
|
|||
|
||||
mod tests;
|
||||
|
||||
/// Data for a single *location*.
|
||||
/// Data for a reference at single *location*.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub(super) struct LocationState {
|
||||
/// A location is "accessed" when it is child-accessed for the first time (and the initial
|
||||
|
|
@ -81,6 +83,49 @@ impl LocationState {
|
|||
self.permission
|
||||
}
|
||||
|
||||
/// Performs an access on this index and updates node,
|
||||
/// perm and wildcard_state to reflect the transition.
|
||||
fn perform_transition(
|
||||
&mut self,
|
||||
idx: UniIndex,
|
||||
nodes: &mut UniValMap<Node>,
|
||||
wildcard_accesses: &mut UniValMap<WildcardState>,
|
||||
access_kind: AccessKind,
|
||||
access_cause: AccessCause,
|
||||
access_range: Option<AllocRange>,
|
||||
relatedness: AccessRelatedness,
|
||||
span: Span,
|
||||
location_range: Range<u64>,
|
||||
protected: bool,
|
||||
) -> Result<(), TransitionError> {
|
||||
// Call this function now (i.e. only if we know `relatedness`), which
|
||||
// ensures it is only called when `skip_if_known_noop` returns
|
||||
// `Recurse`, due to the contract of `traverse_this_parents_children_other`.
|
||||
self.record_new_access(access_kind, relatedness);
|
||||
|
||||
let transition = self.perform_access(access_kind, relatedness, protected)?;
|
||||
if !transition.is_noop() {
|
||||
let node = nodes.get_mut(idx).unwrap();
|
||||
// Record the event as part of the history.
|
||||
node.debug_info.history.push(diagnostics::Event {
|
||||
transition,
|
||||
is_foreign: relatedness.is_foreign(),
|
||||
access_cause,
|
||||
access_range,
|
||||
transition_range: location_range,
|
||||
span,
|
||||
});
|
||||
|
||||
// We need to update the wildcard state, if the permission
|
||||
// of an exposed pointer changes.
|
||||
if node.is_exposed {
|
||||
let access_type = self.permission.strongest_allowed_child_access(protected);
|
||||
WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the effect of an access to one location, including
|
||||
/// - applying `Permission::perform_access` to the inner `Permission`,
|
||||
/// - emitting protector UB if the location is accessed,
|
||||
|
|
@ -211,30 +256,44 @@ impl fmt::Display for LocationState {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of the full tree for a particular location: for all nodes, the local permissions
|
||||
/// of that node, and the tracking for wildcard accesses.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LocationTree {
|
||||
/// Maps a tag to a perm, with possible lazy initialization.
|
||||
///
|
||||
/// NOTE: not all tags registered in `Tree::nodes` are necessarily in all
|
||||
/// ranges of `perms`, because `perms` is in part lazily initialized.
|
||||
/// Just because `nodes.get(key)` is `Some(_)` does not mean you can safely
|
||||
/// `unwrap` any `perm.get(key)`.
|
||||
///
|
||||
/// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
|
||||
pub perms: UniValMap<LocationState>,
|
||||
/// Maps a tag and a location to its wildcard access tracking information,
|
||||
/// with possible lazy initialization.
|
||||
///
|
||||
/// If this allocation doesn't have any exposed nodes, then this map doesn't get
|
||||
/// initialized. This way we only need to allocate the map if we need it.
|
||||
///
|
||||
/// NOTE: same guarantees on entry initialization as for `perms`.
|
||||
pub wildcard_accesses: UniValMap<WildcardState>,
|
||||
}
|
||||
/// Tree structure with both parents and children since we want to be
|
||||
/// able to traverse the tree efficiently in both directions.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Tree {
|
||||
/// Mapping from tags to keys. The key obtained can then be used in
|
||||
/// any of the `UniValMap` relative to this allocation, i.e. both the
|
||||
/// `nodes` and `rperms` of the same `Tree`.
|
||||
/// any of the `UniValMap` relative to this allocation, i.e.
|
||||
/// `nodes`, `LocationTree::perms` and `LocationTree::wildcard_accesses`
|
||||
/// of the same `Tree`.
|
||||
/// The parent-child relationship in `Node` is encoded in terms of these same
|
||||
/// keys, so traversing the entire tree needs exactly one access to
|
||||
/// `tag_mapping`.
|
||||
pub(super) tag_mapping: UniKeyMap<BorTag>,
|
||||
/// All nodes of this tree.
|
||||
pub(super) nodes: UniValMap<Node>,
|
||||
/// Maps a tag and a location to a perm, with possible lazy
|
||||
/// initialization.
|
||||
///
|
||||
/// NOTE: not all tags registered in `nodes` are necessarily in all
|
||||
/// ranges of `rperms`, because `rperms` is in part lazily initialized.
|
||||
/// Just because `nodes.get(key)` is `Some(_)` does not mean you can safely
|
||||
/// `unwrap` any `perm.get(key)`.
|
||||
///
|
||||
/// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
|
||||
pub(super) rperms: DedupRangeMap<UniValMap<LocationState>>,
|
||||
/// Associates with each location its state and wildcard access tracking.
|
||||
pub(super) locations: DedupRangeMap<LocationTree>,
|
||||
/// The index of the root node.
|
||||
pub(super) root: UniIndex,
|
||||
}
|
||||
|
|
@ -260,7 +319,9 @@ pub(super) struct Node {
|
|||
/// in cases where there is no location state yet. See `foreign_access_skipping.rs`,
|
||||
/// and `LocationState::idempotent_foreign_access` for more information
|
||||
default_initial_idempotent_foreign_access: IdempotentForeignAccess,
|
||||
/// Some extra information useful only for debugging purposes
|
||||
/// Whether a wildcard access could happen through this node.
|
||||
pub is_exposed: bool,
|
||||
/// Some extra information useful only for debugging purposes.
|
||||
pub debug_info: NodeDebugInfo,
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +334,7 @@ struct NodeAppArgs<'visit> {
|
|||
/// The node map of this tree.
|
||||
nodes: &'visit mut UniValMap<Node>,
|
||||
/// The permissions map of this tree.
|
||||
perms: &'visit mut UniValMap<LocationState>,
|
||||
loc: &'visit mut LocationTree,
|
||||
}
|
||||
/// Data given to the error handler
|
||||
struct ErrHandlerArgs<'node, InErr> {
|
||||
|
|
@ -293,7 +354,7 @@ struct ErrHandlerArgs<'node, InErr> {
|
|||
struct TreeVisitor<'tree> {
|
||||
tag_mapping: &'tree UniKeyMap<BorTag>,
|
||||
nodes: &'tree mut UniValMap<Node>,
|
||||
perms: &'tree mut UniValMap<LocationState>,
|
||||
loc: &'tree mut LocationTree,
|
||||
}
|
||||
|
||||
/// Whether to continue exploring the children recursively or not.
|
||||
|
|
@ -350,7 +411,7 @@ where
|
|||
idx: UniIndex,
|
||||
rel_pos: AccessRelatedness,
|
||||
) -> ContinueTraversal {
|
||||
let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, perms: this.perms };
|
||||
let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc };
|
||||
(self.f_continue)(&args)
|
||||
}
|
||||
|
||||
|
|
@ -360,14 +421,15 @@ where
|
|||
idx: UniIndex,
|
||||
rel_pos: AccessRelatedness,
|
||||
) -> Result<(), OutErr> {
|
||||
(self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, perms: this.perms })
|
||||
.map_err(|error_kind| {
|
||||
(self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc }).map_err(
|
||||
|error_kind| {
|
||||
(self.err_builder)(ErrHandlerArgs {
|
||||
error_kind,
|
||||
conflicting_info: &this.nodes.get(idx).unwrap().debug_info,
|
||||
accessed_info: &this.nodes.get(self.initial).unwrap().debug_info,
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn go_upwards_from_accessed(
|
||||
|
|
@ -577,6 +639,7 @@ impl Tree {
|
|||
default_initial_perm: root_default_perm,
|
||||
// The root may never be skipped, all accesses will be local.
|
||||
default_initial_idempotent_foreign_access: IdempotentForeignAccess::None,
|
||||
is_exposed: false,
|
||||
debug_info,
|
||||
},
|
||||
);
|
||||
|
|
@ -595,9 +658,10 @@ impl Tree {
|
|||
IdempotentForeignAccess::None,
|
||||
),
|
||||
);
|
||||
DedupRangeMap::new(size, perms)
|
||||
let wildcard_accesses = UniValMap::default();
|
||||
DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses })
|
||||
};
|
||||
Self { root: root_idx, nodes, rperms, tag_mapping }
|
||||
Self { root: root_idx, nodes, locations: rperms, tag_mapping }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -633,11 +697,13 @@ impl<'tcx> Tree {
|
|||
children: SmallVec::default(),
|
||||
default_initial_perm: outside_perm,
|
||||
default_initial_idempotent_foreign_access: default_strongest_idempotent,
|
||||
is_exposed: false,
|
||||
debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
|
||||
},
|
||||
);
|
||||
let parent_node = self.nodes.get_mut(parent_idx).unwrap();
|
||||
// Register new_tag as a child of parent_tag
|
||||
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
|
||||
parent_node.children.push(idx);
|
||||
|
||||
// We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.
|
||||
let mut min_sifa = default_strongest_idempotent;
|
||||
|
|
@ -651,11 +717,19 @@ impl<'tcx> Tree {
|
|||
);
|
||||
|
||||
min_sifa = cmp::min(min_sifa, perm.idempotent_foreign_access);
|
||||
for (_perms_range, perms) in self
|
||||
.rperms
|
||||
for (_range, loc) in self
|
||||
.locations
|
||||
.iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start))
|
||||
{
|
||||
perms.insert(idx, perm);
|
||||
loc.perms.insert(idx, perm);
|
||||
}
|
||||
}
|
||||
|
||||
// We need to ensure the consistency of the wildcard access tracking data structure.
|
||||
// For this, we insert the correct entry for this tag based on its parent, if it exists.
|
||||
for (_range, loc) in self.locations.iter_mut_all() {
|
||||
if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {
|
||||
loc.wildcard_accesses.insert(idx, parent_access.for_new_child());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -689,9 +763,9 @@ impl<'tcx> Tree {
|
|||
// as the default SIFA for not-yet-initialized locations.
|
||||
// Record whether we did any change; if not, the invariant is restored and we can stop the traversal.
|
||||
let mut any_change = false;
|
||||
for (_, map) in self.rperms.iter_mut_all() {
|
||||
for (_range, loc) in self.locations.iter_mut_all() {
|
||||
// Check if this node has a state for this location (or range of locations).
|
||||
if let Some(perm) = map.get_mut(current) {
|
||||
if let Some(perm) = loc.perms.get_mut(current) {
|
||||
// Update the per-location SIFA, recording if it changed.
|
||||
any_change |=
|
||||
perm.idempotent_foreign_access.ensure_no_stronger_than(strongest_allowed);
|
||||
|
|
@ -720,28 +794,37 @@ impl<'tcx> Tree {
|
|||
/// - the absence of Strong Protectors anywhere in the allocation
|
||||
pub fn dealloc(
|
||||
&mut self,
|
||||
tag: BorTag,
|
||||
prov: ProvenanceExtra,
|
||||
access_range: AllocRange,
|
||||
global: &GlobalState,
|
||||
alloc_id: AllocId, // diagnostics
|
||||
span: Span, // diagnostics
|
||||
) -> InterpResult<'tcx> {
|
||||
self.perform_access(
|
||||
tag,
|
||||
prov,
|
||||
Some((access_range, AccessKind::Write, diagnostics::AccessCause::Dealloc)),
|
||||
global,
|
||||
alloc_id,
|
||||
span,
|
||||
)?;
|
||||
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) {
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
|
||||
|
||||
// Check if this breaks any strong protector.
|
||||
// (Weak protectors are already handled by `perform_access`.)
|
||||
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
|
||||
// The order in which we check if any nodes are invalidated only
|
||||
// matters to diagnostics, so we use the root as a default tag.
|
||||
let start_tag = match prov {
|
||||
ProvenanceExtra::Concrete(tag) => tag,
|
||||
ProvenanceExtra::Wildcard => self.nodes.get(self.root).unwrap().tag,
|
||||
};
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, loc }
|
||||
.traverse_this_parents_children_other(
|
||||
tag,
|
||||
// visit all children, skipping none
|
||||
start_tag,
|
||||
// Visit all children, skipping none.
|
||||
|_| ContinueTraversal::Recurse,
|
||||
|args: NodeAppArgs<'_>| -> Result<(), TransitionError> {
|
||||
let node = args.nodes.get(args.idx).unwrap();
|
||||
let perm = args.perms.entry(args.idx);
|
||||
let perm = args.loc.perms.entry(args.idx);
|
||||
|
||||
let perm =
|
||||
perm.get().copied().unwrap_or_else(|| node.default_location_state());
|
||||
|
|
@ -764,9 +847,15 @@ impl<'tcx> Tree {
|
|||
conflicting_info,
|
||||
access_cause: diagnostics::AccessCause::Dealloc,
|
||||
alloc_id,
|
||||
error_offset: perms_range.start,
|
||||
error_offset: loc_range.start,
|
||||
error_kind,
|
||||
accessed_info,
|
||||
accessed_info: match prov {
|
||||
ProvenanceExtra::Concrete(_) => Some(accessed_info),
|
||||
// `accessed_info` contains the info of `start_tag`.
|
||||
// On a wildcard access this is not the info of the accessed tag
|
||||
// (as we don't know the accessed tag).
|
||||
ProvenanceExtra::Wildcard => None,
|
||||
},
|
||||
}
|
||||
.build()
|
||||
},
|
||||
|
|
@ -795,12 +884,15 @@ impl<'tcx> Tree {
|
|||
/// - recording the history.
|
||||
pub fn perform_access(
|
||||
&mut self,
|
||||
tag: BorTag,
|
||||
prov: ProvenanceExtra,
|
||||
access_range_and_kind: Option<(AllocRange, AccessKind, diagnostics::AccessCause)>,
|
||||
global: &GlobalState,
|
||||
alloc_id: AllocId, // diagnostics
|
||||
span: Span, // diagnostics
|
||||
) -> InterpResult<'tcx> {
|
||||
let ProvenanceExtra::Concrete(tag) = prov else {
|
||||
return self.perform_wildcard_access(access_range_and_kind, global, alloc_id, span);
|
||||
};
|
||||
use std::ops::Range;
|
||||
// Performs the per-node work:
|
||||
// - insert the permission if it does not exist
|
||||
|
|
@ -814,7 +906,7 @@ impl<'tcx> Tree {
|
|||
// the `RangeMap` on which we are currently working).
|
||||
let node_skipper = |access_kind: AccessKind, args: &NodeAppArgs<'_>| -> ContinueTraversal {
|
||||
let node = args.nodes.get(args.idx).unwrap();
|
||||
let perm = args.perms.get(args.idx);
|
||||
let perm = args.loc.perms.get(args.idx);
|
||||
|
||||
let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
|
||||
old_state.skip_if_known_noop(access_kind, args.rel_pos)
|
||||
|
|
@ -825,29 +917,23 @@ impl<'tcx> Tree {
|
|||
args: NodeAppArgs<'_>|
|
||||
-> Result<(), TransitionError> {
|
||||
let node = args.nodes.get_mut(args.idx).unwrap();
|
||||
let mut perm = args.perms.entry(args.idx);
|
||||
let mut perm = args.loc.perms.entry(args.idx);
|
||||
|
||||
let old_state = perm.or_insert(node.default_location_state());
|
||||
|
||||
// Call this function now, which ensures it is only called when
|
||||
// `skip_if_known_noop` returns `Recurse`, due to the contract of
|
||||
// `traverse_this_parents_children_other`.
|
||||
old_state.record_new_access(access_kind, args.rel_pos);
|
||||
let state = perm.or_insert(node.default_location_state());
|
||||
|
||||
let protected = global.borrow().protected_tags.contains_key(&node.tag);
|
||||
let transition = old_state.perform_access(access_kind, args.rel_pos, protected)?;
|
||||
// Record the event as part of the history
|
||||
if !transition.is_noop() {
|
||||
node.debug_info.history.push(diagnostics::Event {
|
||||
transition,
|
||||
is_foreign: args.rel_pos.is_foreign(),
|
||||
access_cause,
|
||||
access_range: access_range_and_kind.map(|x| x.0),
|
||||
transition_range: perms_range,
|
||||
span,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
state.perform_transition(
|
||||
args.idx,
|
||||
args.nodes,
|
||||
&mut args.loc.wildcard_accesses,
|
||||
access_kind,
|
||||
access_cause,
|
||||
/* access_range */ access_range_and_kind.map(|x| x.0),
|
||||
args.rel_pos,
|
||||
span,
|
||||
perms_range,
|
||||
protected,
|
||||
)
|
||||
};
|
||||
|
||||
// Error handler in case `node_app` goes wrong.
|
||||
|
|
@ -863,7 +949,7 @@ impl<'tcx> Tree {
|
|||
alloc_id,
|
||||
error_offset: perms_range.start,
|
||||
error_kind,
|
||||
accessed_info,
|
||||
accessed_info: Some(accessed_info),
|
||||
}
|
||||
.build()
|
||||
};
|
||||
|
|
@ -871,14 +957,13 @@ impl<'tcx> Tree {
|
|||
if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {
|
||||
// Default branch: this is a "normal" access through a known range.
|
||||
// We iterate over affected locations and traverse the tree for each of them.
|
||||
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size)
|
||||
{
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
|
||||
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, loc }
|
||||
.traverse_this_parents_children_other(
|
||||
tag,
|
||||
|args| node_skipper(access_kind, args),
|
||||
|args| node_app(perms_range.clone(), access_kind, access_cause, args),
|
||||
|args| err_handler(perms_range.clone(), access_cause, args),
|
||||
|args| node_app(loc_range.clone(), access_kind, access_cause, args),
|
||||
|args| err_handler(loc_range.clone(), access_cause, args),
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -891,21 +976,21 @@ impl<'tcx> Tree {
|
|||
// See the test case `returned_mut_is_usable` from
|
||||
// `tests/pass/tree_borrows/tree-borrows.rs` for an example of
|
||||
// why this is important.
|
||||
for (perms_range, perms) in self.rperms.iter_mut_all() {
|
||||
for (loc_range, loc) in self.locations.iter_mut_all() {
|
||||
let idx = self.tag_mapping.get(&tag).unwrap();
|
||||
// Only visit accessed permissions
|
||||
if let Some(p) = perms.get(idx)
|
||||
if let Some(p) = loc.perms.get(idx)
|
||||
&& let Some(access_kind) = p.permission.protector_end_access()
|
||||
&& p.accessed
|
||||
{
|
||||
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
|
||||
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, loc }
|
||||
.traverse_nonchildren(
|
||||
tag,
|
||||
|args| node_skipper(access_kind, args),
|
||||
|args| node_app(perms_range.clone(), access_kind, access_cause, args),
|
||||
|args| err_handler(perms_range.clone(), access_cause, args),
|
||||
)?;
|
||||
tag,
|
||||
|args| node_skipper(access_kind, args),
|
||||
|args| node_app(loc_range.clone(), access_kind, access_cause, args),
|
||||
|args| err_handler(loc_range.clone(), access_cause, args),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -921,7 +1006,7 @@ impl Tree {
|
|||
// merge some adjacent ranges that were made equal by the removal of some
|
||||
// tags (this does not necessarily mean that they have identical internal representations,
|
||||
// see the `PartialEq` impl for `UniValMap`)
|
||||
self.rperms.merge_adjacent_thorough();
|
||||
self.locations.merge_adjacent_thorough();
|
||||
}
|
||||
|
||||
/// Checks if a node is useless and should be GC'ed.
|
||||
|
|
@ -953,10 +1038,14 @@ impl Tree {
|
|||
let child = self.nodes.get(child_idx).unwrap();
|
||||
// Check that for that one child, `can_be_replaced_by_child` holds for the permission
|
||||
// on all locations.
|
||||
for (_, data) in self.rperms.iter_all() {
|
||||
let parent_perm =
|
||||
data.get(idx).map(|x| x.permission).unwrap_or_else(|| node.default_initial_perm);
|
||||
let child_perm = data
|
||||
for (_range, loc) in self.locations.iter_all() {
|
||||
let parent_perm = loc
|
||||
.perms
|
||||
.get(idx)
|
||||
.map(|x| x.permission)
|
||||
.unwrap_or_else(|| node.default_initial_perm);
|
||||
let child_perm = loc
|
||||
.perms
|
||||
.get(child_idx)
|
||||
.map(|x| x.permission)
|
||||
.unwrap_or_else(|| child.default_initial_perm);
|
||||
|
|
@ -980,8 +1069,9 @@ impl Tree {
|
|||
// before we can safely apply `UniKeyMap::remove` to truly remove
|
||||
// this tag from the `tag_mapping`.
|
||||
let node = self.nodes.remove(this).unwrap();
|
||||
for (_perms_range, perms) in self.rperms.iter_mut_all() {
|
||||
perms.remove(this);
|
||||
for (_range, loc) in self.locations.iter_mut_all() {
|
||||
loc.perms.remove(this);
|
||||
loc.wildcard_accesses.remove(this);
|
||||
}
|
||||
self.tag_mapping.remove(&node.tag);
|
||||
}
|
||||
|
|
@ -1058,6 +1148,126 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
/// Methods for wildcard accesses.
|
||||
impl<'tcx> Tree {
|
||||
/// Analogous to `perform_access`, but we do not know from which exposed
|
||||
/// reference the access happens.
|
||||
pub fn perform_wildcard_access(
|
||||
&mut self,
|
||||
access_range_and_kind: Option<(AllocRange, AccessKind, diagnostics::AccessCause)>,
|
||||
global: &GlobalState,
|
||||
alloc_id: AllocId, // diagnostics
|
||||
span: Span, // diagnostics
|
||||
) -> InterpResult<'tcx> {
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
self.verify_wildcard_consistency(global);
|
||||
|
||||
if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {
|
||||
// This does a traversal starting from the root through the tree updating
|
||||
// the permissions of each node.
|
||||
// The difference to `perform_access` is that we take the access
|
||||
// relatedness from the wildcard tracking state of the node instead of
|
||||
// from the visitor itself.
|
||||
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
|
||||
let root_tag = self.nodes.get(self.root).unwrap().tag;
|
||||
TreeVisitor { loc, nodes: &mut self.nodes, tag_mapping: &self.tag_mapping }
|
||||
.traverse_this_parents_children_other(
|
||||
root_tag,
|
||||
|args: &NodeAppArgs<'_>| -> ContinueTraversal {
|
||||
let node = args.nodes.get(args.idx).unwrap();
|
||||
let perm = args.loc.perms.get(args.idx);
|
||||
let wildcard_state = args
|
||||
.loc
|
||||
.wildcard_accesses
|
||||
.get(args.idx)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let old_state =
|
||||
perm.copied().unwrap_or_else(|| node.default_location_state());
|
||||
// If we know where, relative to this node, the wildcard access occurs,
|
||||
// then check if we can skip the entire subtree.
|
||||
if let Some(relatedness) =
|
||||
wildcard_state.access_relatedness(access_kind)
|
||||
&& let Some(relatedness) = relatedness.to_relatedness()
|
||||
{
|
||||
// We can use the usual SIFA machinery to skip nodes.
|
||||
old_state.skip_if_known_noop(access_kind, relatedness)
|
||||
} else {
|
||||
ContinueTraversal::Recurse
|
||||
}
|
||||
},
|
||||
|args| {
|
||||
let node = args.nodes.get_mut(args.idx).unwrap();
|
||||
let mut entry = args.loc.perms.entry(args.idx);
|
||||
let perm = entry.or_insert(node.default_location_state());
|
||||
|
||||
let protected = global.borrow().protected_tags.contains_key(&node.tag);
|
||||
|
||||
let Some(wildcard_relatedness) = args
|
||||
.loc
|
||||
.wildcard_accesses
|
||||
.get(args.idx)
|
||||
.and_then(|s| s.access_relatedness(access_kind))
|
||||
else {
|
||||
// There doesn't exist a valid exposed reference for this access to
|
||||
// happen through.
|
||||
// If this fails for one id, then it fails for all ids so this.
|
||||
// Since we always check the root first, this means it should always
|
||||
// fail on the root.
|
||||
assert_eq!(self.root, args.idx);
|
||||
return Err(no_valid_exposed_references_error(
|
||||
alloc_id,
|
||||
loc_range.start,
|
||||
access_cause,
|
||||
));
|
||||
};
|
||||
|
||||
let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
|
||||
// If the access type is Either, then we do not apply any transition
|
||||
// to this node, but we still update each of its children.
|
||||
// This is an imprecision! In the future, maybe we can still do some sort
|
||||
// of best-effort update here.
|
||||
return Ok(());
|
||||
};
|
||||
// We know the exact relatedness, so we can actually do precise checks.
|
||||
perm.perform_transition(
|
||||
args.idx,
|
||||
args.nodes,
|
||||
&mut args.loc.wildcard_accesses,
|
||||
access_kind,
|
||||
access_cause,
|
||||
Some(access_range),
|
||||
relatedness,
|
||||
span,
|
||||
loc_range.clone(),
|
||||
protected,
|
||||
)
|
||||
.map_err(|trans| {
|
||||
let node = args.nodes.get(args.idx).unwrap();
|
||||
TbError {
|
||||
conflicting_info: &node.debug_info,
|
||||
access_cause,
|
||||
alloc_id,
|
||||
error_offset: loc_range.start,
|
||||
error_kind: trans,
|
||||
accessed_info: None,
|
||||
}
|
||||
.build()
|
||||
})
|
||||
},
|
||||
|err| err.error_kind,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
// This is for the special access when a protector gets released.
|
||||
// Wildcard pointers are never protected, so this is unreachable.
|
||||
unreachable!()
|
||||
};
|
||||
interp_ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn default_location_state(&self) -> LocationState {
|
||||
LocationState::new_non_accessed(
|
||||
|
|
@ -1071,7 +1281,15 @@ impl VisitProvenance for Tree {
|
|||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
// To ensure that the root never gets removed, we visit it
|
||||
// (the `root` node of `Tree` is not an `Option<_>`)
|
||||
visit(None, Some(self.nodes.get(self.root).unwrap().tag))
|
||||
visit(None, Some(self.nodes.get(self.root).unwrap().tag));
|
||||
|
||||
// We also need to keep around any exposed tags through which
|
||||
// an access could still happen.
|
||||
for (_id, node) in self.nodes.iter() {
|
||||
if node.is_exposed {
|
||||
visit(None, Some(node.tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::mem;
|
||||
|
||||
|
|
@ -20,10 +21,15 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use crate::helpers::ToUsize;
|
||||
|
||||
/// Intermediate key between a UniKeyMap and a UniValMap.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct UniIndex {
|
||||
idx: u32,
|
||||
}
|
||||
impl Debug for UniIndex {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.idx.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// From K to UniIndex
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
@ -201,6 +207,19 @@ impl<V> UniValMap<V> {
|
|||
mem::swap(&mut res, &mut self.data[idx.idx.to_usize()]);
|
||||
res
|
||||
}
|
||||
|
||||
/// Returns true if the map is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.iter().all(|v| v.is_none())
|
||||
}
|
||||
|
||||
/// Iterates over all key-value pairs in the map.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (UniIndex, &V)> {
|
||||
self.data
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, v)| v.as_ref().map(|r| (UniIndex { idx: i.try_into().unwrap() }, r)))
|
||||
}
|
||||
}
|
||||
|
||||
/// An access to a single value of the map.
|
||||
|
|
|
|||
519
src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
Normal file
519
src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs
Normal file
|
|
@ -0,0 +1,519 @@
|
|||
use std::cmp::max;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::Tree;
|
||||
use super::tree::{AccessRelatedness, Node};
|
||||
use super::unimap::{UniIndex, UniValMap};
|
||||
use crate::BorTag;
|
||||
use crate::borrow_tracker::AccessKind;
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
use crate::borrow_tracker::GlobalState;
|
||||
|
||||
/// Represents the maximum access level that is possible.
|
||||
///
|
||||
/// Note that we derive Ord and PartialOrd, so the order in which variants are listed below matters:
|
||||
/// None < Read < Write. Do not change that order.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub enum WildcardAccessLevel {
|
||||
#[default]
|
||||
None,
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
/// Where the access happened relative to the current node.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum WildcardAccessRelatedness {
|
||||
/// The access definitively happened through a local node.
|
||||
LocalAccess,
|
||||
/// The access definitively happened through a foreign node.
|
||||
ForeignAccess,
|
||||
/// We do not know if the access is foreign or local.
|
||||
EitherAccess,
|
||||
}
|
||||
impl WildcardAccessRelatedness {
|
||||
pub fn to_relatedness(self) -> Option<AccessRelatedness> {
|
||||
match self {
|
||||
Self::LocalAccess => Some(AccessRelatedness::LocalAccess),
|
||||
Self::ForeignAccess => Some(AccessRelatedness::ForeignAccess),
|
||||
Self::EitherAccess => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State per location per node keeping track of where relative to this
|
||||
/// node exposed nodes are and what access permissions they have.
|
||||
///
|
||||
/// Designed to be completely determined by its parent, siblings and
|
||||
/// direct children's max_local_access/max_foreign_access.
|
||||
#[derive(Clone, Default, PartialEq, Eq)]
|
||||
pub struct WildcardState {
|
||||
/// How many of this node's direct children have `max_local_access()==Write`.
|
||||
child_writes: u16,
|
||||
/// How many of this node's direct children have `max_local_access()>=Read`.
|
||||
child_reads: u16,
|
||||
/// The maximum access level that could happen from an exposed node
|
||||
/// that is foreign to this node.
|
||||
///
|
||||
/// This is calculated as the `max()` of the parent's `max_foreign_access`,
|
||||
/// `exposed_as` and the siblings' `max_local_access()`.
|
||||
max_foreign_access: WildcardAccessLevel,
|
||||
/// At what access level this node itself is exposed.
|
||||
exposed_as: WildcardAccessLevel,
|
||||
}
|
||||
impl Debug for WildcardState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("WildcardState")
|
||||
.field("child_r/w", &(self.child_reads, self.child_writes))
|
||||
.field("foreign", &self.max_foreign_access)
|
||||
.field("exposed_as", &self.exposed_as)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
impl WildcardState {
|
||||
/// The maximum access level that could happen from an exposed
|
||||
/// node that is local to this node.
|
||||
fn max_local_access(&self) -> WildcardAccessLevel {
|
||||
use WildcardAccessLevel::*;
|
||||
max(
|
||||
self.exposed_as,
|
||||
if self.child_writes > 0 {
|
||||
Write
|
||||
} else if self.child_reads > 0 {
|
||||
Read
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// From where relative to the node with this wildcard info a read or write access could happen.
|
||||
pub fn access_relatedness(&self, kind: AccessKind) -> Option<WildcardAccessRelatedness> {
|
||||
match kind {
|
||||
AccessKind::Read => self.read_access_relatedness(),
|
||||
AccessKind::Write => self.write_access_relatedness(),
|
||||
}
|
||||
}
|
||||
|
||||
/// From where relative to the node with this wildcard info a read access could happen.
|
||||
fn read_access_relatedness(&self) -> Option<WildcardAccessRelatedness> {
|
||||
let has_foreign = self.max_foreign_access >= WildcardAccessLevel::Read;
|
||||
let has_local = self.max_local_access() >= WildcardAccessLevel::Read;
|
||||
use WildcardAccessRelatedness as E;
|
||||
match (has_foreign, has_local) {
|
||||
(true, true) => Some(E::EitherAccess),
|
||||
(true, false) => Some(E::ForeignAccess),
|
||||
(false, true) => Some(E::LocalAccess),
|
||||
(false, false) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// From where relative to the node with this wildcard info a write access could happen.
|
||||
fn write_access_relatedness(&self) -> Option<WildcardAccessRelatedness> {
|
||||
let has_foreign = self.max_foreign_access == WildcardAccessLevel::Write;
|
||||
let has_local = self.max_local_access() == WildcardAccessLevel::Write;
|
||||
use WildcardAccessRelatedness as E;
|
||||
match (has_foreign, has_local) {
|
||||
(true, true) => Some(E::EitherAccess),
|
||||
(true, false) => Some(E::ForeignAccess),
|
||||
(false, true) => Some(E::LocalAccess),
|
||||
(false, false) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the access tracking information for a new child node of a parent with this
|
||||
/// wildcard info.
|
||||
/// The new node doesn't have any child reads/writes, but calculates `max_foreign_access`
|
||||
/// from its parent.
|
||||
pub fn for_new_child(&self) -> Self {
|
||||
Self {
|
||||
max_foreign_access: max(self.max_foreign_access, self.max_local_access()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes the nodes of `children` onto the stack who's `max_foreign_access`
|
||||
/// needs to be updated.
|
||||
///
|
||||
/// * `children`: A list of nodes with the same parent. `children` doesn't
|
||||
/// necessarily have to contain all children of parent, but can just be
|
||||
/// a subset.
|
||||
///
|
||||
/// * `child_reads`, `child_writes`: How many of `children` have `max_local_access()`
|
||||
/// of at least `read`/`write`
|
||||
///
|
||||
/// * `new_foreign_access`, `old_foreign_access`:
|
||||
/// The max possible access level that is foreign to all `children`
|
||||
/// (i.e., it is not local to *any* of them).
|
||||
/// This can be calculated as the max of the parent's `exposed_as()`, `max_foreign_access`
|
||||
/// and of all `max_local_access()` of any nodes with the same parent that are
|
||||
/// not listed in `children`.
|
||||
///
|
||||
/// This access level changed from `old` to `new`, which is why we need to
|
||||
/// update `children`.
|
||||
fn push_relevant_children(
|
||||
stack: &mut Vec<(UniIndex, WildcardAccessLevel)>,
|
||||
new_foreign_access: WildcardAccessLevel,
|
||||
old_foreign_access: WildcardAccessLevel,
|
||||
child_reads: u16,
|
||||
child_writes: u16,
|
||||
children: impl Iterator<Item = UniIndex>,
|
||||
|
||||
wildcard_accesses: &UniValMap<WildcardState>,
|
||||
) {
|
||||
use WildcardAccessLevel::*;
|
||||
|
||||
// Nothing changed so we don't need to update anything.
|
||||
if new_foreign_access == old_foreign_access {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to consider that the children's `max_local_access()` affect each
|
||||
// other's `max_foreign_access`, but do not affect their own `max_foreign_access`.
|
||||
|
||||
// The new `max_foreign_acces` for children with `max_local_access()==Write`.
|
||||
let write_foreign_access = max(
|
||||
new_foreign_access,
|
||||
if child_writes > 1 {
|
||||
// There exists at least one more child with exposed write access.
|
||||
// This means that a foreign write through that node is possible.
|
||||
Write
|
||||
} else if child_reads > 1 {
|
||||
// There exists at least one more child with exposed read access,
|
||||
// but no other with write access.
|
||||
// This means that a foreign read but no write through that node
|
||||
// is possible.
|
||||
Read
|
||||
} else {
|
||||
// There are no other nodes with read or write access.
|
||||
// This means no foreign writes through other children are possible.
|
||||
None
|
||||
},
|
||||
);
|
||||
|
||||
// The new `max_foreign_acces` for children with `max_local_access()==Read`.
|
||||
let read_foreign_access = max(
|
||||
new_foreign_access,
|
||||
if child_writes > 0 {
|
||||
// There exists at least one child with write access (and it's not this one).
|
||||
Write
|
||||
} else if child_reads > 1 {
|
||||
// There exists at least one more child with exposed read access,
|
||||
// but no other with write access.
|
||||
Read
|
||||
} else {
|
||||
// There are no other nodes with read or write access,
|
||||
None
|
||||
},
|
||||
);
|
||||
|
||||
// The new `max_foreign_acces` for children with `max_local_access()==None`.
|
||||
let none_foreign_access = max(
|
||||
new_foreign_access,
|
||||
if child_writes > 0 {
|
||||
// There exists at least one child with write access (and it's not this one).
|
||||
Write
|
||||
} else if child_reads > 0 {
|
||||
// There exists at least one child with read access (and it's not this one),
|
||||
// but none with write access.
|
||||
Read
|
||||
} else {
|
||||
// No children are exposed as read or write.
|
||||
None
|
||||
},
|
||||
);
|
||||
|
||||
stack.extend(children.filter_map(|child| {
|
||||
let state = wildcard_accesses.get(child).cloned().unwrap_or_default();
|
||||
|
||||
let new_foreign_access = match state.max_local_access() {
|
||||
Write => write_foreign_access,
|
||||
Read => read_foreign_access,
|
||||
None => none_foreign_access,
|
||||
};
|
||||
|
||||
if new_foreign_access != state.max_foreign_access {
|
||||
Some((child, new_foreign_access))
|
||||
} else {
|
||||
Option::None
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// Update the tracking information of a tree, to reflect that the node specified by `id` is
|
||||
/// now exposed with `new_exposed_as`.
|
||||
///
|
||||
/// Propagates the Willard access information over the tree. This needs to be called every
|
||||
/// time the access level of an exposed node changes, to keep the state in sync with
|
||||
/// the rest of the tree.
|
||||
pub fn update_exposure(
|
||||
id: UniIndex,
|
||||
new_exposed_as: WildcardAccessLevel,
|
||||
nodes: &UniValMap<Node>,
|
||||
wildcard_accesses: &mut UniValMap<WildcardState>,
|
||||
) {
|
||||
let mut entry = wildcard_accesses.entry(id);
|
||||
let src_state = entry.or_insert(Default::default());
|
||||
let old_exposed_as = src_state.exposed_as;
|
||||
|
||||
// If the exposure doesn't change, then we don't need to update anything.
|
||||
if old_exposed_as == new_exposed_as {
|
||||
return;
|
||||
}
|
||||
|
||||
let src_old_local_access = src_state.max_local_access();
|
||||
|
||||
src_state.exposed_as = new_exposed_as;
|
||||
|
||||
let src_new_local_access = src_state.max_local_access();
|
||||
|
||||
// Stack of nodes for which the max_foreign_access field needs to be updated.
|
||||
// Will be filled with the children of this node and its parents children before
|
||||
// we begin downwards traversal.
|
||||
let mut stack: Vec<(UniIndex, WildcardAccessLevel)> = Vec::new();
|
||||
|
||||
// Add the direct children of this node to the stack.
|
||||
{
|
||||
let node = nodes.get(id).unwrap();
|
||||
Self::push_relevant_children(
|
||||
&mut stack,
|
||||
// new_foreign_access
|
||||
max(src_state.max_foreign_access, new_exposed_as),
|
||||
// old_foreign_access
|
||||
max(src_state.max_foreign_access, old_exposed_as),
|
||||
// Consider all children.
|
||||
src_state.child_reads,
|
||||
src_state.child_writes,
|
||||
node.children.iter().copied(),
|
||||
wildcard_accesses,
|
||||
);
|
||||
}
|
||||
// We need to propagate the tracking info up the tree, for this we traverse
|
||||
// up the parents.
|
||||
// We can skip propagating info to the parent and siblings of a node if its
|
||||
// access didn't change.
|
||||
{
|
||||
// The child from which we came.
|
||||
let mut child = id;
|
||||
// This is the `max_local_access()` of the child we came from, before
|
||||
// this update...
|
||||
let mut old_child_access = src_old_local_access;
|
||||
// and after this update.
|
||||
let mut new_child_access = src_new_local_access;
|
||||
while let Some(parent_id) = nodes.get(child).unwrap().parent {
|
||||
let parent_node = nodes.get(parent_id).unwrap();
|
||||
let mut entry = wildcard_accesses.entry(parent_id);
|
||||
let parent_state = entry.or_insert(Default::default());
|
||||
|
||||
let old_parent_local_access = parent_state.max_local_access();
|
||||
use WildcardAccessLevel::*;
|
||||
// Updating this node's tracking state for its children.
|
||||
match (old_child_access, new_child_access) {
|
||||
(None | Read, Write) => parent_state.child_writes += 1,
|
||||
(Write, None | Read) => parent_state.child_writes -= 1,
|
||||
_ => {}
|
||||
}
|
||||
match (old_child_access, new_child_access) {
|
||||
(None, Read | Write) => parent_state.child_reads += 1,
|
||||
(Read | Write, None) => parent_state.child_reads -= 1,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let new_parent_local_access = parent_state.max_local_access();
|
||||
|
||||
{
|
||||
// We need to update the `max_foreign_access` of `child`'s
|
||||
// siblings. For this we can reuse the `push_relevant_children`
|
||||
// function.
|
||||
//
|
||||
// We pass it just the siblings without child itself. Since
|
||||
// `child`'s `max_local_access()` is foreign to all of its
|
||||
// siblings we can pass it as part of the foreign access.
|
||||
|
||||
let parent_access =
|
||||
max(parent_state.exposed_as, parent_state.max_foreign_access);
|
||||
// This is how many of `child`'s siblings have read/write local access.
|
||||
// If `child` itself has access, then we need to subtract its access from the count.
|
||||
let sibling_reads =
|
||||
parent_state.child_reads - if new_child_access >= Read { 1 } else { 0 };
|
||||
let sibling_writes =
|
||||
parent_state.child_writes - if new_child_access >= Write { 1 } else { 0 };
|
||||
Self::push_relevant_children(
|
||||
&mut stack,
|
||||
// new_foreign_access
|
||||
max(parent_access, new_child_access),
|
||||
// old_foreign_access
|
||||
max(parent_access, old_child_access),
|
||||
// Consider only siblings of child.
|
||||
sibling_reads,
|
||||
sibling_writes,
|
||||
parent_node.children.iter().copied().filter(|id| child != *id),
|
||||
wildcard_accesses,
|
||||
);
|
||||
}
|
||||
if old_parent_local_access == new_parent_local_access {
|
||||
// We didn't change `max_local_access()` for parent, so we don't need to propagate further upwards.
|
||||
break;
|
||||
}
|
||||
|
||||
old_child_access = old_parent_local_access;
|
||||
new_child_access = new_parent_local_access;
|
||||
child = parent_id;
|
||||
}
|
||||
}
|
||||
// Traverses down the tree to update max_foreign_access fields of children and cousins who need to be updated.
|
||||
while let Some((id, new_access)) = stack.pop() {
|
||||
let node = nodes.get(id).unwrap();
|
||||
let mut entry = wildcard_accesses.entry(id);
|
||||
let state = entry.or_insert(Default::default());
|
||||
|
||||
let old_access = state.max_foreign_access;
|
||||
state.max_foreign_access = new_access;
|
||||
|
||||
Self::push_relevant_children(
|
||||
&mut stack,
|
||||
// new_foreign_access
|
||||
max(state.exposed_as, new_access),
|
||||
// old_foreign_access
|
||||
max(state.exposed_as, old_access),
|
||||
// Consider all children.
|
||||
state.child_reads,
|
||||
state.child_writes,
|
||||
node.children.iter().copied(),
|
||||
wildcard_accesses,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tree {
|
||||
/// Marks the tag as exposed & updates the wildcard tracking data structure
|
||||
/// to represent its access level.
|
||||
/// Also takes as an argument whether the tag is protected or not.
|
||||
pub fn expose_tag(&mut self, tag: BorTag, protected: bool) {
|
||||
let id = self.tag_mapping.get(&tag).unwrap();
|
||||
let node = self.nodes.get_mut(id).unwrap();
|
||||
node.is_exposed = true;
|
||||
let node = self.nodes.get(id).unwrap();
|
||||
|
||||
// When the first tag gets exposed then we initialize the
|
||||
// wildcard state for every node and location in the tree.
|
||||
for (_, loc) in self.locations.iter_mut_all() {
|
||||
let perm = loc
|
||||
.perms
|
||||
.get(id)
|
||||
.map(|p| p.permission())
|
||||
.unwrap_or_else(|| node.default_location_state().permission());
|
||||
|
||||
let access_type = perm.strongest_allowed_child_access(protected);
|
||||
WildcardState::update_exposure(
|
||||
id,
|
||||
access_type,
|
||||
&self.nodes,
|
||||
&mut loc.wildcard_accesses,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This updates the wildcard tracking data structure to reflect the release of
|
||||
/// the protector on `tag`.
|
||||
pub(super) fn update_exposure_for_protector_release(&mut self, tag: BorTag) {
|
||||
let idx = self.tag_mapping.get(&tag).unwrap();
|
||||
|
||||
// We check if the node is already exposed, as we don't want to expose any
|
||||
// nodes which aren't already exposed.
|
||||
|
||||
if self.nodes.get(idx).unwrap().is_exposed {
|
||||
// Updates the exposure to the new permission on every location.
|
||||
self.expose_tag(tag, /* protected */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "expensive-consistency-checks")]
|
||||
impl Tree {
|
||||
/// Checks that the wildcard tracking data structure is internally consistent and
|
||||
/// has the correct `exposed_as` values.
|
||||
pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
|
||||
let protected_tags = &global.borrow().protected_tags;
|
||||
for (_, loc) in self.locations.iter_all() {
|
||||
let wildcard_accesses = &loc.wildcard_accesses;
|
||||
let perms = &loc.perms;
|
||||
// Checks if accesses is empty.
|
||||
if wildcard_accesses.is_empty() {
|
||||
return;
|
||||
}
|
||||
for (id, node) in self.nodes.iter() {
|
||||
let state = wildcard_accesses.get(id).unwrap();
|
||||
|
||||
let expected_exposed_as = if node.is_exposed {
|
||||
let perm = perms.get(id).unwrap();
|
||||
|
||||
perm.permission()
|
||||
.strongest_allowed_child_access(protected_tags.contains_key(&node.tag))
|
||||
} else {
|
||||
WildcardAccessLevel::None
|
||||
};
|
||||
|
||||
// The foreign wildcard accesses possible at a node are determined by which
|
||||
// accesses can originate from their siblings, their parent, and from above
|
||||
// their parent.
|
||||
let expected_max_foreign_access = if let Some(parent) = node.parent {
|
||||
let parent_node = self.nodes.get(parent).unwrap();
|
||||
let parent_state = wildcard_accesses.get(parent).unwrap();
|
||||
|
||||
let max_sibling_access = parent_node
|
||||
.children
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|child| *child != id)
|
||||
.map(|child| {
|
||||
let state = wildcard_accesses.get(child).unwrap();
|
||||
state.max_local_access()
|
||||
})
|
||||
.fold(WildcardAccessLevel::None, max);
|
||||
|
||||
max_sibling_access
|
||||
.max(parent_state.max_foreign_access)
|
||||
.max(parent_state.exposed_as)
|
||||
} else {
|
||||
WildcardAccessLevel::None
|
||||
};
|
||||
|
||||
// Count how many children can be the source of wildcard reads or writes
|
||||
// (either directly, or via their children).
|
||||
let child_accesses = node.children.iter().copied().map(|child| {
|
||||
let state = wildcard_accesses.get(child).unwrap();
|
||||
state.max_local_access()
|
||||
});
|
||||
let expected_child_reads =
|
||||
child_accesses.clone().filter(|a| *a >= WildcardAccessLevel::Read).count();
|
||||
let expected_child_writes =
|
||||
child_accesses.filter(|a| *a >= WildcardAccessLevel::Write).count();
|
||||
|
||||
assert_eq!(
|
||||
expected_exposed_as, state.exposed_as,
|
||||
"tag {:?} (id:{id:?}) should be exposed as {expected_exposed_as:?} but is exposed as {:?}",
|
||||
node.tag, state.exposed_as
|
||||
);
|
||||
assert_eq!(
|
||||
expected_max_foreign_access, state.max_foreign_access,
|
||||
"expected {:?}'s (id:{id:?}) max_foreign_access to be {:?} instead of {:?}",
|
||||
node.tag, expected_max_foreign_access, state.max_foreign_access
|
||||
);
|
||||
let child_reads: usize = state.child_reads.into();
|
||||
assert_eq!(
|
||||
expected_child_reads, child_reads,
|
||||
"expected {:?}'s (id:{id:?}) child_reads to be {} instead of {}",
|
||||
node.tag, expected_child_reads, child_reads
|
||||
);
|
||||
let child_writes: usize = state.child_writes.into();
|
||||
assert_eq!(
|
||||
expected_child_writes, child_writes,
|
||||
"expected {:?}'s (id:{id:?}) child_writes to be {} instead of {}",
|
||||
node.tag, expected_child_writes, child_writes
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -181,13 +181,13 @@ impl EpollInterestTable {
|
|||
.borrow_mut()
|
||||
.extract_if(range_for_id(id), |_, _| true)
|
||||
// Consume the iterator.
|
||||
.for_each(|_| ());
|
||||
.for_each(drop);
|
||||
epoll
|
||||
.ready_set
|
||||
.borrow_mut()
|
||||
.extract_if(range_for_id(id), |_| true)
|
||||
// Consume the iterator.
|
||||
.for_each(|_| ());
|
||||
.for_each(drop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
src/tools/miri/tests/fail/tree_borrows/wildcard/dealloc.rs
Normal file
21
src/tools/miri/tests/fail/tree_borrows/wildcard/dealloc.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that deallocation through a wildcard ref fails,
|
||||
/// if all exposed references are disabled.
|
||||
pub fn main() {
|
||||
use std::alloc::Layout;
|
||||
let x = unsafe { std::alloc::alloc_zeroed(Layout::new::<u32>()) as *mut u32 };
|
||||
|
||||
let ref1 = unsafe { &mut *x };
|
||||
let ref2 = unsafe { &mut *x };
|
||||
|
||||
let int = ref1 as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
// Disables ref1 and therefore also wild.
|
||||
*ref2 = 14;
|
||||
|
||||
// Tries to dealloc through a wildcard reference even though all exposed
|
||||
// references are disabled.
|
||||
|
||||
unsafe { std::alloc::dealloc(wild as *mut u8, Layout::new::<u32>()) }; //~ ERROR: /deallocation through <wildcard> .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: deallocation through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/dealloc.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { std::alloc::dealloc(wild as *mut u8, Layout::new::<u32>()) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
20
src/tools/miri/tests/fail/tree_borrows/wildcard/gc.rs
Normal file
20
src/tools/miri/tests/fail/tree_borrows/wildcard/gc.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
#[path = "../../../utils/mod.rs"]
|
||||
mod utils;
|
||||
|
||||
/// Checks that the garbage collector doesn't remove any exposed tags.
|
||||
fn main() {
|
||||
let mut _x: u32 = 4;
|
||||
let int = {
|
||||
let y = &_x;
|
||||
y as *const u32 as usize
|
||||
};
|
||||
// If y wasn't exposed, this would gc it.
|
||||
utils::run_provenance_gc();
|
||||
// This should disable y.
|
||||
_x = 5;
|
||||
let wild = int as *const u32;
|
||||
|
||||
let _fail = unsafe { *wild }; //~ ERROR: /read access through <wildcard> at .* is forbidden/
|
||||
}
|
||||
14
src/tools/miri/tests/fail/tree_borrows/wildcard/gc.stderr
Normal file
14
src/tools/miri/tests/fail/tree_borrows/wildcard/gc.stderr
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: read access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/gc.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = unsafe { *wild };
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that disabling an exposed reference correctly narrows the
|
||||
/// possible locations a wildcard access could happen from.
|
||||
/// Also checks that an access is treated as foreign, if all exposed
|
||||
/// (non-disabled) references are ancestors.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ref1 = &mut x;
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
|
||||
let ref2 = &mut *ref1;
|
||||
|
||||
let ref3 = &mut *ref2;
|
||||
let _int3 = ref3 as *mut u32 as usize;
|
||||
|
||||
// Write through ref3 so that all references are active.
|
||||
*ref3 = 43;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref1(Act)* │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref2(Act) │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref3(Act)* │
|
||||
// │ │
|
||||
// └────────────┘
|
||||
|
||||
// Writes through either ref1 or ref3, which is either a child or foreign
|
||||
// access to ref2.
|
||||
unsafe { wild.write(42) };
|
||||
|
||||
// Reading from ref2 still works, since the previous access could have been
|
||||
// through its child.
|
||||
// This also freezes ref3.
|
||||
let _x = *ref2;
|
||||
|
||||
// We can still write through wild, as there is still the exposed ref1 with
|
||||
// write permissions under proper exposed provenance, this would be UB as the
|
||||
// only tag wild can assume to not invalidate ref2 is ref3, which we just
|
||||
// invalidated.
|
||||
//
|
||||
// This disables ref2, ref3.
|
||||
unsafe { wild.write(43) };
|
||||
|
||||
// Fails because ref2 is disabled.
|
||||
let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = *ref2;
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
|
||||
help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child.rs:LL:CC
|
||||
|
|
||||
LL | let ref2 = &mut *ref1;
|
||||
| ^^^^^^^^^^
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(43) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to a loss of read and write permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks if we correctly determine the correct exposed reference a write
|
||||
/// access could happen through,
|
||||
/// if there are also exposed reference through which only a read could happen.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ref1 = &mut x;
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
|
||||
let ref2 = &mut *ref1;
|
||||
|
||||
let ref3 = &*ref2;
|
||||
let _int3 = ref3 as *const u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref1(Res)* │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref2(Res) │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref3(Frz)* │
|
||||
// │ │
|
||||
// └────────────┘
|
||||
|
||||
// Writes through ref1 as we cannot write through ref3 since it's frozen.
|
||||
// Disables ref2, ref3.
|
||||
unsafe { wild.write(42) };
|
||||
|
||||
// ref2 is disabled.
|
||||
let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = *ref2;
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
|
||||
help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | let ref2 = &mut *ref1;
|
||||
| ^^^^^^^^^^
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_child_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(42) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to a loss of read and write permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks with multiple exposed nodes, that if they are all disabled
|
||||
/// then no wildcard accesses are possible.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
let ref3 = unsafe { &mut *ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├──────────────┬───────────────────┐
|
||||
// │ │ │ │
|
||||
// └──────┬─────┘ │ │
|
||||
// │ │ │
|
||||
// │ │ │
|
||||
// ▼ ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐ ┌───────────┐
|
||||
// │ │ │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res)* │ │ ref3(Res) │
|
||||
// │ │ │ │ │ │
|
||||
// └────────────┘ └────────────┘ └───────────┘
|
||||
|
||||
// Disables ref1,ref2.
|
||||
*ref3 = 13;
|
||||
|
||||
// Both exposed references are disabled so this fails.
|
||||
let _fail = unsafe { *wild }; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: read access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_disable.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = unsafe { *wild };
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that if for a node all exposed references are foreign,
|
||||
/// that a wildcard access gets treated as foreign to it.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
let ref3 = unsafe { &mut *ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├──────────────┬───────────────────┐
|
||||
// │ │ │ │
|
||||
// └──────┬─────┘ │ │
|
||||
// │ │ │
|
||||
// │ │ │
|
||||
// ▼ ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐ ┌───────────┐
|
||||
// │ │ │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res)* │ │ ref3(Res) │
|
||||
// │ │ │ │ │ │
|
||||
// └────────────┘ └────────────┘ └───────────┘
|
||||
|
||||
// Disables ref3 as both exposed pointers are foreign to it.
|
||||
unsafe { wild.write(13) };
|
||||
|
||||
// Fails because ref3 is disabled.
|
||||
let _fail = *ref3; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_foreign.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = *ref3;
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
|
||||
help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_foreign.rs:LL:CC
|
||||
|
|
||||
LL | let ref3 = unsafe { &mut *ptr_base };
|
||||
| ^^^^^^^^^^^^^^
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_foreign.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(13) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to a loss of read and write permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks if a local access gets correctly triggered, if we know that
|
||||
/// all exposed references are local to a node.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// Activates ptr_base.
|
||||
unsafe { wild.write(41) };
|
||||
|
||||
// ┌─────────────┐
|
||||
// │ │
|
||||
// │ x (Act) │
|
||||
// │ │
|
||||
// └──────┬──────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────────┐
|
||||
// │ │
|
||||
// │ ptr_base (Act) ├──────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res)* │
|
||||
// │ │ │ │
|
||||
// └────────────┘ └────────────┘
|
||||
|
||||
// We read from x causing a foreign access to ptr_base, freezing it
|
||||
// (as the previous wildcard access has made it active).
|
||||
let _y = x;
|
||||
|
||||
// While both exposed references are still enabled for writes, any write
|
||||
// through them would cause UB at ptr_base.
|
||||
unsafe { wild.write(0) }; //~ ERROR: /write access through <wildcard> at .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_local.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(0) };
|
||||
| ^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <wildcard> is a child of the conflicting tag <TAG>
|
||||
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
|
||||
help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_local.rs:LL:CC
|
||||
|
|
||||
LL | let ptr_base = &mut x as *mut u32;
|
||||
| ^^^^^^
|
||||
help: the conflicting tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_local.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(41) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||
help: the conflicting tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_local.rs:LL:CC
|
||||
|
|
||||
LL | let _y = x;
|
||||
| ^
|
||||
= help: this transition corresponds to a loss of write permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks if we correctly determine the correct exposed reference a write
|
||||
/// access could happen through, if there are also exposed reference
|
||||
/// through which only a read access could happen.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &*ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *const u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├──────────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Frz)* │
|
||||
// │ │ │ │
|
||||
// └────────────┘ └────────────┘
|
||||
|
||||
// Disables ref2 as the only write could happen through ref1.
|
||||
unsafe { wild.write(13) };
|
||||
|
||||
// Fails because ref2 is disabled.
|
||||
let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = *ref2;
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
|
||||
help: the accessed tag <TAG> was created here, in the initial state Frozen
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | let ref2 = unsafe { &*ptr_base };
|
||||
| ^^^^^^^^^^
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/multi_exposed_siblings_unique_writer.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(13) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to a loss of read permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that wildcard accesses correctly infers the allowed permissions
|
||||
/// on protected conflicted pointers.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
let protect = |arg: &mut u32| {
|
||||
// Expose arg.
|
||||
let int = arg as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
|
||||
// Does a foreign read to arg marking it as conflicted and making child
|
||||
// writes UB while it's protected.
|
||||
let _x = *ref2;
|
||||
|
||||
// The only exposed reference (arg) doesn't allow child writes, so this is UB.
|
||||
unsafe { *wild = 4 }; //~ ERROR: /write access through <wildcard> at .* is forbidden/
|
||||
};
|
||||
|
||||
protect(ref1);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/protector_conflicted.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { *wild = 4 };
|
||||
| ^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
= note: BACKTRACE:
|
||||
= note: inside closure at tests/fail/tree_borrows/wildcard/protector_conflicted.rs:LL:CC
|
||||
note: inside `main`
|
||||
--> tests/fail/tree_borrows/wildcard/protector_conflicted.rs:LL:CC
|
||||
|
|
||||
LL | protect(ref1);
|
||||
| ^^^^^^^^^^^^^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that with only one exposed reference, that if this reference gets
|
||||
/// disabled no wildcard accesses are possible.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├───────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌───────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res) │
|
||||
// │ │ │ │
|
||||
// └────────────┘ └───────────┘
|
||||
|
||||
// Disables ref1.
|
||||
*ref2 = 13;
|
||||
|
||||
// Tries to do a wildcard access through the only exposed reference ref1,
|
||||
// which is disabled.
|
||||
let _fail = unsafe { *wild }; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: read access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_disable.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = unsafe { *wild };
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks that with only one exposed reference, wildcard accesses
|
||||
/// correctly cause foreign accesses.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├───────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌───────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res) │
|
||||
// │ │ │ │
|
||||
// └────────────┘ └───────────┘
|
||||
|
||||
// Write through the wildcard to the only exposed reference ref1,
|
||||
// disabling ref2.
|
||||
unsafe { wild.write(13) };
|
||||
|
||||
let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_foreign.rs:LL:CC
|
||||
|
|
||||
LL | let _fail = *ref2;
|
||||
| ^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
|
||||
help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_foreign.rs:LL:CC
|
||||
|
|
||||
LL | let ref2 = unsafe { &mut *ptr_base };
|
||||
| ^^^^^^^^^^^^^^
|
||||
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_foreign.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(13) };
|
||||
| ^^^^^^^^^^^^^^
|
||||
= help: this transition corresponds to a loss of read and write permissions
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// Checks if a local access gets correctly triggered during wildcard access,
|
||||
/// if we know that the only exposed reference is local to the node.
|
||||
pub fn main() {
|
||||
let mut x: u32 = 0;
|
||||
|
||||
let ref1 = &mut x;
|
||||
|
||||
let int = ref1 as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
|
||||
// Activates ref1.
|
||||
unsafe { wild.write(41) };
|
||||
|
||||
// Reads from x causing a foreign read on ref1, freezing it
|
||||
// (because it was active).
|
||||
let _y = x;
|
||||
|
||||
// The only exposed reference (ref1) is frozen, so wildcard writes are UB.
|
||||
unsafe { wild.write(0) }; //~ ERROR: /write access through <wildcard> at .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_local.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { wild.write(0) };
|
||||
| ^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
/// If we have only exposed read-only pointers, doing a write through a
|
||||
/// wildcard ptr should fail.
|
||||
fn main() {
|
||||
let mut x = 0;
|
||||
let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic
|
||||
let addr = (&x as *const i32).expose_provenance();
|
||||
let ptr = std::ptr::with_exposed_provenance_mut::<i32>(addr);
|
||||
unsafe { *ptr = 0 }; //~ ERROR: /write access through <wildcard> at .* is forbidden/
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> tests/fail/tree_borrows/wildcard/single_exposed_only_ro.rs:LL:CC
|
||||
|
|
||||
LL | unsafe { *ptr = 0 };
|
||||
| ^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: there are no exposed tags which may perform this access here
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
//@error-in-other-file: /deallocation through .* is forbidden/
|
||||
|
||||
fn inner(x: &mut i32, f: fn(usize)) {
|
||||
// `f` may mutate, but it may not deallocate!
|
||||
// `f` takes a raw pointer so that the only protector
|
||||
// is that on `x`
|
||||
f(x as *mut i32 as usize)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
inner(Box::leak(Box::new(0)), |raw| {
|
||||
drop(unsafe { Box::from_raw(raw as *mut i32) });
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
error: Undefined Behavior: deallocation through <wildcard> at ALLOC[0x0] is forbidden
|
||||
--> RUSTLIB/alloc/src/boxed.rs:LL:CC
|
||||
|
|
||||
LL | self.1.deallocate(From::from(ptr.cast()), layout);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
||||
|
|
||||
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
|
||||
= help: the allocation of the accessed tag <wildcard> also contains the strongly protected tag <TAG>
|
||||
= help: the strongly protected tag <TAG> disallows deallocations
|
||||
help: the strongly protected tag <TAG> was created here, in the initial state Reserved
|
||||
--> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
|
||||
|
|
||||
LL | fn inner(x: &mut i32, f: fn(usize)) {
|
||||
| ^
|
||||
= note: BACKTRACE (of the first span):
|
||||
= note: inside `<std::boxed::Box<i32> as std::ops::Drop>::drop` at RUSTLIB/alloc/src/boxed.rs:LL:CC
|
||||
= note: inside `std::ptr::drop_in_place::<std::boxed::Box<i32>> - shim(Some(std::boxed::Box<i32>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
|
||||
= note: inside `std::mem::drop::<std::boxed::Box<i32>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
|
||||
note: inside closure
|
||||
--> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
|
||||
|
|
||||
LL | drop(unsafe { Box::from_raw(raw as *mut i32) });
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: inside `<{closure@tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC} as std::ops::FnOnce<(usize,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
|
||||
note: inside `inner`
|
||||
--> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
|
||||
|
|
||||
LL | f(x as *mut i32 as usize)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
note: inside `main`
|
||||
--> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
|
||||
|
|
||||
LL | / inner(Box::leak(Box::new(0)), |raw| {
|
||||
LL | | drop(unsafe { Box::from_raw(raw as *mut i32) });
|
||||
LL | | });
|
||||
| |______^
|
||||
|
||||
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
//@revisions: stack tree
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
use std::ptr;
|
||||
|
||||
// Just to make sure that casting a ref to raw, to int and back to raw
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
//@revisions: stack tree
|
||||
//@compile-flags: -Zmiri-permissive-provenance
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
use std::cell::Cell;
|
||||
|
||||
fn main() {
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// This test seems to involve a "stashed diagnostic" (or at least it used to at the time of
|
||||
// writing). Ensure we handle that correctly.
|
||||
|
||||
pub trait Trait {
|
||||
type Assoc: Assoc;
|
||||
}
|
||||
|
||||
pub trait Assoc {}
|
||||
|
||||
fn main() {}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
warning: integer-to-pointer cast
|
||||
--> tests/pass/stacked_borrows/issue-miri-2389.rs:LL:CC
|
||||
|
|
||||
LL | let wildcard = &root0 as *const Cell<i32> as usize as *const Cell<i32>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast
|
||||
|
|
||||
= help: this program is using integer-to-pointer casts or (equivalently) `ptr::with_exposed_provenance`, which means that Miri might miss pointer bugs in this program
|
||||
= help: see https://doc.rust-lang.org/nightly/std/ptr/fn.with_exposed_provenance.html for more details on that operation
|
||||
= help: to ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead
|
||||
= help: you can then set `MIRIFLAGS=-Zmiri-strict-provenance` to ensure you are not relying on `with_exposed_provenance` semantics
|
||||
= help: alternatively, `MIRIFLAGS=-Zmiri-permissive-provenance` disables this warning
|
||||
|
||||
145
src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs
Normal file
145
src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
// NOTE: This file documents UB that is not detected by wildcard provenance.
|
||||
|
||||
pub fn main() {
|
||||
uncertain_provenance();
|
||||
protected_exposed();
|
||||
protected_wildcard();
|
||||
}
|
||||
|
||||
/// Currently, if we do not know for a tag if an access is local or foreign,
|
||||
/// then we do not do any state transitions on that tag. However, to implement
|
||||
/// proper provenance we would have to instead pick the correct transition
|
||||
/// non-deterministically.
|
||||
///
|
||||
/// This test contains such UB that will not be detectable with wildcard provenance,
|
||||
/// but would be detectable if we implemented this behavior correctly.
|
||||
pub fn uncertain_provenance() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
// We create 2 mutable references, each with a unique tag.
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
//ref1 : Reserved
|
||||
//ref2 : Reserved
|
||||
|
||||
// We need to pick the "correct" tag for wild from the exposed tags.
|
||||
let wild = int1 as *mut u32;
|
||||
// wild=ref1 wild=ref2
|
||||
//ref1 : Reserved Reserved
|
||||
//ref2 : Reserved Reserved
|
||||
|
||||
// We write to wild, disabling the other tag.
|
||||
unsafe { wild.write(13) };
|
||||
// wild=ref1 wild=ref2
|
||||
//ref1 : Unique Disabled
|
||||
//ref2 : Disabled Unique
|
||||
|
||||
// We access both references, even though one of them should be
|
||||
// disabled under proper exposed provenance.
|
||||
// This is UB, however, wildcard provenance cannot detect this.
|
||||
assert_eq!(*ref1, 13);
|
||||
// wild=ref1 wild=ref2
|
||||
//ref1 : Unique UB
|
||||
//ref2 : Disabled Frozen
|
||||
assert_eq!(*ref2, 13);
|
||||
// wild=ref1 wild=ref2
|
||||
//ref1 : Frozen UB
|
||||
//ref2 : UB Frozen
|
||||
}
|
||||
|
||||
/// If a reference is protected, then all foreign writes to it cause UB.
|
||||
/// This effectively means any write needs to happen through a child of
|
||||
/// the protected reference.
|
||||
/// With this information we could further narrow the possible candidates
|
||||
/// for a wildcard write.
|
||||
/// However, currently tree borrows doesn't do this, so this test has UB
|
||||
/// that isn't detected.
|
||||
pub fn protected_exposed() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
|
||||
fn protect(ref3: &mut u32) {
|
||||
let int3 = ref3 as *mut u32 as usize;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├──────────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res) │ │ ref2(Res)* │
|
||||
// │ │ │ │
|
||||
// └──────┬─────┘ └────────────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref3(Res)* │
|
||||
// │ │
|
||||
// └────────────┘
|
||||
|
||||
// Since ref3 is protected, we could know that every write from outside it will be UB.
|
||||
// This means we know that the access is through ref3, disabling ref2.
|
||||
let wild = int3 as *mut u32;
|
||||
unsafe { wild.write(13) }
|
||||
}
|
||||
protect(ref1);
|
||||
|
||||
// ref2 is disabled, so this read causes UB, but we currently don't protect this.
|
||||
let _fail = *ref2;
|
||||
}
|
||||
|
||||
/// Currently, we do not assign protectors to wildcard references.
|
||||
/// This test has UB because it does a foreign write to a protected reference.
|
||||
/// However, that reference is a wildcard, so this doesn't get detected.
|
||||
#[allow(unused_variables)]
|
||||
pub fn protected_wildcard() {
|
||||
let mut x: u32 = 32;
|
||||
let ref1 = &mut x;
|
||||
let ref2 = &mut *ref1;
|
||||
|
||||
let int = ref2 as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
let wild_ref = unsafe { &mut *wild };
|
||||
|
||||
let mut protect = |arg: &mut u32| {
|
||||
// arg is a protected pointer with wildcard provenance.
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref1(Res) │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref2(Res)* │
|
||||
// │ │
|
||||
// └────────────┘
|
||||
|
||||
// Writes to ref1, disabling ref2, i.e. disabling all exposed references.
|
||||
// Since a wildcard reference is protected, this is UB. But we currently don't detect this.
|
||||
*ref1 = 13;
|
||||
};
|
||||
|
||||
// We pass a pointer with wildcard provenance to the function.
|
||||
protect(wild_ref);
|
||||
}
|
||||
171
src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs
Normal file
171
src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
|
||||
|
||||
pub fn main() {
|
||||
multiple_exposed_siblings();
|
||||
multiple_exposed_child();
|
||||
dealloc();
|
||||
protector();
|
||||
protector_conflicted_release();
|
||||
returned_mut_is_usable();
|
||||
}
|
||||
|
||||
/// Checks that an access through a wildcard reference
|
||||
/// doesn't disable any exposed references.
|
||||
/// It tests this with exposed references that are siblings of each other.
|
||||
pub fn multiple_exposed_siblings() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
// Both references get exposed.
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
let _int2 = ref2 as *mut u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ptr_base ├────────────┐
|
||||
// │ │ │
|
||||
// └──────┬─────┘ │
|
||||
// │ │
|
||||
// │ │
|
||||
// ▼ ▼
|
||||
// ┌────────────┐ ┌────────────┐
|
||||
// │ │ │ │
|
||||
// │ ref1(Res)* │ │ ref2(Res)* │
|
||||
// │ │ │ │
|
||||
// └────────────┘ └────────────┘
|
||||
|
||||
// Writes through either of the two exposed references.
|
||||
// We do not know which so we cannot disable the other.
|
||||
unsafe { wild.write(13) };
|
||||
|
||||
// Reading through either of these references should be valid.
|
||||
assert_eq!(*ref2, 13);
|
||||
}
|
||||
|
||||
/// Checks that an access through a wildcard reference
|
||||
/// doesn't disable any exposed references.
|
||||
/// It tests this with exposed references where one is the ancestor of the other.
|
||||
pub fn multiple_exposed_child() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ref1 = &mut x;
|
||||
let int1 = ref1 as *mut u32 as usize;
|
||||
|
||||
let ref2 = &mut *ref1;
|
||||
|
||||
let ref3 = &mut *ref2;
|
||||
let _int3 = ref3 as *mut u32 as usize;
|
||||
|
||||
let wild = int1 as *mut u32;
|
||||
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref1(Res)* │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref2(Res) │
|
||||
// │ │
|
||||
// └──────┬─────┘
|
||||
// │
|
||||
// │
|
||||
// ▼
|
||||
// ┌────────────┐
|
||||
// │ │
|
||||
// │ ref3(Res)* │
|
||||
// │ │
|
||||
// └────────────┘
|
||||
|
||||
// This writes either through ref1 or ref3, which is either a child or foreign access to ref2.
|
||||
unsafe { wild.write(42) };
|
||||
|
||||
// Reading from ref2 still works, since the previous access could have been through its child.
|
||||
// This also freezes ref3.
|
||||
let _x = *ref2;
|
||||
|
||||
// We can still write through wild, as there is still the exposed ref1 with write permissions.
|
||||
unsafe { wild.write(43) };
|
||||
}
|
||||
|
||||
/// Checks that we can deallocate through a wildcard reference.
|
||||
fn dealloc() {
|
||||
use std::alloc::Layout;
|
||||
let x = unsafe { std::alloc::alloc_zeroed(Layout::new::<u32>()) as *mut u32 };
|
||||
let ref1 = unsafe { &mut *x };
|
||||
let int = ref1 as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
|
||||
unsafe { std::alloc::dealloc(wild as *mut u8, Layout::new::<u32>()) };
|
||||
}
|
||||
|
||||
/// Checks that we can pass a wildcard reference to a function.
|
||||
fn protector() {
|
||||
fn protect(arg: &mut u32) {
|
||||
*arg = 4;
|
||||
}
|
||||
let mut x: u32 = 32;
|
||||
let ref1 = &mut x;
|
||||
let int = ref1 as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
let wild_ref = unsafe { &mut *wild };
|
||||
|
||||
protect(wild_ref);
|
||||
|
||||
assert_eq!(*ref1, 4);
|
||||
}
|
||||
|
||||
/// Checks whether we correctly handle the protector being released on
|
||||
/// a conflicted exposed reference.
|
||||
fn protector_conflicted_release() {
|
||||
let mut x: u32 = 42;
|
||||
|
||||
let ptr_base = &mut x as *mut u32;
|
||||
let ref1 = unsafe { &mut *ptr_base };
|
||||
let ref2 = unsafe { &mut *ptr_base };
|
||||
|
||||
let protect = |arg: &mut u32| {
|
||||
// Expose arg.
|
||||
let int = arg as *mut u32 as usize;
|
||||
let wild = int as *mut u32;
|
||||
|
||||
// Do a foreign read to arg marking it as conflicted and making child_writes UB while its protected.
|
||||
let _x = *ref2;
|
||||
|
||||
return wild;
|
||||
};
|
||||
|
||||
let wild = protect(ref1);
|
||||
|
||||
// The protector on arg got released so writes through arg should work again.
|
||||
unsafe { *wild = 4 };
|
||||
}
|
||||
|
||||
/// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference.
|
||||
fn returned_mut_is_usable() {
|
||||
// NOTE: Currently we ignore protectors on wildcard references.
|
||||
fn reborrow(x: &mut u8) -> &mut u8 {
|
||||
let y = &mut *x;
|
||||
// Activate the reference so that it is vulnerable to foreign reads.
|
||||
*y = *y;
|
||||
y
|
||||
// An implicit read through `x` is inserted here.
|
||||
}
|
||||
let mut x: u8 = 0;
|
||||
let ref1 = &mut x;
|
||||
let int = ref1 as *mut u8 as usize;
|
||||
let wild = int as *mut u8;
|
||||
let wild_ref = unsafe { &mut *wild };
|
||||
|
||||
let y = reborrow(wild_ref);
|
||||
|
||||
*y = 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue