Skip to content

Commit f128b78

Browse files
committed
Auto merge of rust-lang#129836 - jieyouxu:exp-msvc-ci-2, r=<try>
[EXPERIMENTAL] more msvc ci debugging, do not look, cursed things inside Messing around with msvc CI again... This time just trying to find if any processes are holding on to e.g. `miri.exe`. This is just a proof of concept, Chris probably knows how to implement this correctly lol. It uses `sysinfo` later because I got lazy and can't be bummed to also implement the find process info stuff in syscalls. Please do not look inside as this PR contains provenance crimes and most likely UB. This PR is not a place of honor... no highly esteemed deed is commemorated here... nothing valued is here. What is here was dangerous and repulsive to us. This message is a warning about danger. The danger is in a particular location... it increases towards a center... the center of danger is here... of a particular size and shape, and below us. The danger is still present, in your time, as it was in ours. The danger is to the body, and it can kill. The form of the danger is an emanation of Windows API calls. The danger is unleashed only if you substantially look at this PR. This PR is best shunned and left uninhabited. r? ghost try-job: x86_64-msvc-ext
2 parents a48861a + 1152626 commit f128b78

File tree

5 files changed

+234
-7
lines changed

5 files changed

+234
-7
lines changed

src/bootstrap/Cargo.lock

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ version = "1.0.8"
1717
source = "registry+https://github.com/rust-lang/crates.io-index"
1818
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
1919

20+
[[package]]
21+
name = "anyhow"
22+
version = "1.0.86"
23+
source = "registry+https://github.com/rust-lang/crates.io-index"
24+
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
25+
2026
[[package]]
2127
name = "bitflags"
2228
version = "2.6.0"
@@ -36,6 +42,7 @@ dependencies = [
3642
name = "bootstrap"
3743
version = "0.0.0"
3844
dependencies = [
45+
"anyhow",
3946
"build_helper",
4047
"cc",
4148
"clap",

src/bootstrap/Cargo.toml

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build = "build.rs"
66
default-run = "bootstrap"
77

88
[features]
9+
default = ["sysinfo"]
910
build-metrics = ["sysinfo"]
1011
bootstrap-self-test = [] # enabled in the bootstrap unit tests
1112

@@ -41,7 +42,7 @@ cc = "=1.0.97"
4142
cmake = "=0.1.48"
4243

4344
build_helper = { path = "../tools/build_helper" }
44-
clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
45+
clap = { version = "4.4", default-features = false, features = ["derive", "error-context", "help", "std", "usage"] }
4546
clap_complete = "4.4"
4647
fd-lock = "4.0"
4748
home = "0.5"
@@ -62,6 +63,9 @@ toml = "0.5"
6263
walkdir = "2.4"
6364
xz2 = "0.1"
6465

66+
# EXPERIMENTAL
67+
anyhow = "1"
68+
6569
# Dependencies needed by the build-metrics feature
6670
sysinfo = { version = "0.31.2", default-features = false, optional = true, features = ["system"] }
6771

@@ -71,13 +75,19 @@ version = "1.0.0"
7175
[target.'cfg(windows)'.dependencies.windows]
7276
version = "0.52"
7377
features = [
78+
"Wdk_Foundation",
79+
"Wdk_Storage_FileSystem",
80+
"Wdk_System_SystemServices",
7481
"Win32_Foundation",
7582
"Win32_Security",
83+
"Win32_Storage_FileSystem",
7684
"Win32_System_Diagnostics_Debug",
85+
"Win32_System_IO",
7786
"Win32_System_JobObjects",
7887
"Win32_System_ProcessStatus",
7988
"Win32_System_Threading",
8089
"Win32_System_Time",
90+
"Win32_System_WindowsProgramming",
8191
]
8292

8393
[dev-dependencies]

src/bootstrap/src/lib.rs

+58
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ use crate::utils::helpers::{
4343

4444
mod core;
4545
mod utils;
46+
#[cfg(windows)]
47+
mod windows_hacks;
4648

4749
pub use core::builder::PathSet;
4850
pub use core::config::flags::{Flags, Subcommand};
@@ -1663,12 +1665,68 @@ Executed at: {executed_at}"#,
16631665
if src == dst {
16641666
return;
16651667
}
1668+
16661669
if let Err(e) = fs::remove_file(dst) {
16671670
if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
16681671
// workaround for https://github.com/rust-lang/rust/issues/127126
16691672
// if removing the file fails, attempt to rename it instead.
16701673
let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
16711674
let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
1675+
1676+
#[cfg(windows)]
1677+
{
1678+
eprintln!(
1679+
"[DEBUG]: copy_link_internal: `fs::remove_file` failed on dst=`{}`: {e}",
1680+
dst.display()
1681+
);
1682+
eprintln!("[DEBUG]: copy_link_internal: after `fs::remove_file` failed");
1683+
// HACK(jieyouxu): let's see what's holding up. Note that this is not robost to TOCTOU
1684+
// races where the process was holding on to the file when calling `remove_file` but
1685+
// released immediately after before gathering process IDs holding the file.
1686+
let mut process_ids = windows_hacks::process_ids_using_file(dst).unwrap();
1687+
process_ids.dedup();
1688+
process_ids.sort();
1689+
1690+
if !process_ids.is_empty() {
1691+
eprintln!(
1692+
"[DEBUG] copy_link_internal: pids holding dst=`{}`: {:?}",
1693+
dst.display(),
1694+
process_ids
1695+
);
1696+
}
1697+
1698+
use sysinfo::{ProcessRefreshKind, RefreshKind, System};
1699+
1700+
let sys = System::new_with_specifics(
1701+
RefreshKind::new().with_processes(ProcessRefreshKind::everything()),
1702+
);
1703+
1704+
let mut holdups = vec![];
1705+
for (pid, process) in sys.processes() {
1706+
if process_ids.contains(&(pid.as_u32() as usize)) {
1707+
holdups.push((pid.as_u32(), process.exe().unwrap_or(Path::new(""))));
1708+
}
1709+
}
1710+
1711+
if holdups.is_empty() {
1712+
eprintln!(
1713+
"[DEBUG] copy_link_internal: did not find any process holding up dst=`{}`, so how did we fail?",
1714+
dst.display(),
1715+
);
1716+
} else {
1717+
eprintln!(
1718+
"[DEBUG] copy_link_internal: printing process names (where available) holding dst=`{}`",
1719+
dst.display()
1720+
);
1721+
for (pid, process_exe) in holdups {
1722+
eprintln!(
1723+
"[DEBUG] copy_link_internal: process holding dst=`{}`: pid={pid}, process_name={:?}",
1724+
dst.display(),
1725+
process_exe
1726+
);
1727+
}
1728+
}
1729+
}
16721730
}
16731731
}
16741732
let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));

src/bootstrap/src/windows_hacks.rs

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//! Experimental windows hacks to try find what the hecc is holding on to the files that cannot be
2+
//! deleted.
3+
4+
// Adapted from <https://stackoverflow.com/questions/67187979/how-to-call-ntopenfile> from
5+
// Delphi for Rust :3
6+
// Also references <https://gist.github.com/antonioCoco/9db236d6089b4b492746f7de31b21d9d>.
7+
8+
// SAFETY:
9+
// YOLO.
10+
11+
// Windows API naming
12+
#![allow(nonstandard_style)]
13+
// Well because CI does deny-warnings :)
14+
#![deny(unused_imports)]
15+
16+
use std::mem;
17+
use std::os::windows::ffi::OsStrExt;
18+
use std::path::Path;
19+
20+
use anyhow::Result;
21+
use windows::core::PWSTR;
22+
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
23+
use windows::Wdk::Storage::FileSystem::{
24+
NtOpenFile, NtQueryInformationFile, FILE_OPEN_REPARSE_POINT,
25+
};
26+
use windows::Wdk::System::SystemServices::FILE_PROCESS_IDS_USING_FILE_INFORMATION;
27+
use windows::Win32::Foundation::{
28+
CloseHandle, HANDLE, STATUS_INFO_LENGTH_MISMATCH, UNICODE_STRING,
29+
};
30+
use windows::Win32::Storage::FileSystem::{
31+
FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE,
32+
};
33+
use windows::Win32::System::WindowsProgramming::FILE_INFORMATION_CLASS;
34+
use windows::Win32::System::IO::IO_STATUS_BLOCK;
35+
36+
/// Wraps a windows API that returns [`NTSTATUS`]:
37+
///
38+
/// - First convert [`NTSTATUS`] to [`HRESULT`].
39+
/// - Then convert [`HRESULT`] into a [`WinError`] with or without optional info.
40+
macro_rules! try_syscall {
41+
($syscall: expr) => {{
42+
let status = $syscall;
43+
if status.is_err() {
44+
::anyhow::Result::Err(::windows::core::Error::from(status.to_hresult()))?;
45+
}
46+
}};
47+
($syscall: expr, $additional_info: expr) => {{
48+
let status = $syscall;
49+
if status.is_err() {
50+
::anyhow::Result::Err(::windows::core::Error::new(
51+
$syscall.into(),
52+
$additional_info.into(),
53+
))?;
54+
}
55+
}};
56+
}
57+
58+
pub(crate) fn process_ids_using_file(path: &Path) -> Result<Vec<usize>> {
59+
// Gotta have it in UTF-16LE.
60+
let mut nt_path = {
61+
let path = std::path::absolute(path)?;
62+
r"\??\".encode_utf16().chain(path.as_os_str().encode_wide()).collect::<Vec<u16>>()
63+
};
64+
65+
let nt_path_unicode_string = UNICODE_STRING {
66+
Length: u16::try_from(nt_path.len() * 2)?,
67+
MaximumLength: u16::try_from(nt_path.len() * 2)?,
68+
Buffer: PWSTR::from_raw(nt_path.as_mut_ptr()),
69+
};
70+
71+
let object_attributes = OBJECT_ATTRIBUTES {
72+
Length: mem::size_of::<OBJECT_ATTRIBUTES>() as _,
73+
ObjectName: &nt_path_unicode_string,
74+
..Default::default()
75+
};
76+
77+
let mut io_status = IO_STATUS_BLOCK::default();
78+
let mut handle = HANDLE::default();
79+
80+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntopenfile
81+
try_syscall!(
82+
unsafe {
83+
NtOpenFile(
84+
&mut handle as *mut _,
85+
FILE_READ_ATTRIBUTES.0,
86+
&object_attributes,
87+
&mut io_status as *mut _,
88+
(FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE).0,
89+
FILE_OPEN_REPARSE_POINT.0,
90+
)
91+
},
92+
"tried to open file"
93+
);
94+
95+
/// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class
96+
// Remark: apparently windows 0.52 doesn't have this or something, it appears in at least >=
97+
// 0.53.
98+
const FileProcessIdsUsingFileInformation: FILE_INFORMATION_CLASS = FILE_INFORMATION_CLASS(47);
99+
100+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-ntqueryinformationfile
101+
const INCREMENT: usize = 8;
102+
let mut buf = vec![FILE_PROCESS_IDS_USING_FILE_INFORMATION::default(); INCREMENT as usize];
103+
let mut buf_idx = 0;
104+
let mut status = unsafe {
105+
NtQueryInformationFile(
106+
handle,
107+
&mut io_status as *mut _,
108+
buf.as_mut_ptr().cast(),
109+
(INCREMENT * mem::size_of::<FILE_PROCESS_IDS_USING_FILE_INFORMATION>()) as u32,
110+
FileProcessIdsUsingFileInformation,
111+
)
112+
};
113+
while status == STATUS_INFO_LENGTH_MISMATCH {
114+
buf.resize(buf.len() + INCREMENT, FILE_PROCESS_IDS_USING_FILE_INFORMATION::default());
115+
buf_idx += INCREMENT;
116+
status = unsafe {
117+
NtQueryInformationFile(
118+
handle,
119+
&mut io_status as *mut _,
120+
buf.as_mut_ptr()
121+
.offset(
122+
(buf_idx * mem::size_of::<FILE_PROCESS_IDS_USING_FILE_INFORMATION>())
123+
as isize,
124+
)
125+
.cast(),
126+
(INCREMENT * mem::size_of::<FILE_PROCESS_IDS_USING_FILE_INFORMATION>()) as u32,
127+
FileProcessIdsUsingFileInformation,
128+
)
129+
};
130+
}
131+
132+
let mut process_ids = vec![];
133+
134+
for FILE_PROCESS_IDS_USING_FILE_INFORMATION {
135+
NumberOfProcessIdsInList,
136+
ProcessIdList: [ptr],
137+
} in buf
138+
{
139+
if NumberOfProcessIdsInList >= 1 {
140+
// only fetch the first one
141+
process_ids.push(unsafe {
142+
// This is almost certaintly UB, provenance be damned
143+
let ptr = ptr as *mut usize;
144+
*ptr
145+
});
146+
}
147+
}
148+
149+
try_syscall!(unsafe { CloseHandle(handle) }, "close file handle");
150+
151+
Ok(process_ids)
152+
}

src/ci/github-actions/jobs.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,12 @@ pr:
8585
- image: mingw-check-tidy
8686
continue_on_error: true
8787
<<: *job-linux-4c
88-
- image: x86_64-gnu-llvm-17
89-
env:
90-
ENABLE_GCC_CODEGEN: "1"
91-
<<: *job-linux-16c
92-
- image: x86_64-gnu-tools
93-
<<: *job-linux-16c
88+
#- image: x86_64-gnu-llvm-17
89+
# env:
90+
# ENABLE_GCC_CODEGEN: "1"
91+
# <<: *job-linux-16c
92+
#- image: x86_64-gnu-tools
93+
# <<: *job-linux-16c
9494

9595
# Jobs that run when you perform a try build (@bors try)
9696
# These jobs automatically inherit envs.try, to avoid repeating

0 commit comments

Comments
 (0)