From 617ab8989f6db76f3a6cf0e433e9f2fa560066b7 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 12:06:25 +0900 Subject: [PATCH 1/9] feat: publish to crates --- .github/workflows/{ci.yml => cicd.yml} | 59 ++- Cargo.lock | 14 +- Cargo.toml | 1 + lib/common/Cargo.toml | 2 +- lib/common/src/ioctl.rs | 20 +- lib/common/src/jvck/metadata.rs | 22 +- lib/common/src/jvck/store.rs | 147 +++++-- lib/common/src/xts.rs | 48 ++- lib/loader/Cargo.toml | 2 +- lib/loader/src/chainload.rs | 3 +- lib/loader/src/cpu.rs | 5 +- lib/loader/src/debug.rs | 5 +- lib/loader/src/hook/block_io.rs | 3 +- lib/loader/src/hook/mod.rs | 13 +- lib/windrv/Cargo.toml | 2 +- lib/windrv/src/device.rs | 36 +- lib/windrv/src/filter/handover_mount.rs | 18 +- lib/windrv/src/filter/io.rs | 57 +-- lib/windrv/src/filter/manager.rs | 75 +++- lib/windrv/src/filter/mod.rs | 8 +- lib/windrv/src/filter/volume_thread.rs | 86 ++-- lib/windrv/src/handover.rs | 7 +- lib/windrv/src/io.rs | 89 +++- lib/windrv/src/ioctl/dispatch.rs | 546 +++++++++++++++++------- lib/windrv/src/lib.rs | 2 +- lib/windrv/src/ntddk_ex.rs | 10 +- lib/windrv/src/provider.rs | 9 +- lib/windrv/src/registry.rs | 31 +- sample/common/Cargo.toml | 2 +- sample/common/src/config.rs | 10 +- sample/crypto-test/Cargo.toml | 2 +- sample/loader/Cargo.toml | 2 +- sample/windrv/Cargo.toml | 2 +- sample/windrv/src/lib.rs | 84 ++-- sample/windrv/src/provider.rs | 4 +- 35 files changed, 1019 insertions(+), 407 deletions(-) rename .github/workflows/{ci.yml => cicd.yml} (76%) diff --git a/.github/workflows/ci.yml b/.github/workflows/cicd.yml similarity index 76% rename from .github/workflows/ci.yml rename to .github/workflows/cicd.yml index 6269c9f..07b36a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/cicd.yml @@ -6,8 +6,7 @@ name: CI on: - push: - branches: [main] + push: {} pull_request: branches: [main] workflow_dispatch: @@ -83,6 +82,20 @@ jobs: - name: install x86_64-unknown-uefi run: rustup target add x86_64-unknown-uefi + - name: fmt + run: cargo fmt --check + + - name: clippy (vck-common, host) + run: cargo clippy -p vck-common -- -D warnings + + - name: clippy (vck-driver, kernel target) + shell: msys2 {0} + run: cargo clippy -p vck-driver -p vck-sample-driver -p vck-crypto-test-driver -- -D warnings + + - name: clippy (vck-loader, UEFI target) + shell: msys2 {0} + run: cargo clippy -p vck-loader -p vck-sample-loader --target x86_64-unknown-uefi -- -D warnings + - name: cargo test (host) shell: msys2 {0} run: cargo test -p vck-common @@ -169,3 +182,45 @@ jobs: name: test-results-${{ matrix.suite }}-${{ github.run_id }} path: testing/results/${{ matrix.suite }}/ if-no-files-found: warn + + # ── Publish to crates.io on version tag ──────────────────────────────────── + publish: + name: Publish to crates.io + if: startsWith(github.ref, 'refs/tags/v') + needs: + - build-and-test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Verify tag matches Cargo.toml version + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + CARGO_VERSION=$(cargo metadata --no-deps --format-version 1 \ + | jq -r '.packages[] | select(.name=="vck-common") | .version') + if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then + echo "Version mismatch: tag=${TAG_VERSION}, Cargo.toml=${CARGO_VERSION}" + exit 1 + fi + echo "Version verified: ${CARGO_VERSION}" + + - id: auth + uses: rust-lang/crates-io-auth-action@v1 + + - name: Publish vck-common + shell: msys2 {0} + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish -p vck-common + + - name: Publish vck-driver + shell: msys2 {0} + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish -p vck-driver + + - name: Publish vck-loader + shell: msys2 {0} + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish -p vck-loader diff --git a/Cargo.lock b/Cargo.lock index b0144a3..381f3f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1020,7 +1020,7 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vck-common" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes", "cbc", @@ -1042,7 +1042,7 @@ dependencies = [ [[package]] name = "vck-crypto-test-driver" -version = "0.1.0" +version = "0.0.1" dependencies = [ "vck-common", "vck-driver", @@ -1053,7 +1053,7 @@ dependencies = [ [[package]] name = "vck-driver" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes", "irql", @@ -1069,7 +1069,7 @@ dependencies = [ [[package]] name = "vck-loader" -version = "0.1.0" +version = "0.0.1" dependencies = [ "aes", "uefi", @@ -1080,7 +1080,7 @@ dependencies = [ [[package]] name = "vck-sample-common" -version = "0.1.0" +version = "0.0.1" dependencies = [ "serde", "uefi", @@ -1090,7 +1090,7 @@ dependencies = [ [[package]] name = "vck-sample-driver" -version = "0.1.0" +version = "0.0.1" dependencies = [ "spin", "vck-common", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "vck-sample-loader" -version = "0.1.0" +version = "0.0.1" dependencies = [ "log", "uefi", diff --git a/Cargo.toml b/Cargo.toml index a0c5caa..c24b2c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ [workspace.package] edition = "2021" +version = "0.0.1" license = "Apache-2.0" repository = "https://github.com/jc-lab/volumecrypt-kit" diff --git a/lib/common/Cargo.toml b/lib/common/Cargo.toml index e555413..f0f1713 100644 --- a/lib/common/Cargo.toml +++ b/lib/common/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-common" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/lib/common/src/ioctl.rs b/lib/common/src/ioctl.rs index 0e049ac..17dba04 100644 --- a/lib/common/src/ioctl.rs +++ b/lib/common/src/ioctl.rs @@ -75,12 +75,24 @@ mod tests { #[test] fn ctl_code_matches_windows_layout() { // DeviceType occupies bits 31..16. - assert_eq!(ctl_code(0x22, 0, METHOD_BUFFERED, FILE_ANY_ACCESS), 0x0022_0000); + assert_eq!( + ctl_code(0x22, 0, METHOD_BUFFERED, FILE_ANY_ACCESS), + 0x0022_0000 + ); // Access occupies bits 15..14. - assert_eq!(ctl_code(0, 0, METHOD_BUFFERED, FILE_READ_ACCESS), 0x0000_4000); - assert_eq!(ctl_code(0, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS), 0x0000_8000); + assert_eq!( + ctl_code(0, 0, METHOD_BUFFERED, FILE_READ_ACCESS), + 0x0000_4000 + ); + assert_eq!( + ctl_code(0, 0, METHOD_BUFFERED, FILE_WRITE_ACCESS), + 0x0000_8000 + ); // Function occupies bits 13..2. - assert_eq!(ctl_code(0, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS), 0x0000_2000); + assert_eq!( + ctl_code(0, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS), + 0x0000_2000 + ); // Method occupies bits 1..0. assert_eq!(ctl_code(0, 0, METHOD_NEITHER, FILE_ANY_ACCESS), 0x0000_0003); } diff --git a/lib/common/src/jvck/metadata.rs b/lib/common/src/jvck/metadata.rs index 955359e..48b156c 100644 --- a/lib/common/src/jvck/metadata.rs +++ b/lib/common/src/jvck/metadata.rs @@ -434,7 +434,14 @@ mod tests { header.vendor_reserved = [0xC7; VENDOR_RESERVED_SIZE]; let mut block = [0u8; METADATA_BLOCK_SIZE]; header - .encode(&sample_secrets(), 1, VolumeState::Encrypt, &TEST_SALT, vmk, &mut block) + .encode( + &sample_secrets(), + 1, + VolumeState::Encrypt, + &TEST_SALT, + vmk, + &mut block, + ) .unwrap(); let parsed = JvckHeader::parse(&block).unwrap(); assert_eq!(parsed.vendor_reserved, [0xC7; VENDOR_RESERVED_SIZE]); @@ -497,9 +504,7 @@ mod tests { sector_size: 512, header_replica_count: 0, footer_replica_count: 2, - volume_id: [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - ], + volume_id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], vendor_reserved: [0u8; VENDOR_RESERVED_SIZE], }; let secrets = JvckSecrets { @@ -508,7 +513,14 @@ mod tests { }; let mut block = [0u8; METADATA_BLOCK_SIZE]; header - .encode(&secrets, 12345, VolumeState::Encrypt, &TEST_SALT, vmk, &mut block) + .encode( + &secrets, + 12345, + VolumeState::Encrypt, + &TEST_SALT, + vmk, + &mut block, + ) .unwrap(); block } diff --git a/lib/common/src/jvck/store.rs b/lib/common/src/jvck/store.rs index bebb81a..9bc76a1 100644 --- a/lib/common/src/jvck/store.rs +++ b/lib/common/src/jvck/store.rs @@ -727,7 +727,9 @@ impl JvckMetadataReader { } } let (codec, unsealed) = chosen.ok_or_else(|| { - last_err.unwrap_or(VckError::NotFound("no JVCK metadata replica could be unsealed")) + last_err.unwrap_or(VckError::NotFound( + "no JVCK metadata replica could be unsealed", + )) })?; let store = JvckMetadataStore { @@ -831,7 +833,9 @@ mod uefi_io { .map_err(|e| VckError::Io(format!("open BlockIO failed: {e:?}")))?; let media = block_io.media(); if !media.is_media_present() { - return Err(VckError::Io("matched partition has no media present".into())); + return Err(VckError::Io( + "matched partition has no media present".into(), + )); } let sector_size = media.block_size(); let media_id = media.media_id(); @@ -937,9 +941,16 @@ mod tests { ensure_rng(); // 1024 sectors: 2 footer replicas (512) + 512 data sectors. let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); assert_eq!(store.offset_sector(), 0); assert_eq!(store.data_sector_count(), 512); assert_eq!(store.footer_replica_count(), 2); @@ -959,7 +970,9 @@ mod tests { use_footer: 2, metadata_size: MD_SIZE, }; - let store = JvckMetadataStore::create(io, VMK, opts, [3; 32], [4; 32], [7; 16], default_codec()).unwrap(); + let store = + JvckMetadataStore::create(io, VMK, opts, [3; 32], [4; 32], [7; 16], default_codec()) + .unwrap(); assert_eq!(store.offset_sector(), 256); assert_eq!(store.data_sector_count(), 512); } @@ -968,9 +981,16 @@ mod tests { fn store_then_load_offset_roundtrip() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); store .store(&EncryptedOffset { @@ -987,9 +1007,16 @@ mod tests { fn reopen_finds_existing_metadata() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [5; 32], [6; 32], [8; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [5; 32], + [6; 32], + [8; 16], + default_codec(), + ) + .unwrap(); store .store(&EncryptedOffset { sector: 777, @@ -1008,9 +1035,16 @@ mod tests { fn recovery_picks_largest_offset() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); // All replicas at 500. store .store(&EncryptedOffset { @@ -1042,14 +1076,24 @@ mod tests { fn state_persists_across_reopen() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); // Fresh volumes default to Encrypt. assert_eq!(store.load_state().unwrap(), VolumeState::Encrypt); store - .store(&EncryptedOffset { sector: 100, total_sectors: 512 }) + .store(&EncryptedOffset { + sector: 100, + total_sectors: 512, + }) .unwrap(); store.store_state(VolumeState::Decrypt).unwrap(); @@ -1065,9 +1109,16 @@ mod tests { ensure_rng(); // footer-only: 2 replicas of 256 sectors -> 255 vendor-data sectors each. let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); assert_eq!(store.replica_count(), 2); assert_eq!(store.vendor_data_sector_count(), 255); @@ -1091,9 +1142,16 @@ mod tests { fn write_vendor_data_all_mirrors_every_replica() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); assert_eq!(store.replica_count(), 2); let data = alloc::vec![0x5Au8; 1024]; // 2 sectors @@ -1115,10 +1173,20 @@ mod tests { fn set_vendor_reserved_persists_to_all_replicas() { ensure_rng(); let io = MemVolume::new(512, 1024); - let mut store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); - assert_eq!(store.vendor_reserved(), &[0u8; metadata::VENDOR_RESERVED_SIZE]); + let mut store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); + assert_eq!( + store.vendor_reserved(), + &[0u8; metadata::VENDOR_RESERVED_SIZE] + ); let vr = [0xABu8; metadata::VENDOR_RESERVED_SIZE]; store.set_vendor_reserved(&vr).unwrap(); @@ -1138,9 +1206,16 @@ mod tests { fn reader_replica_ctx_exposes_block_and_vendor_data() { ensure_rng(); let io = MemVolume::new(512, 1024); - let store = - JvckMetadataStore::create(io, VMK, footer_only_options(), [1; 32], [2; 32], [9; 16], default_codec()) - .unwrap(); + let store = JvckMetadataStore::create( + io, + VMK, + footer_only_options(), + [1; 32], + [2; 32], + [9; 16], + default_codec(), + ) + .unwrap(); let marker = alloc::vec![0xE7u8; 512]; store.write_vendor_data_all(0, &marker).unwrap(); @@ -1152,7 +1227,10 @@ mod tests { let ctx = reader.replica_ctx(1).unwrap(); assert_eq!(ctx.replica_index(), 1); assert_eq!(&ctx.block()[..4], b"JVCK"); - assert_eq!(ctx.encrypted_metadata().len(), metadata::ENCRYPTED_METADATA_SIZE); + assert_eq!( + ctx.encrypted_metadata().len(), + metadata::ENCRYPTED_METADATA_SIZE + ); let mut vd = alloc::vec![0u8; 512]; ctx.read_vendor_data(0, &mut vd).unwrap(); assert_eq!(vd, marker); @@ -1189,7 +1267,8 @@ mod tests { // 2 footer replicas (64 sectors) + 64 data sectors. let io = MemVolume::new(sector_size, 128); let store = - JvckMetadataStore::create(io, VMK, opts, [1; 32], [2; 32], [9; 16], default_codec()).unwrap(); + JvckMetadataStore::create(io, VMK, opts, [1; 32], [2; 32], [9; 16], default_codec()) + .unwrap(); assert_eq!(store.data_sector_count(), 128 - 2 * expected_rs); assert_eq!(store.sector_size(), sector_size); diff --git a/lib/common/src/xts.rs b/lib/common/src/xts.rs index a6a8df4..f558139 100644 --- a/lib/common/src/xts.rs +++ b/lib/common/src/xts.rs @@ -101,10 +101,10 @@ fn gf128_mul(t: Block) -> Block { impl XtsVolumeCipher { pub fn new(key1: &[u8; 32], key2: &[u8; 32]) -> VckResult { - let cipher_1 = Aes256::new_from_slice(key1) - .map_err(|_| VckError::CryptoFailed("invalid XTS key1"))?; - let cipher_2 = Aes256::new_from_slice(key2) - .map_err(|_| VckError::CryptoFailed("invalid XTS key2"))?; + let cipher_1 = + Aes256::new_from_slice(key1).map_err(|_| VckError::CryptoFailed("invalid XTS key1"))?; + let cipher_2 = + Aes256::new_from_slice(key2).map_err(|_| VckError::CryptoFailed("invalid XTS key2"))?; Ok(Self { cipher_1, cipher_2 }) } @@ -149,18 +149,24 @@ impl XtsVolumeCipher { while off + BATCH <= n { let mut ts = [Block::default(); BATCH]; ts[0] = tw; - for i in 1..BATCH { ts[i] = gf128_mul(ts[i - 1]); } + for i in 1..BATCH { + ts[i] = gf128_mul(ts[i - 1]); + } tw = gf128_mul(ts[BATCH - 1]); let mut batch = [Block::default(); BATCH]; for i in 0..BATCH { let src = §or[(off + i) * 16..(off + i + 1) * 16]; - for j in 0..16 { batch[i][j] = src[j] ^ ts[i][j]; } + for j in 0..16 { + batch[i][j] = src[j] ^ ts[i][j]; + } } self.cipher_1.encrypt_blocks(&mut batch); for i in 0..BATCH { let dst = &mut sector[(off + i) * 16..(off + i + 1) * 16]; - for j in 0..16 { dst[j] = batch[i][j] ^ ts[i][j]; } + for j in 0..16 { + dst[j] = batch[i][j] ^ ts[i][j]; + } } off += BATCH; } @@ -168,11 +174,15 @@ impl XtsVolumeCipher { // Scalar tail for sectors whose block count is not a multiple of BATCH. while off < n { let block = &mut sector[off * 16..(off + 1) * 16]; - for j in 0..16 { block[j] ^= tw[j]; } + for j in 0..16 { + block[j] ^= tw[j]; + } let mut ga = *Block::from_slice(block); self.cipher_1.encrypt_block(&mut ga); block.copy_from_slice(&ga); - for j in 0..16 { block[j] ^= tw[j]; } + for j in 0..16 { + block[j] ^= tw[j]; + } tw = gf128_mul(tw); off += 1; } @@ -190,29 +200,39 @@ impl XtsVolumeCipher { while off + BATCH <= n { let mut ts = [Block::default(); BATCH]; ts[0] = tw; - for i in 1..BATCH { ts[i] = gf128_mul(ts[i - 1]); } + for i in 1..BATCH { + ts[i] = gf128_mul(ts[i - 1]); + } tw = gf128_mul(ts[BATCH - 1]); let mut batch = [Block::default(); BATCH]; for i in 0..BATCH { let src = §or[(off + i) * 16..(off + i + 1) * 16]; - for j in 0..16 { batch[i][j] = src[j] ^ ts[i][j]; } + for j in 0..16 { + batch[i][j] = src[j] ^ ts[i][j]; + } } self.cipher_1.decrypt_blocks(&mut batch); for i in 0..BATCH { let dst = &mut sector[(off + i) * 16..(off + i + 1) * 16]; - for j in 0..16 { dst[j] = batch[i][j] ^ ts[i][j]; } + for j in 0..16 { + dst[j] = batch[i][j] ^ ts[i][j]; + } } off += BATCH; } while off < n { let block = &mut sector[off * 16..(off + 1) * 16]; - for j in 0..16 { block[j] ^= tw[j]; } + for j in 0..16 { + block[j] ^= tw[j]; + } let mut ga = *Block::from_slice(block); self.cipher_1.decrypt_block(&mut ga); block.copy_from_slice(&ga); - for j in 0..16 { block[j] ^= tw[j]; } + for j in 0..16 { + block[j] ^= tw[j]; + } tw = gf128_mul(tw); off += 1; } diff --git a/lib/loader/Cargo.toml b/lib/loader/Cargo.toml index 76e5505..79e18ee 100644 --- a/lib/loader/Cargo.toml +++ b/lib/loader/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-loader" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/lib/loader/src/chainload.rs b/lib/loader/src/chainload.rs index fa617da..abf6e0f 100644 --- a/lib/loader/src/chainload.rs +++ b/lib/loader/src/chainload.rs @@ -36,6 +36,7 @@ pub fn chainload_next(next_loader: &DevicePath) -> VckResult<()> { ) .map_err(|e| VckError::Io(format!("LoadImage(next loader) failed: {e:?}")))?; - start_image(image).map_err(|e| VckError::Io(format!("StartImage(next loader) failed: {e:?}")))?; + start_image(image) + .map_err(|e| VckError::Io(format!("StartImage(next loader) failed: {e:?}")))?; Ok(()) } diff --git a/lib/loader/src/cpu.rs b/lib/loader/src/cpu.rs index 5a3ef3b..1669aa0 100644 --- a/lib/loader/src/cpu.rs +++ b/lib/loader/src/cpu.rs @@ -74,7 +74,10 @@ fn bit(v: u64, b: u64) -> u32 { /// supported) set them to the values required for AES-NI execution. pub fn report_and_enable_xmm() { let aes = has_aes_ni(); - vck_log!("cpu: AES-NI {}", if aes { "supported" } else { "not supported" }); + vck_log!( + "cpu: AES-NI {}", + if aes { "supported" } else { "not supported" } + ); let cr0 = read_cr0(); let cr4 = read_cr4(); diff --git a/lib/loader/src/debug.rs b/lib/loader/src/debug.rs index db293ba..ff1a5a8 100644 --- a/lib/loader/src/debug.rs +++ b/lib/loader/src/debug.rs @@ -65,10 +65,7 @@ fn efi_time_to_unix(t: &uefi::runtime::Time) -> (u64, u32) { let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // day-of-era [0, 146096] let days = era * 146_097 + doe - EPOCH_DAYS; - let secs = days * 86_400 - + t.hour() as u64 * 3_600 - + t.minute() as u64 * 60 - + t.second() as u64; + let secs = days * 86_400 + t.hour() as u64 * 3_600 + t.minute() as u64 * 60 + t.second() as u64; let millis = t.nanosecond() / 1_000_000; (secs, millis) } diff --git a/lib/loader/src/hook/block_io.rs b/lib/loader/src/hook/block_io.rs index d439fb2..1a6ac18 100644 --- a/lib/loader/src/hook/block_io.rs +++ b/lib/loader/src/hook/block_io.rs @@ -111,7 +111,8 @@ impl BlockIoHook { (*self.protocol).read_blocks = entry.original; let entries = &mut *HOOK_TABLE.entries.get(); for slot in entries.iter_mut() { - if matches!(slot, Some(e) if e.protocol == self.protocol as *const BlockIoProtocol) { + if matches!(slot, Some(e) if e.protocol == self.protocol as *const BlockIoProtocol) + { *slot = None; } } diff --git a/lib/loader/src/hook/mod.rs b/lib/loader/src/hook/mod.rs index a57a76e..adfcd3f 100644 --- a/lib/loader/src/hook/mod.rs +++ b/lib/loader/src/hook/mod.rs @@ -86,7 +86,9 @@ impl BlockIoHookEngine { let matched = match open_protocol_exclusive::(handle) { Ok(pinfo) => pinfo .gpt_partition_entry() - .map(|gpt| guid_from_windows_bytes(gpt.unique_partition_guid.to_bytes()) == target) + .map(|gpt| { + guid_from_windows_bytes(gpt.unique_partition_guid.to_bytes()) == target + }) .unwrap_or(false), Err(_) => false, }; @@ -98,9 +100,12 @@ impl BlockIoHookEngine { // `read_blocks` field. `BlockIO` is `#[repr(transparent)]` over it. let mut scoped = open_protocol_exclusive::(handle) .map_err(|e| VckError::Io(format!("open BlockIO for hook failed: {e:?}")))?; - let proto = scoped.get_mut().ok_or(VckError::Io( - alloc::string::String::from("BlockIO interface is null"), - ))? as *mut BlockIO as *mut uefi_raw::protocol::block::BlockIoProtocol; + let proto = scoped + .get_mut() + .ok_or(VckError::Io(alloc::string::String::from( + "BlockIO interface is null", + )))? as *mut BlockIO + as *mut uefi_raw::protocol::block::BlockIoProtocol; self.block_io = Some(block_io::BlockIoHook::install(proto, engine_ptr)?); // Keep the protocol open (and the patch live) past this scope. diff --git a/lib/windrv/Cargo.toml b/lib/windrv/Cargo.toml index 05e0f6b..a0bac3b 100644 --- a/lib/windrv/Cargo.toml +++ b/lib/windrv/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-driver" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true diff --git a/lib/windrv/src/device.rs b/lib/windrv/src/device.rs index 7f53c83..e5af91d 100644 --- a/lib/windrv/src/device.rs +++ b/lib/windrv/src/device.rs @@ -13,11 +13,11 @@ use core::ptr::null_mut; use vck_common::VckResult; use wdk_sys::{ ntddk::{ - IoCreateSymbolicLink, IoDeleteDevice, IoDeleteSymbolicLink, - IoRegisterShutdownNotification, IoUnregisterShutdownNotification, + IoCreateSymbolicLink, IoDeleteDevice, IoDeleteSymbolicLink, IoRegisterShutdownNotification, + IoUnregisterShutdownNotification, }, BOOLEAN, DO_BUFFERED_IO, DO_DEVICE_INITIALIZING, DRIVER_OBJECT, FILE_DEVICE_SECURE_OPEN, - FILE_DEVICE_UNKNOWN, GUID, NTSTATUS, PCUNICODE_STRING, PDEVICE_OBJECT, UNICODE_STRING, ULONG, + FILE_DEVICE_UNKNOWN, GUID, NTSTATUS, PCUNICODE_STRING, PDEVICE_OBJECT, ULONG, UNICODE_STRING, }; use crate::nt::{ntstatus_to_result, UnicodeString}; @@ -119,19 +119,22 @@ impl ControlDevice { let sddl = UnicodeString::from_str(security.sddl); let mut device_object = null_mut(); - ntstatus_to_result(unsafe { - IoCreateDeviceSecure( - driver_object, - DEVICE_EXTENSION_SIZE, - device_name.as_ptr(), - FILE_DEVICE_UNKNOWN, - FILE_DEVICE_SECURE_OPEN, - BOOLEAN::default(), - sddl.as_ptr() as PCUNICODE_STRING, - &security.class_guid as *const GUID, - &mut device_object, - ) - }, "IoCreateDeviceSecure failed")?; + ntstatus_to_result( + unsafe { + IoCreateDeviceSecure( + driver_object, + DEVICE_EXTENSION_SIZE, + device_name.as_ptr(), + FILE_DEVICE_UNKNOWN, + FILE_DEVICE_SECURE_OPEN, + BOOLEAN::default(), + sddl.as_ptr() as PCUNICODE_STRING, + &security.class_guid as *const GUID, + &mut device_object, + ) + }, + "IoCreateDeviceSecure failed", + )?; unsafe { (*device_object).Flags |= DO_BUFFERED_IO; @@ -184,4 +187,3 @@ impl ControlDevice { Ok(()) } } - diff --git a/lib/windrv/src/filter/handover_mount.rs b/lib/windrv/src/filter/handover_mount.rs index be9670e..e2781fc 100644 --- a/lib/windrv/src/filter/handover_mount.rs +++ b/lib/windrv/src/filter/handover_mount.rs @@ -35,8 +35,7 @@ use wdk_sys::{ ExAllocatePool2, ExFreePool, IoAllocateWorkItem, IoFreeWorkItem, IoQueueWorkItem, KeGetCurrentIrql, KeInitializeEvent, KeSetEvent, KeWaitForSingleObject, }, - KEVENT, NTSTATUS, PDEVICE_OBJECT, PIO_WORKITEM, PIRP, - SL_PENDING_RETURNED, + KEVENT, NTSTATUS, PDEVICE_OBJECT, PIO_WORKITEM, PIRP, SL_PENDING_RETURNED, _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, _MODE::KernelMode, @@ -56,7 +55,12 @@ const POOL_FLAG_NON_PAGED: u64 = 0x0000_0000_0000_0040; const VCK_POOL_TAG: u32 = u32::from_le_bytes(*b"VCKM"); unsafe fn current_sl(irp: PIRP) -> wdk_sys::PIO_STACK_LOCATION { - (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation + (*irp) + .Tail + .Overlay + .__bindgen_anon_2 + .__bindgen_anon_1 + .CurrentStackLocation } // --------------------------------------------------------------------------- @@ -179,7 +183,8 @@ pub unsafe fn try_mount_handover_volume(filter_do: PDEVICE_OBJECT) { if guid != handover.partition_guid { crate::vck_log!( "handover_mount: partition {} != target {}, skipping", - guid, handover.partition_guid + guid, + handover.partition_guid ); return; } @@ -260,7 +265,10 @@ pub unsafe fn try_mount_handover_volume(filter_do: PDEVICE_OBJECT) { crate::filter::filter_bind_volume(filter_do, volume.clone()); crate::vck_log!( "handover_mount: OS volume bound path={} offset_sector={} data_sectors={} boundary={}", - volume_path, offset_sector, data_sectors, initial_boundary + volume_path, + offset_sector, + data_sectors, + initial_boundary ); unsafe { crate::filter::volume_thread::wake_for(&volume) }; } diff --git a/lib/windrv/src/filter/io.rs b/lib/windrv/src/filter/io.rs index 649a6d5..1f1f434 100644 --- a/lib/windrv/src/filter/io.rs +++ b/lib/windrv/src/filter/io.rs @@ -22,38 +22,39 @@ use core::ffi::c_void; use wdk_sys::{ ntddk::{ExFreePool, IofCallDriver, IofCompleteRequest}, CCHAR, IO_NO_INCREMENT, IRP_MJ_DEVICE_CONTROL, IRP_MJ_PNP, IRP_MJ_READ, IRP_MJ_WRITE, - IRP_MN_START_DEVICE, NTSTATUS, - PDEVICE_OBJECT, PIO_STACK_LOCATION, PIRP, - SL_INVOKE_ON_CANCEL, SL_INVOKE_ON_ERROR, SL_INVOKE_ON_SUCCESS, SL_PENDING_RETURNED, + IRP_MN_START_DEVICE, NTSTATUS, PDEVICE_OBJECT, PIO_STACK_LOCATION, PIRP, SL_INVOKE_ON_CANCEL, + SL_INVOKE_ON_ERROR, SL_INVOKE_ON_SUCCESS, SL_PENDING_RETURNED, }; use core::sync::atomic::Ordering; use crate::{ - device::DeviceExtension, - filter::pass_through, - nt::nt_success, - registry::AttachedVolume, + device::DeviceExtension, filter::pass_through, nt::nt_success, registry::AttachedVolume, }; const STATUS_CONTINUE_COMPLETION: NTSTATUS = 0; const STATUS_ACCESS_DENIED: NTSTATUS = 0xC000_0022u32 as i32; -const IOCTL_DISK_GET_LENGTH_INFO: u32 = 0x0007_405C; -const IOCTL_DISK_GET_PARTITION_INFO: u32 = 0x0007_4004; +const IOCTL_DISK_GET_LENGTH_INFO: u32 = 0x0007_405C; +const IOCTL_DISK_GET_PARTITION_INFO: u32 = 0x0007_4004; const IOCTL_DISK_GET_PARTITION_INFO_EX: u32 = 0x0007_0048; fn size_field_offset(code: u32) -> Option { match code { - IOCTL_DISK_GET_LENGTH_INFO => Some(0), - IOCTL_DISK_GET_PARTITION_INFO => Some(8), + IOCTL_DISK_GET_LENGTH_INFO => Some(0), + IOCTL_DISK_GET_PARTITION_INFO => Some(8), IOCTL_DISK_GET_PARTITION_INFO_EX => Some(16), _ => None, } } unsafe fn current_sl(irp: PIRP) -> PIO_STACK_LOCATION { - (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation + (*irp) + .Tail + .Overlay + .__bindgen_anon_2 + .__bindgen_anon_1 + .CurrentStackLocation } unsafe fn next_sl(irp: PIRP) -> PIO_STACK_LOCATION { @@ -84,9 +85,11 @@ unsafe fn intercept_size_ioctl( if ctx.is_null() { return pass_through(lower, irp); } - (*ctx).data_bytes = volume.data_sectors().saturating_mul(volume.sector_size as u64); + (*ctx).data_bytes = volume + .data_sectors() + .saturating_mul(volume.sector_size as u64); - let cur = current_sl(irp); + let cur = current_sl(irp); let next = next_sl(irp); core::ptr::copy_nonoverlapping(cur, next, 1); (*next).CompletionRoutine = Some(size_ioctl_completion); @@ -102,7 +105,7 @@ unsafe extern "C" fn size_ioctl_completion( ) -> NTSTATUS { let ctx = &*(context as *const SizeCtx); let stack = current_sl(irp); - let code = (*stack).Parameters.DeviceIoControl.IoControlCode; + let code = (*stack).Parameters.DeviceIoControl.IoControlCode; let status = (*irp).IoStatus.__bindgen_anon_1.Status; if nt_success(status) { @@ -117,7 +120,8 @@ unsafe extern "C" fn size_ioctl_completion( ); crate::vck_log!( "filter: size ioctl 0x{:08x} reported as {} bytes", - code, ctx.data_bytes + code, + ctx.data_bytes ); } } @@ -149,11 +153,10 @@ unsafe fn handle_filter_pnp( } // Equivalent to IoCopyCurrentIrpStackLocationToNext: the completion routine // needs our stack location preserved (we do NOT skip it here). - let cur = current_sl(irp); + let cur = current_sl(irp); let next = next_sl(irp); core::ptr::copy_nonoverlapping(cur, next, 1); - (*next).CompletionRoutine = - Some(crate::filter::handover_mount::on_start_device_completed); + (*next).CompletionRoutine = Some(crate::filter::handover_mount::on_start_device_completed); (*next).Context = filter_do.cast(); (*next).Control = (SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL) as u8; IofCallDriver(lower, irp) @@ -170,20 +173,26 @@ unsafe fn block_irp(irp: PIRP, status: NTSTATUS) { } fn is_metadata_sector(volume: &AttachedVolume, byte_offset: u64, sector_size: u64) -> bool { - if sector_size == 0 { return false; } + if sector_size == 0 { + return false; + } let lba = byte_offset / sector_size; data_relative(volume, lba).is_none() } fn data_relative(volume: &AttachedVolume, abs_lba: u64) -> Option { let offset = volume.offset_sector(); - let total = volume.data_sectors(); + let total = volume.data_sectors(); abs_lba.checked_sub(offset).filter(|rel| *rel < total) } unsafe fn shift_offset(volume: &AttachedVolume, stack: PIO_STACK_LOCATION) { - let shift = volume.offset_sector().saturating_mul(volume.sector_size as u64); - if shift == 0 { return; } + let shift = volume + .offset_sector() + .saturating_mul(volume.sector_size as u64); + if shift == 0 { + return; + } let byte_offset = &mut (*stack).Parameters.Read.ByteOffset; byte_offset.QuadPart += shift as i64; } @@ -198,7 +207,7 @@ unsafe fn shift_offset(volume: &AttachedVolume, stack: PIO_STACK_LOCATION) { /// `filter_do` must be one of this driver's filter device objects and `irp` /// a valid IRP it currently owns. pub unsafe fn handle_filter_irp(filter_do: PDEVICE_OBJECT, irp: PIRP) -> NTSTATUS { - let ext = DeviceExtension::of(filter_do); + let ext = DeviceExtension::of(filter_do); let lower = ext.lower_device; let stack = current_sl(irp); diff --git a/lib/windrv/src/filter/manager.rs b/lib/windrv/src/filter/manager.rs index f411484..d540484 100644 --- a/lib/windrv/src/filter/manager.rs +++ b/lib/windrv/src/filter/manager.rs @@ -87,7 +87,9 @@ pub fn attach_filter( }; if !nt_success(status) { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoGetDeviceObjectPointer(target) failed".into())); + return Err(VckError::Io( + "IoGetDeviceObjectPointer(target) failed".into(), + )); } let mut lower: PDEVICE_OBJECT = null_mut(); @@ -95,7 +97,9 @@ pub fn attach_filter( unsafe { ObfDereferenceObject(file_obj.cast()) }; if !nt_success(status) || lower.is_null() { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoAttachDeviceToDeviceStackSafe failed".into())); + return Err(VckError::Io( + "IoAttachDeviceToDeviceStackSafe failed".into(), + )); } unsafe { @@ -115,7 +119,8 @@ pub fn attach_filter( crate::vck_log!( "filter: attached filter={:p} lower={:p} (NOTE: may be above FSD if FSD already mounted)", - filter_do, lower + filter_do, + lower ); Ok((filter_do, lower)) } @@ -160,7 +165,9 @@ pub fn attach_filter_unbound( }; if !nt_success(status) { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoGetDeviceObjectPointer(unbound) failed".into())); + return Err(VckError::Io( + "IoGetDeviceObjectPointer(unbound) failed".into(), + )); } let mut lower: PDEVICE_OBJECT = null_mut(); @@ -168,7 +175,9 @@ pub fn attach_filter_unbound( unsafe { ObfDereferenceObject(file_obj.cast()) }; if !nt_success(status) || lower.is_null() { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoAttachDeviceToDeviceStackSafe(unbound) failed".into())); + return Err(VckError::Io( + "IoAttachDeviceToDeviceStackSafe(unbound) failed".into(), + )); } unsafe { @@ -215,7 +224,9 @@ pub fn attach_filter_to_raw_device( let status = unsafe { IoAttachDeviceToDeviceStackSafe(filter_do, target_do, &mut lower) }; if !nt_success(status) || lower.is_null() { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoAttachDeviceToDeviceStackSafe(raw) failed".into())); + return Err(VckError::Io( + "IoAttachDeviceToDeviceStackSafe(raw) failed".into(), + )); } unsafe { @@ -292,7 +303,9 @@ pub fn attach_filter_to_device( let status = unsafe { IoAttachDeviceToDeviceStackSafe(filter_do, target_do, &mut lower) }; if !nt_success(status) || lower.is_null() { unsafe { IoDeleteDevice(filter_do) }; - return Err(VckError::Io("IoAttachDeviceToDeviceStackSafe(device) failed".into())); + return Err(VckError::Io( + "IoAttachDeviceToDeviceStackSafe(device) failed".into(), + )); } unsafe { @@ -414,7 +427,10 @@ where // device object that was the PDO when AddDevice was called. let base_dev = unsafe { IoGetDeviceAttachmentBaseRef(vol_do) }; crate::vck_log!( - "find_filter: vol_do={:p} base_dev={:p} nt_path={}", vol_do, base_dev, nt_path + "find_filter: vol_do={:p} base_dev={:p} nt_path={}", + vol_do, + base_dev, + nt_path ); // Primary: name-based lookup using the actual device object name. // vol_do might be e.g. \Device\HarddiskVolume5 even if nt_path=\??\D:. @@ -426,16 +442,30 @@ where let st = ObQueryNameString(vol_do.cast(), buf.as_mut_ptr().cast(), 256, &mut ret_len); if st >= 0 { let name_len = u16::from_le_bytes([buf[0], buf[1]]) as usize / 2; - let name_ptr = usize::from_le_bytes(buf[8..16].try_into().unwrap_or([0;8])); + let name_ptr = usize::from_le_bytes(buf[8..16].try_into().unwrap_or([0; 8])); if name_len > 0 && name_ptr != 0 { let chars = core::slice::from_raw_parts(name_ptr as *const u16, name_len.min(64)); let mut s = alloc::string::String::new(); - for &c in chars { if c >= 0x20 && c < 0x7F { s.push(c as u8 as char); } else { s.push('?'); } } + for &c in chars { + if c >= 0x20 && c < 0x7F { + s.push(c as u8 as char); + } else { + s.push('?'); + } + } s - } else { alloc::string::String::new() } - } else { alloc::string::String::new() } + } else { + alloc::string::String::new() + } + } else { + alloc::string::String::new() + } }; - crate::vck_log!("find_filter: vol_do_name='{}' nt_path='{}'", dev_name, nt_path); + crate::vck_log!( + "find_filter: vol_do_name='{}' nt_path='{}'", + dev_name, + nt_path + ); if !dev_name.is_empty() { if let Some(result) = name_lookup_fn(&dev_name) { crate::vck_log!("find_filter: found via vol_do_name={}", dev_name); @@ -531,15 +561,24 @@ pub fn find_our_filter_in_stack(nt_path: &str) -> Option<(PDEVICE_OBJECT, PDEVIC let mut depth = 0u32; loop { let ext = (*current).DeviceExtension as *const DeviceExtension; - let kind = if !ext.is_null() { (*ext).kind } else { 0xFFFF_FFFF }; + let kind = if !ext.is_null() { + (*ext).kind + } else { + 0xFFFF_FFFF + }; crate::vck_log!( - "find_filter[{}]: do={:p} ext_kind=0x{:08x}", depth, current, kind + "find_filter[{}]: do={:p} ext_kind=0x{:08x}", + depth, + current, + kind ); if !ext.is_null() && (*ext).kind == DEVICE_KIND_FILTER { let lower = (*ext).lower_device; ObfDereferenceObject(current.cast()); crate::vck_log!( - "find_filter: found filter_do={:p} lower={:p}", current, lower + "find_filter: found filter_do={:p} lower={:p}", + current, + lower ); return Some((current, lower)); } @@ -552,7 +591,9 @@ pub fn find_our_filter_in_stack(nt_path: &str) -> Option<(PDEVICE_OBJECT, PDEVIC } current = next; depth += 1; - if depth > 12 { break; } + if depth > 12 { + break; + } } } None diff --git a/lib/windrv/src/filter/mod.rs b/lib/windrv/src/filter/mod.rs index d8e815f..5741fa5 100644 --- a/lib/windrv/src/filter/mod.rs +++ b/lib/windrv/src/filter/mod.rs @@ -15,6 +15,8 @@ pub mod volume_thread; pub use context::FilterContext; pub use io::handle_filter_irp; pub use irp::pass_through; -pub use manager::{attach_filter, attach_filter_to_device, attach_filter_to_raw_device, - attach_filter_unbound, detach_filter, filter_bind_volume, filter_rebind_volume, - find_our_filter_in_stack, find_filter_for_volume, unbind_filter}; +pub use manager::{ + attach_filter, attach_filter_to_device, attach_filter_to_raw_device, attach_filter_unbound, + detach_filter, filter_bind_volume, filter_rebind_volume, find_filter_for_volume, + find_our_filter_in_stack, unbind_filter, +}; diff --git a/lib/windrv/src/filter/volume_thread.rs b/lib/windrv/src/filter/volume_thread.rs index 841467a..dd50218 100644 --- a/lib/windrv/src/filter/volume_thread.rs +++ b/lib/windrv/src/filter/volume_thread.rs @@ -33,9 +33,8 @@ use wdk_sys::{ IofCompleteRequest, KeInitializeEvent, KeSetEvent, KeWaitForSingleObject, ObReferenceObjectByHandle, ObfDereferenceObject, PsCreateSystemThread, ZwClose, }, - CCHAR, HANDLE, IO_NO_INCREMENT, KEVENT, LARGE_INTEGER, NTSTATUS, - PDEVICE_OBJECT, PIO_STACK_LOCATION, PIRP, - SL_PENDING_RETURNED, + CCHAR, HANDLE, IO_NO_INCREMENT, KEVENT, LARGE_INTEGER, NTSTATUS, PDEVICE_OBJECT, + PIO_STACK_LOCATION, PIRP, SL_PENDING_RETURNED, _EVENT_TYPE::SynchronizationEvent, _KWAIT_REASON::Executive, _MODE::KernelMode, @@ -72,7 +71,7 @@ const IDLE_TIMEOUT_100NS: i64 = -5_000_000; // --------------------------------------------------------------------------- struct IrpEntry { - irp: PIRP, + irp: PIRP, is_write: bool, } @@ -81,12 +80,12 @@ struct IrpEntry { // --------------------------------------------------------------------------- pub struct VolumeThread { - queue: Mutex>, + queue: Mutex>, /// The volume currently bound to the owning filter (swapped on rebind). - current: Mutex>, - wake: KEVENT, + current: Mutex>, + wake: KEVENT, shutdown: AtomicBool, - thread: *mut c_void, // PETHREAD (set after PsCreateSystemThread) + thread: *mut c_void, // PETHREAD (set after PsCreateSystemThread) } unsafe impl Send for VolumeThread {} @@ -97,19 +96,24 @@ impl VolumeThread { /// pointer is stored in the filter's DeviceExtension. pub unsafe fn start(volume: Arc) -> Box { let mut vt = Box::new(VolumeThread { - queue: Mutex::new(VecDeque::new()), - current: Mutex::new(volume), - wake: core::mem::zeroed(), + queue: Mutex::new(VecDeque::new()), + current: Mutex::new(volume), + wake: core::mem::zeroed(), shutdown: AtomicBool::new(false), - thread: null_mut(), + thread: null_mut(), }); KeInitializeEvent(&mut vt.wake, SynchronizationEvent, 0); let self_ptr: *mut VolumeThread = &mut *vt; let mut handle: HANDLE = null_mut(); let st = PsCreateSystemThread( - &mut handle, THREAD_ALL_ACCESS, null_mut(), null_mut(), null_mut(), - Some(thread_main), self_ptr.cast::(), + &mut handle, + THREAD_ALL_ACCESS, + null_mut(), + null_mut(), + null_mut(), + Some(thread_main), + self_ptr.cast::(), ); if !nt_success(st) { crate::vck_log!("volume_thread: create failed 0x{:08x}", st); @@ -117,7 +121,12 @@ impl VolumeThread { } let mut obj: *mut c_void = null_mut(); let _ = ObReferenceObjectByHandle( - handle, SYNCHRONIZE, null_mut(), KernelMode as i8, &mut obj, null_mut(), + handle, + SYNCHRONIZE, + null_mut(), + KernelMode as i8, + &mut obj, + null_mut(), ); let _ = ZwClose(handle); vt.thread = obj; @@ -196,7 +205,12 @@ pub unsafe fn enqueue(vt: *mut VolumeThread, irp: PIRP, is_write: bool) -> NTSTA } // Mark pending before returning STATUS_PENDING. Cancellation is handled at // dequeue (see `claim_irp`), so we install no cancel routine here. - let sl = (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation; + let sl = (*irp) + .Tail + .Overlay + .__bindgen_anon_2 + .__bindgen_anon_1 + .CurrentStackLocation; (*sl).Control |= SL_PENDING_RETURNED as u8; q.push_back(IrpEntry { irp, is_write }); drop(q); @@ -220,7 +234,9 @@ unsafe fn claim_cancelled(irp: PIRP) -> bool { /// engine state) so the sweep resumes promptly. pub unsafe fn wake_for(volume: &AttachedVolume) { let filter_do = volume.filter_device.load(Ordering::Acquire); - if filter_do.is_null() { return; } + if filter_do.is_null() { + return; + } let ext = (*filter_do).DeviceExtension as *mut DeviceExtension; if !(*ext).vthread.is_null() { (*(*ext).vthread).signal(); @@ -260,17 +276,22 @@ unsafe extern "C" fn thread_main(context: *mut c_void) { // (2) One sweep batch (no-op unless engine is Encrypting/Decrypting). match vol.sweep_step(BATCH_SECTORS) { - Ok(true) => did = true, // more sweep work remains + Ok(true) => did = true, // more sweep work remains Ok(false) => {} - Err(e) => crate::vck_log!("volume_thread: sweep err: {}", e), + Err(e) => crate::vck_log!("volume_thread: sweep err: {}", e), } // (3) Idle → wait for a wake (new IRP / rebind / start_encrypt) or timeout. if !did { - let mut timeout = LARGE_INTEGER { QuadPart: IDLE_TIMEOUT_100NS }; + let mut timeout = LARGE_INTEGER { + QuadPart: IDLE_TIMEOUT_100NS, + }; KeWaitForSingleObject( &vt.wake as *const KEVENT as *mut c_void, - Executive, KernelMode as i8, 0, &mut timeout, + Executive, + KernelMode as i8, + 0, + &mut timeout, ); } } @@ -299,12 +320,19 @@ unsafe fn process_irp(vol: &AttachedVolume, ep: IrpEntry) { // --------------------------------------------------------------------------- fn current_sl(irp: PIRP) -> PIO_STACK_LOCATION { - unsafe { (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation } + unsafe { + (*irp) + .Tail + .Overlay + .__bindgen_anon_2 + .__bindgen_anon_1 + .CurrentStackLocation + } } fn data_relative(volume: &AttachedVolume, abs_lba: u64) -> Option { let offset = volume.offset_sector(); - let total = volume.data_sectors(); + let total = volume.data_sectors(); abs_lba.checked_sub(offset).filter(|rel| *rel < total) } @@ -332,16 +360,16 @@ unsafe fn complete_irp(irp: PIRP, status: NTSTATUS, information: usize) { // an already-locked MDL faults (PAGE_FAULT_IN_NONPAGED_AREA). unsafe fn process_read(irp: PIRP, volume: &AttachedVolume) { - let stack = current_sl(irp); + let stack = current_sl(irp); let byte_offset = (*stack).Parameters.Read.ByteOffset.QuadPart as u64; - let length = (*stack).Parameters.Read.Length as usize; + let length = (*stack).Parameters.Read.Length as usize; let sector_size = volume.sector_size as usize; if sector_size == 0 || length == 0 { complete_irp(irp, STATUS_SUCCESS, 0); return; } - let first_lba = byte_offset / sector_size as u64; + let first_lba = byte_offset / sector_size as u64; let sector_count = length / sector_size; if sector_count == 0 { complete_irp(irp, STATUS_SUCCESS, 0); @@ -388,16 +416,16 @@ unsafe fn process_read(irp: PIRP, volume: &AttachedVolume) { // --------------------------------------------------------------------------- unsafe fn process_write(irp: PIRP, volume: &AttachedVolume) { - let stack = current_sl(irp); + let stack = current_sl(irp); let byte_offset = (*stack).Parameters.Write.ByteOffset.QuadPart as u64; - let length = (*stack).Parameters.Write.Length as usize; + let length = (*stack).Parameters.Write.Length as usize; let sector_size = volume.sector_size as usize; if sector_size == 0 || length == 0 { complete_irp(irp, STATUS_SUCCESS, 0); return; } - let first_lba = byte_offset / sector_size as u64; + let first_lba = byte_offset / sector_size as u64; let sector_count = length / sector_size; if sector_count == 0 { complete_irp(irp, STATUS_SUCCESS, 0); diff --git a/lib/windrv/src/handover.rs b/lib/windrv/src/handover.rs index 5b40577..7dcd2d5 100644 --- a/lib/windrv/src/handover.rs +++ b/lib/windrv/src/handover.rs @@ -81,7 +81,8 @@ fn read_handover_variable(var_name: &str, var_guid: [u8; 16]) -> VckResult VckResult VckResult { - let mut me = Self { handle, sector_size: 0, total_sectors: 0 }; + let mut me = Self { + handle, + sector_size: 0, + total_sectors: 0, + }; me.allow_extended_dasd_io(); me.query_geometry()?; Ok(me) @@ -153,10 +159,10 @@ impl KernelVolumeIo { ObOpenObjectByPointer( device_object.cast::(), OBJ_KERNEL_HANDLE, - null_mut(), // PassedAccessState + null_mut(), // PassedAccessState FILE_READ_DATA | FILE_WRITE_DATA, - null_mut(), // ObjectType — null = any - 0, // KernelMode + null_mut(), // ObjectType — null = any + 0, // KernelMode &mut handle, ) }; @@ -304,8 +310,7 @@ impl KernelVolumeIo { let byte_offset = lba .checked_mul(self.sector_size as u64) .ok_or_else(|| VckError::Io("sector offset overflow".into()))?; - let length = - u32::try_from(len).map_err(|_| VckError::Io("I/O length too large".into()))?; + let length = u32::try_from(len).map_err(|_| VckError::Io("I/O length too large".into()))?; let mut iosb: IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; let mut offset = LARGE_INTEGER { @@ -389,15 +394,17 @@ impl SectorIo for KernelVolumeIo { // acquire the same lock in a filter completion routine. // --------------------------------------------------------------------------- +use crate::nt::STATUS_PENDING; use wdk_sys::{ ntddk::{ IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IofCallDriver, KeInitializeEvent, KeWaitForSingleObject, }, - KEVENT, IRP_MJ_READ, IRP_MJ_WRITE, _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, + IRP_MJ_READ, IRP_MJ_WRITE, KEVENT, + _EVENT_TYPE::NotificationEvent, + _KWAIT_REASON::Executive, _MODE::KernelMode, }; -use crate::nt::STATUS_PENDING; /// IRP-based sector I/O routed directly to `device_object`, bypassing any /// filter sitting above it. The caller guarantees the device outlives this @@ -415,7 +422,11 @@ impl LowerDeviceIo { /// Wrap a lower device object. Does NOT take a reference; the caller must /// ensure the device outlives this value. pub fn new(device_object: PDEVICE_OBJECT, sector_size: u32, total_sectors: u64) -> Self { - Self { device_object, sector_size, total_sectors } + Self { + device_object, + sector_size, + total_sectors, + } } /// Send a METHOD_BUFFERED disk IOCTL (no input) directly to the device via an @@ -442,7 +453,9 @@ impl LowerDeviceIo { ) }; if irp.is_null() { - return Err(VckError::Io("IoBuildDeviceIoControlRequest(lower) failed".into())); + return Err(VckError::Io( + "IoBuildDeviceIoControlRequest(lower) failed".into(), + )); } let mut status = unsafe { IofCallDriver(self.device_object, irp) }; if status == STATUS_PENDING { @@ -471,7 +484,9 @@ impl LowerDeviceIo { let off = DISK_GEOMETRY_BYTES_PER_SECTOR_OFFSET; let bps = u32::from_le_bytes(geom[off..off + 4].try_into().unwrap()); if bps == 0 { - return Err(VckError::Io("lower device reported zero sector size".into())); + return Err(VckError::Io( + "lower device reported zero sector size".into(), + )); } let mut len = [0u8; 8]; self.device_ioctl(IOCTL_DISK_GET_LENGTH_INFO, &mut len)?; @@ -508,7 +523,9 @@ impl LowerDeviceIo { let mut event: KEVENT = unsafe { core::mem::zeroed() }; let mut iosb: IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; - let mut offset = LARGE_INTEGER { QuadPart: byte_offset as i64 }; + let mut offset = LARGE_INTEGER { + QuadPart: byte_offset as i64, + }; unsafe { KeInitializeEvent(&mut event, NotificationEvent, 0) }; @@ -524,7 +541,9 @@ impl LowerDeviceIo { ) }; if irp.is_null() { - return Err(VckError::Io("IoBuildSynchronousFsdRequest(lower) failed".into())); + return Err(VckError::Io( + "IoBuildSynchronousFsdRequest(lower) failed".into(), + )); } let mut status = unsafe { IofCallDriver(self.device_object, irp) }; @@ -548,8 +567,12 @@ impl LowerDeviceIo { } impl SectorIo for LowerDeviceIo { - fn sector_size(&self) -> u32 { self.sector_size } - fn total_sectors(&self) -> u64 { self.total_sectors } + fn sector_size(&self) -> u32 { + self.sector_size + } + fn total_sectors(&self) -> u64 { + self.total_sectors + } fn read_sectors(&self, lba: u64, buf: &mut [u8]) -> VckResult<()> { self.run_sync(IRP_MJ_READ, lba, buf.as_mut_ptr(), buf.len()) @@ -591,7 +614,11 @@ pub fn open_volume_handle_raw(nt_path: &str) -> Option { 0, ) }; - if nt_success(status) { Some(handle) } else { None } + if nt_success(status) { + Some(handle) + } else { + None + } } /// Send a no-in/no-out FSCTL to `handle`. Returns the NTSTATUS. @@ -611,18 +638,26 @@ impl OffsetSectorIo { inner: alloc::sync::Arc, partition_start_lba: u64, ) -> Self { - Self { inner, partition_start_lba } + Self { + inner, + partition_start_lba, + } } } impl vck_common::SectorIo for OffsetSectorIo { - fn sector_size(&self) -> u32 { self.inner.sector_size() } - fn total_sectors(&self) -> u64 { self.inner.total_sectors() } + fn sector_size(&self) -> u32 { + self.inner.sector_size() + } + fn total_sectors(&self) -> u64 { + self.inner.total_sectors() + } fn read_sectors(&self, lba: u64, buf: &mut [u8]) -> VckResult<()> { self.inner.read_sectors(self.partition_start_lba + lba, buf) } fn write_sectors(&self, lba: u64, buf: &[u8]) -> VckResult<()> { - self.inner.write_sectors(self.partition_start_lba + lba, buf) + self.inner + .write_sectors(self.partition_start_lba + lba, buf) } } @@ -630,8 +665,16 @@ pub fn send_fsctl(handle: wdk_sys::HANDLE, code: u32) -> i32 { let mut iosb: IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; unsafe { ZwFsControlFile( - handle, null_mut(), None, null_mut(), &mut iosb, - code, null_mut(), 0, null_mut(), 0, + handle, + null_mut(), + None, + null_mut(), + &mut iosb, + code, + null_mut(), + 0, + null_mut(), + 0, ) } } diff --git a/lib/windrv/src/ioctl/dispatch.rs b/lib/windrv/src/ioctl/dispatch.rs index 6fe940f..4aa909d 100644 --- a/lib/windrv/src/ioctl/dispatch.rs +++ b/lib/windrv/src/ioctl/dispatch.rs @@ -12,9 +12,7 @@ use core::ptr::null_mut; use core::sync::atomic::{AtomicPtr, AtomicU64, Ordering}; use spin::Mutex; -use vck_common::{ - types::Guid, EncryptedOffsetStore, SectorIo, VckError, VckResult, VolumeId, -}; +use vck_common::{types::Guid, EncryptedOffsetStore, SectorIo, VckError, VckResult, VolumeId}; use crate::{ crypto::aes_xts::AesXtsCipher, @@ -31,9 +29,9 @@ use alloc::vec::Vec; use serde::Serialize; use super::types::{ - BenchAesReq, BenchAesResp, JvckVolumeAttachReq, JvckVolumeAttachResp, - JvckVolumePrepareReq, JvckVolumePrepareResp, ProgressEvent, VolumeListEntry, - VolumeListResponse, VolumeRequest, VolumeStatus, + BenchAesReq, BenchAesResp, JvckVolumeAttachReq, JvckVolumeAttachResp, JvckVolumePrepareReq, + JvckVolumePrepareResp, ProgressEvent, VolumeListEntry, VolumeListResponse, VolumeRequest, + VolumeStatus, }; pub type IoctlResponse = Vec; @@ -126,7 +124,8 @@ fn handle_get_status(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResult &nt_path, |dev| registry.find_pdo_filter(dev), |name| registry.find_pdo_filter_by_name(name), - ).is_some(); + ) + .is_some(); if let Some(volume) = resolve_volume(registry, &req.volume_path) { let snapshot = volume.encryption.lock().snapshot(); @@ -174,34 +173,31 @@ fn handle_list_volumes(registry: &VolumeAttachRegistry) -> VckResult VckResult { +fn handle_start_encrypt(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResult { let req: VolumeRequest = decode_req(input)?; let volume = resolve_volume(registry, &req.volume_path) .ok_or(VckError::NotFound("volume is not attached"))?; - volume.encryption.lock().start_encrypt(&*volume.offset_store); + volume + .encryption + .lock() + .start_encrypt(&*volume.offset_store); unsafe { crate::filter::volume_thread::wake_for(&volume) }; encode_resp(&EmptyResponse {}) } -fn handle_start_decrypt( - registry: &VolumeAttachRegistry, - input: &[u8], -) -> VckResult { +fn handle_start_decrypt(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResult { let req: VolumeRequest = decode_req(input)?; let volume = resolve_volume(registry, &req.volume_path) .ok_or(VckError::NotFound("volume is not attached"))?; - volume.encryption.lock().start_decrypt(&*volume.offset_store); + volume + .encryption + .lock() + .start_decrypt(&*volume.offset_store); unsafe { crate::filter::volume_thread::wake_for(&volume) }; encode_resp(&EmptyResponse {}) } -fn handle_get_progress( - registry: &VolumeAttachRegistry, - input: &[u8], -) -> VckResult { +fn handle_get_progress(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResult { let req: VolumeRequest = decode_req(input)?; let volume = resolve_volume(registry, &req.volume_path) .ok_or(VckError::NotFound("volume is not attached"))?; @@ -251,14 +247,21 @@ fn handle_jvck_prepare_adddevice_path( }; // Query geometry (NTFS is mounted, ZwCreateFile works via existing filter pass-through). - let io_volume_id = VolumeId { partition_guid: Guid::nil(), device_path: nt_path.clone() }; + let io_volume_id = VolumeId { + partition_guid: Guid::nil(), + device_path: nt_path.clone(), + }; let geom_io = KernelVolumeIo::open_query(&io_volume_id).map_err(|e| { crate::vck_log!("jvck_prepare(add): geom open failed: {}", e); e })?; let sector_size = geom_io.sector_size(); let total_sectors = geom_io.total_sectors(); - crate::vck_log!("jvck_prepare(add): bps={} total={}", sector_size, total_sectors); + crate::vck_log!( + "jvck_prepare(add): bps={} total={}", + sector_size, + total_sectors + ); drop(geom_io); // Compute replica geometry. @@ -279,9 +282,13 @@ fn handle_jvck_prepare_adddevice_path( } let replica_lbas = { let mut v: alloc::vec::Vec = alloc::vec::Vec::new(); - for i in 0..req.use_header as u64 { v.push(i * rs); } + for i in 0..req.use_header as u64 { + v.push(i * rs); + } let fs = total_sectors - (req.use_footer as u64 * rs); - for j in 0..req.use_footer as u64 { v.push(fs + j * rs + rs - 1); } + for j in 0..req.use_footer as u64 { + v.push(fs + j * rs + rs - 1); + } v }; let mut sector_buf = alloc::vec![0u8; sector_size as usize]; @@ -306,17 +313,30 @@ fn handle_jvck_prepare_adddevice_path( io_config: IoConfig::Encrypted { cipher: None, offset_sector, - encrypted_offset: vck_common::types::EncryptedOffset { sector: 0, total_sectors: data_sectors }, - offset_store: Arc::new(DummyOffsetStore { total_sectors: data_sectors }), + encrypted_offset: vck_common::types::EncryptedOffset { + sector: 0, + total_sectors: data_sectors, + }, + offset_store: Arc::new(DummyOffsetStore { + total_sectors: data_sectors, + }), }, encryption: Mutex::new(EncryptionEngine::new( offset_sector, - vck_common::types::EncryptedOffset { sector: 0, total_sectors: data_sectors }, + vck_common::types::EncryptedOffset { + sector: 0, + total_sectors: data_sectors, + }, )), - offset_store: Arc::new(DummyOffsetStore { total_sectors: data_sectors }), + offset_store: Arc::new(DummyOffsetStore { + total_sectors: data_sectors, + }), attach_source, filter_device: AtomicPtr::new(filter_do), - sweep_io: Mutex::new(Arc::new(DummySectorIo { sector_size, total_sectors: data_sectors })), + sweep_io: Mutex::new(Arc::new(DummySectorIo { + sector_size, + total_sectors: data_sectors, + })), encrypted_boundary: AtomicU64::new(0), }); registry.insert(volume.clone()); @@ -334,16 +354,36 @@ fn handle_jvck_prepare_adddevice_path( if let Some(h) = open_volume_handle_raw(&nt_path) { let mut iosb: wdk_sys::IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; unsafe { - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_STORAGE_GET_DEVICE_NUMBER, null_mut(), 0, sdn.as_mut_ptr().cast(), 12); - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_DISK_GET_PARTITION_INFO_EX, null_mut(), 0, partition_info.as_mut_ptr().cast(), 64); + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_STORAGE_GET_DEVICE_NUMBER, + null_mut(), + 0, + sdn.as_mut_ptr().cast(), + 12, + ); + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_DISK_GET_PARTITION_INFO_EX, + null_mut(), + 0, + partition_info.as_mut_ptr().cast(), + 64, + ); let _ = ZwClose(h); } } let disk_num = sdn[1]; let part_num = sdn[2]; - let start_bytes = u64::from_le_bytes(partition_info[4..12].try_into().unwrap_or([0;8])); + let start_bytes = u64::from_le_bytes(partition_info[4..12].try_into().unwrap_or([0; 8])); let start_lba = start_bytes / sector_size as u64; break 'paths ( alloc::format!(r"\Device\Harddisk{}\Partition{}", disk_num, part_num), @@ -373,16 +413,14 @@ fn handle_jvck_prepare_adddevice_path( crate::filter::detach_filter(filter_do); e })?; - let (store_offset, encrypted_offset, offset_store) = - io_config.geometry().ok_or(VckError::ValidationFailed( - "on_attach returned passthrough for a VMK-provided volume", - ))?; + let (store_offset, encrypted_offset, offset_store) = io_config.geometry().ok_or( + VckError::ValidationFailed("on_attach returned passthrough for a VMK-provided volume"), + )?; let store_data = encrypted_offset.total_sectors; let store_bps = sector_size; // sweep_io uses LowerDeviceIo — direct to raw disk, below PartMgr ✓ - let sweep_io: Arc = Arc::new( - LowerDeviceIo::new(lower_do, store_bps, store_data) - ); + let sweep_io: Arc = + Arc::new(LowerDeviceIo::new(lower_do, store_bps, store_data)); registry.remove(&req.volume_path); let boundary = encrypted_offset.sector; let complete = Arc::new(AttachedVolume { @@ -398,16 +436,30 @@ fn handle_jvck_prepare_adddevice_path( }); registry.insert(complete.clone()); unsafe { crate::filter::filter_rebind_volume(filter_do, complete) }; - crate::vck_log!("jvck_prepare(add): fully attached offset={} data={}", store_offset, store_data); + crate::vck_log!( + "jvck_prepare(add): fully attached offset={} data={}", + store_offset, + store_data + ); return encode_resp(&JvckVolumePrepareResp { - offset_sector: store_offset, data_sectors: store_data, sector_size: store_bps, - raw_partition_path, raw_disk_path, partition_start_lba, fully_attached: true, + offset_sector: store_offset, + data_sectors: store_data, + sector_size: store_bps, + raw_partition_path, + raw_disk_path, + partition_start_lba, + fully_attached: true, }); } encode_resp(&JvckVolumePrepareResp { - offset_sector, data_sectors, sector_size, - raw_partition_path, raw_disk_path, partition_start_lba, fully_attached: false, + offset_sector, + data_sectors, + sector_size, + raw_partition_path, + raw_disk_path, + partition_start_lba, + fully_attached: false, }) } @@ -417,16 +469,30 @@ struct DummyOffsetStore { } impl EncryptedOffsetStore for DummyOffsetStore { fn load(&self) -> VckResult { - Ok(vck_common::types::EncryptedOffset { sector: 0, total_sectors: self.total_sectors }) + Ok(vck_common::types::EncryptedOffset { + sector: 0, + total_sectors: self.total_sectors, + }) + } + fn store(&self, _: &vck_common::types::EncryptedOffset) -> VckResult<()> { + Ok(()) + } + fn flush(&self) -> VckResult<()> { + Ok(()) } - fn store(&self, _: &vck_common::types::EncryptedOffset) -> VckResult<()> { Ok(()) } - fn flush(&self) -> VckResult<()> { Ok(()) } } /// Dummy `SectorIo` — I/O calls are no-ops; never used before ATTACH completes. -struct DummySectorIo { sector_size: u32, total_sectors: u64 } +struct DummySectorIo { + sector_size: u32, + total_sectors: u64, +} impl SectorIo for DummySectorIo { - fn sector_size(&self) -> u32 { self.sector_size } - fn total_sectors(&self) -> u64 { self.total_sectors } + fn sector_size(&self) -> u32 { + self.sector_size + } + fn total_sectors(&self) -> u64 { + self.total_sectors + } fn read_sectors(&self, _: u64, _: &mut [u8]) -> VckResult<()> { Err(VckError::Io("DummySectorIo: not ready yet".into())) } @@ -462,8 +528,12 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu let req: JvckVolumePrepareReq = decode_req(input)?; crate::vck_log!( "jvck_prepare: path={} use_h={} use_f={} md_size={} block_len={} is_os={}", - req.volume_path, req.use_header, req.use_footer, req.metadata_size, - req.metadata_block.len(), req.is_os_volume + req.volume_path, + req.use_header, + req.use_footer, + req.metadata_size, + req.metadata_block.len(), + req.is_os_volume ); // OS (system) volumes register as Handover so DETACH_ALL / detach / unload @@ -499,7 +569,8 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu ) { crate::vck_log!( "jvck_prepare: AddDevice filter found filter={:p} lower={:p}", - filter_do, lower_do + filter_do, + lower_do ); return handle_jvck_prepare_adddevice_path( registry, req, nt_path, driver, filter_do, lower_do, @@ -538,18 +609,38 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu let mut total = 0u64; let mut geom = [0u8; 32]; if crate::nt::nt_success(unsafe { - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_DISK_GET_DRIVE_GEOMETRY_PREPARE, null_mut(), 0, - geom.as_mut_ptr().cast(), 32) + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_DISK_GET_DRIVE_GEOMETRY_PREPARE, + null_mut(), + 0, + geom.as_mut_ptr().cast(), + 32, + ) }) { - bps = u32::from_le_bytes(geom[20..24].try_into().unwrap_or([0;4])); - if bps == 0 { bps = 512; } + bps = u32::from_le_bytes(geom[20..24].try_into().unwrap_or([0; 4])); + if bps == 0 { + bps = 512; + } } let mut len_buf = [0u8; 8]; if crate::nt::nt_success(unsafe { - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_DISK_GET_LENGTH_INFO_PREPARE, null_mut(), 0, - len_buf.as_mut_ptr().cast(), 8) + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_DISK_GET_LENGTH_INFO_PREPARE, + null_mut(), + 0, + len_buf.as_mut_ptr().cast(), + 8, + ) }) { total = u64::from_le_bytes(len_buf) / bps as u64; } @@ -558,8 +649,16 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu }); if sector_size == 0 || total_sectors == 0 { - if held_lock { if let Some(h) = vol_handle { send_fsctl(h, FSCTL_UNLOCK_VOLUME); } } - if let Some(h) = vol_handle { unsafe { let _ = ZwClose(h); } } + if held_lock { + if let Some(h) = vol_handle { + send_fsctl(h, FSCTL_UNLOCK_VOLUME); + } + } + if let Some(h) = vol_handle { + unsafe { + let _ = ZwClose(h); + } + } return Err(VckError::Io("could not query volume geometry".into())); } @@ -570,7 +669,10 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu let data_sectors = total_sectors.saturating_sub(header_sectors + footer_sectors); let offset_sector = header_sectors; crate::vck_log!( - "jvck_prepare: data_sectors={} offset_sector={} rs={}", data_sectors, offset_sector, rs + "jvck_prepare: data_sectors={} offset_sector={} rs={}", + data_sectors, + offset_sector, + rs ); // Compute replica LBAs for metadata write (done AFTER unlock below). @@ -590,15 +692,25 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu let target_do: PDO = if let Some(h) = vol_handle { let mut fobj: PFILE_OBJECT = null_mut(); let st = unsafe { - ObReferenceObjectByHandle(h, 0, null_mut(), 0, - (&mut fobj as *mut PFILE_OBJECT).cast(), null_mut()) + ObReferenceObjectByHandle( + h, + 0, + null_mut(), + 0, + (&mut fobj as *mut PFILE_OBJECT).cast(), + null_mut(), + ) }; if crate::nt::nt_success(st) && !fobj.is_null() { let do_ptr = unsafe { IoGetRelatedDeviceObject(fobj) }; unsafe { ObfDereferenceObject(fobj.cast()) }; do_ptr - } else { null_mut() } - } else { null_mut() }; + } else { + null_mut() + } + } else { + null_mut() + }; crate::vck_log!("jvck_prepare: target_do={:p}", target_do); let (filter_do, _lower_do) = if !target_do.is_null() { @@ -606,10 +718,19 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu } else { let win32_nt = win32_volume_path_to_nt(&req.volume_path); crate::filter::attach_filter_unbound(driver, &win32_nt) - }.map_err(|err| { + } + .map_err(|err| { crate::vck_log!("jvck_prepare: attach failed: {}", err); - if held_lock { if let Some(h) = vol_handle { send_fsctl(h, FSCTL_UNLOCK_VOLUME); } } - if let Some(h) = vol_handle { unsafe { let _ = ZwClose(h); } } + if held_lock { + if let Some(h) = vol_handle { + send_fsctl(h, FSCTL_UNLOCK_VOLUME); + } + } + if let Some(h) = vol_handle { + unsafe { + let _ = ZwClose(h); + } + } err })?; @@ -621,18 +742,29 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu cipher: None, offset_sector, encrypted_offset: vck_common::types::EncryptedOffset { - sector: 0, total_sectors: data_sectors, + sector: 0, + total_sectors: data_sectors, }, - offset_store: Arc::new(DummyOffsetStore { total_sectors: data_sectors }), + offset_store: Arc::new(DummyOffsetStore { + total_sectors: data_sectors, + }), }, encryption: Mutex::new(EncryptionEngine::new( offset_sector, - vck_common::types::EncryptedOffset { sector: 0, total_sectors: data_sectors }, + vck_common::types::EncryptedOffset { + sector: 0, + total_sectors: data_sectors, + }, )), - offset_store: Arc::new(DummyOffsetStore { total_sectors: data_sectors }), + offset_store: Arc::new(DummyOffsetStore { + total_sectors: data_sectors, + }), attach_source, filter_device: AtomicPtr::new(filter_do), - sweep_io: Mutex::new(Arc::new(DummySectorIo { sector_size, total_sectors: data_sectors })), + sweep_io: Mutex::new(Arc::new(DummySectorIo { + sector_size, + total_sectors: data_sectors, + })), encrypted_boundary: AtomicU64::new(0), }); registry.insert(volume.clone()); @@ -646,7 +778,11 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu crate::vck_log!("jvck_prepare: UNLOCK=0x{:08x}", st); } } - if let Some(h) = vol_handle { unsafe { let _ = ZwClose(h); } } + if let Some(h) = vol_handle { + unsafe { + let _ = ZwClose(h); + } + } // ── Step 6: write metadata_block to replica LBAs (AFTER unlock) ───────── // The filter is now bound with size hiding active. NTFS has re-mounted and @@ -659,7 +795,9 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu if req.metadata_block.len() < METADATA_BLOCK_SIZE { registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); - return Err(VckError::InvalidData("metadata_block must be at least 512 bytes")); + return Err(VckError::InvalidData( + "metadata_block must be at least 512 bytes", + )); } let write_vol_id = VolumeId { partition_guid: Guid::nil(), @@ -672,13 +810,9 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu sector_buf[..copy_len].copy_from_slice(&req.metadata_block[..copy_len]); for lba in &replica_lbas { match write_io.write_sectors(*lba, §or_buf) { - Ok(()) => crate::vck_log!( - "jvck_prepare: wrote metadata lba={}", lba - ), + Ok(()) => crate::vck_log!("jvck_prepare: wrote metadata lba={}", lba), Err(err) => { - crate::vck_log!( - "jvck_prepare: write failed lba={}: {}", lba, err - ); + crate::vck_log!("jvck_prepare: write failed lba={}: {}", lba, err); registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); return Err(err); @@ -725,45 +859,72 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu if let Some(h) = open_volume_handle_raw(&nt_path) { let mut iosb: wdk_sys::IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; let st1 = unsafe { - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_STORAGE_GET_DEVICE_NUMBER, null_mut(), 0, - sdn.as_mut_ptr().cast(), 12) + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_STORAGE_GET_DEVICE_NUMBER, + null_mut(), + 0, + sdn.as_mut_ptr().cast(), + 12, + ) }; if crate::nt::nt_success(st1) { disk_num = sdn[1]; part_num = sdn[2]; } let st2 = unsafe { - ZwDeviceIoControlFile(h, null_mut(), None, null_mut(), &mut iosb, - IOCTL_DISK_GET_PARTITION_INFO_EX, null_mut(), 0, - partition_info.as_mut_ptr().cast(), 64) + ZwDeviceIoControlFile( + h, + null_mut(), + None, + null_mut(), + &mut iosb, + IOCTL_DISK_GET_PARTITION_INFO_EX, + null_mut(), + 0, + partition_info.as_mut_ptr().cast(), + 64, + ) }; if crate::nt::nt_success(st2) { // PartitionStyle(4 bytes) + StartingOffset(8 bytes) at offset 4 - start_offset_bytes = u64::from_le_bytes( - partition_info[4..12].try_into().unwrap_or([0;8]) - ); + start_offset_bytes = + u64::from_le_bytes(partition_info[4..12].try_into().unwrap_or([0; 8])); } crate::vck_log!( "jvck_prepare: disk={} part={} start_offset={}", - disk_num, part_num, start_offset_bytes + disk_num, + part_num, + start_offset_bytes ); - unsafe { let _ = ZwClose(h); } + unsafe { + let _ = ZwClose(h); + } } let partition_path = if disk_num > 0 || part_num > 0 { alloc::format!(r"\Device\Harddisk{}\Partition{}", disk_num, part_num) - } else { String::new() }; + } else { + String::new() + }; let disk_path = if disk_num > 0 || part_num > 0 { alloc::format!(r"\Device\Harddisk{}\DR0", disk_num) - } else { String::new() }; + } else { + String::new() + }; let start_lba = start_offset_bytes / sector_size as u64; (partition_path, disk_path, start_lba) }; crate::vck_log!( "jvck_prepare: raw_partition={} raw_disk={} start_lba={}", - raw_partition_path, raw_disk_path, partition_start_lba + raw_partition_path, + raw_disk_path, + partition_start_lba ); // If VMK was provided, complete the full attach now: read metadata, build @@ -802,37 +963,56 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu crate::filter::detach_filter(filter_do); err })?; - let (store_offset, encrypted_offset, offset_store) = - io_config.geometry().ok_or(VckError::ValidationFailed( - "on_attach returned passthrough for a VMK-provided volume", - ))?; + let (store_offset, encrypted_offset, offset_store) = io_config.geometry().ok_or( + VckError::ValidationFailed("on_attach returned passthrough for a VMK-provided volume"), + )?; let store_data = encrypted_offset.total_sectors; // sweep_io: prefer raw disk (bypass PartMgr), fall back to partition path. let sweep_io: Arc = if !raw_disk_path.is_empty() && partition_start_lba > 0 { - let disk_id = VolumeId { partition_guid: Guid::nil(), device_path: raw_disk_path.clone() }; + let disk_id = VolumeId { + partition_guid: Guid::nil(), + device_path: raw_disk_path.clone(), + }; let disk_total = partition_start_lba + store_data + 1024; - crate::vck_log!("jvck_prepare: sweep via raw disk {} lba={}", raw_disk_path, partition_start_lba); + crate::vck_log!( + "jvck_prepare: sweep via raw disk {} lba={}", + raw_disk_path, + partition_start_lba + ); let disk_io = KernelVolumeIo::open(&disk_id, store_bps, disk_total).map_err(|e| { registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); e })?; - Arc::new(OffsetSectorIo::new(Arc::new(disk_io) as Arc, partition_start_lba)) + Arc::new(OffsetSectorIo::new( + Arc::new(disk_io) as Arc, + partition_start_lba, + )) } else if !raw_partition_path.is_empty() { - let part_id = VolumeId { partition_guid: Guid::nil(), device_path: raw_partition_path.clone() }; - crate::vck_log!("jvck_prepare: sweep via raw partition {}", raw_partition_path); - Arc::new(KernelVolumeIo::open(&part_id, store_bps, store_data).map_err(|e| { - registry.remove(&req.volume_path); - crate::filter::detach_filter(filter_do); - e - })?) + let part_id = VolumeId { + partition_guid: Guid::nil(), + device_path: raw_partition_path.clone(), + }; + crate::vck_log!( + "jvck_prepare: sweep via raw partition {}", + raw_partition_path + ); + Arc::new( + KernelVolumeIo::open(&part_id, store_bps, store_data).map_err(|e| { + registry.remove(&req.volume_path); + crate::filter::detach_filter(filter_do); + e + })?, + ) } else { - Arc::new(KernelVolumeIo::open(&io_volume_id, store_bps, store_data).map_err(|e| { - registry.remove(&req.volume_path); - crate::filter::detach_filter(filter_do); - e - })?) + Arc::new( + KernelVolumeIo::open(&io_volume_id, store_bps, store_data).map_err(|e| { + registry.remove(&req.volume_path); + crate::filter::detach_filter(filter_do); + e + })?, + ) }; registry.remove(&req.volume_path); @@ -850,20 +1030,30 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu }); registry.insert(complete_volume.clone()); unsafe { crate::filter::filter_rebind_volume(filter_do, complete_volume) }; - crate::vck_log!("jvck_prepare: fully attached offset={} data={}", store_offset, store_data); + crate::vck_log!( + "jvck_prepare: fully attached offset={} data={}", + store_offset, + store_data + ); return encode_resp(&JvckVolumePrepareResp { offset_sector: store_offset, data_sectors: store_data, sector_size: store_bps, - raw_partition_path, raw_disk_path, partition_start_lba, + raw_partition_path, + raw_disk_path, + partition_start_lba, fully_attached: true, }); } encode_resp(&JvckVolumePrepareResp { - offset_sector, data_sectors, sector_size, - raw_partition_path, raw_disk_path, partition_start_lba, + offset_sector, + data_sectors, + sector_size, + raw_partition_path, + raw_disk_path, + partition_start_lba, fully_attached: false, }) } @@ -882,7 +1072,8 @@ fn handle_jvck_attach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResul let req: JvckVolumeAttachReq = decode_req(input)?; crate::vck_log!( "jvck_attach: path={} vmk_len={}", - req.volume_path, req.vmk.len() + req.volume_path, + req.vmk.len() ); // ATTACH (phase 2) is the data-volume re-attach path; OS volumes complete via @@ -890,9 +1081,9 @@ fn handle_jvck_attach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResul let attach_source = AttachSource::Ioctl; // Find the provisional volume created by PREPARE (has cipher=None). - let provisional = registry - .get(&req.volume_path) - .ok_or(VckError::NotFound("volume not prepared; call IOCTL_JVCK_PREPARE first"))?; + let provisional = registry.get(&req.volume_path).ok_or(VckError::NotFound( + "volume not prepared; call IOCTL_JVCK_PREPARE first", + ))?; if provisional.cipher().is_some() { return Err(VckError::Unsupported("volume already fully attached")); @@ -926,7 +1117,8 @@ fn handle_jvck_attach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResul let partition_total = probe_io.total_sectors(); crate::vck_log!( "jvck_attach: partition_total={} sector_size={}", - partition_total, probe_io.sector_size() + partition_total, + probe_io.sector_size() ); // Diagnostic: print footer bytes. @@ -953,10 +1145,9 @@ fn handle_jvck_attach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResul })?; crate::vck_log!("jvck_attach: metadata opened"); - let (offset_sector, encrypted_offset, offset_store) = - io_config.geometry().ok_or(VckError::ValidationFailed( - "on_attach returned passthrough for a VMK-provided volume", - ))?; + let (offset_sector, encrypted_offset, offset_store) = io_config.geometry().ok_or( + VckError::ValidationFailed("on_attach returned passthrough for a VMK-provided volume"), + )?; let store_data_sectors = encrypted_offset.total_sectors; crate::vck_log!("jvck_attach: build sweep_io"); @@ -981,11 +1172,22 @@ fn handle_jvck_attach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResul partition_guid: Guid::nil(), device_path: req.raw_partition_path.clone(), }; - crate::vck_log!("jvck_attach: sweep_io via raw partition {}", req.raw_partition_path); - Arc::new(KernelVolumeIo::open(&part_id, store_sector_size, store_data_sectors)?) + crate::vck_log!( + "jvck_attach: sweep_io via raw partition {}", + req.raw_partition_path + ); + Arc::new(KernelVolumeIo::open( + &part_id, + store_sector_size, + store_data_sectors, + )?) } else { crate::vck_log!("jvck_attach: sweep_io via volume (no raw path)"); - Arc::new(KernelVolumeIo::open(&io_volume_id, store_sector_size, store_data_sectors)?) + Arc::new(KernelVolumeIo::open( + &io_volume_id, + store_sector_size, + store_data_sectors, + )?) }; // Replace the provisional volume with the complete one. The filter_do stays; @@ -1051,11 +1253,17 @@ pub fn detach_volume_with_dismount( if let Some(h) = vol_handle { // Step 1: lock (non-fatal if ignore_open_files). let lock_st = send_fsctl(h, FSCTL_LOCK_VOLUME); - crate::vck_log!("detach: LOCK_VOLUME=0x{:08x} ignore={}", lock_st, ignore_open_files); + crate::vck_log!( + "detach: LOCK_VOLUME=0x{:08x} ignore={}", + lock_st, + ignore_open_files + ); if crate::nt::nt_success(lock_st) { held_lock = true; } else if !ignore_open_files { - unsafe { let _ = ZwClose(h); } + unsafe { + let _ = ZwClose(h); + } return Err(VckError::Io("FSCTL_LOCK_VOLUME failed (files open)".into())); } @@ -1064,19 +1272,28 @@ pub fn detach_volume_with_dismount( for attempt in 0..MAX_RETRIES { let dis_st = send_fsctl(h, FSCTL_DISMOUNT_VOLUME); crate::vck_log!( - "detach: DISMOUNT_VOLUME attempt={} status=0x{:08x}", attempt, dis_st + "detach: DISMOUNT_VOLUME attempt={} status=0x{:08x}", + attempt, + dis_st ); if crate::nt::nt_success(dis_st) { dismounted = true; break; } if attempt < MAX_RETRIES - 1 { - let mut interval = LARGE_INTEGER { QuadPart: DELAY_100MS }; - unsafe { KeDelayExecutionThread(0 /*KernelMode*/, 0 /*FALSE*/, &mut interval); } + let mut interval = LARGE_INTEGER { + QuadPart: DELAY_100MS, + }; + unsafe { + KeDelayExecutionThread(0 /*KernelMode*/, 0 /*FALSE*/, &mut interval); + } } } if !dismounted { - crate::vck_log!("detach: DISMOUNT_VOLUME failed after {} retries", MAX_RETRIES); + crate::vck_log!( + "detach: DISMOUNT_VOLUME failed after {} retries", + MAX_RETRIES + ); } // Unlock so the FSD (if any) can re-mount (or stays unmounted — we'll detach). @@ -1084,7 +1301,9 @@ pub fn detach_volume_with_dismount( let unlock_st = send_fsctl(h, FSCTL_UNLOCK_VOLUME); crate::vck_log!("detach: UNLOCK_VOLUME=0x{:08x}", unlock_st); } - unsafe { let _ = ZwClose(h); } + unsafe { + let _ = ZwClose(h); + } } // Brief delay to allow any in-flight IRPs on the filter to complete before @@ -1093,8 +1312,12 @@ pub fn detach_volume_with_dismount( { use wdk_sys::ntddk::KeDelayExecutionThread; use wdk_sys::LARGE_INTEGER; - let mut interval = LARGE_INTEGER { QuadPart: -5_000_000 }; // 500 ms - unsafe { KeDelayExecutionThread(0, 0, &mut interval); } + let mut interval = LARGE_INTEGER { + QuadPart: -5_000_000, + }; // 500 ms + unsafe { + KeDelayExecutionThread(0, 0, &mut interval); + } } // Step 3: filter teardown. User detach keeps the filter as pass-through so @@ -1107,7 +1330,11 @@ pub fn detach_volume_with_dismount( crate::filter::detach_filter(filter_do); } } - crate::vck_log!("detach: done for {} (keep_filter={})", volume_path, keep_filter); + crate::vck_log!( + "detach: done for {} (keep_filter={})", + volume_path, + keep_filter + ); Ok(()) } @@ -1117,7 +1344,10 @@ fn handle_detach(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResult VckResult VckResult { + use crate::ntddk_ex::KeQueryPerformanceCounter; use wdk_sys::{ ntddk::{ExAllocatePool2, ExFreePool}, LARGE_INTEGER, }; - use crate::ntddk_ex::KeQueryPerformanceCounter; const DEFAULT_BYTES: u64 = 1u64 << 30; // 1 GiB - const CHUNK_BYTES: usize = 1 << 20; // 1 MiB scratch buffer + const CHUNK_BYTES: usize = 1 << 20; // 1 MiB scratch buffer const SECTOR_SIZE: usize = 512; const SECTORS_PER_CHUNK: usize = CHUNK_BYTES / SECTOR_SIZE; const POOL_FLAG_NON_PAGED: u64 = 0x0000_0000_0000_0040; const POOL_TAG: u32 = u32::from_le_bytes(*b"VCKI"); let req: BenchAesReq = decode_req(input)?; - let size_bytes = if req.size_bytes == 0 { DEFAULT_BYTES } else { req.size_bytes }; + let size_bytes = if req.size_bytes == 0 { + DEFAULT_BYTES + } else { + req.size_bytes + }; let cipher = AesXtsCipher::new([0u8; 32], [0u8; 32]) .map_err(|_| VckError::CryptoFailed("bench cipher init"))?; @@ -1241,8 +1475,16 @@ fn handle_bench_aes(input: &[u8]) -> VckResult { let enc_ticks = unsafe { enc_end.QuadPart - enc_start.QuadPart } as u64; let dec_ticks = unsafe { dec_end.QuadPart - dec_start.QuadPart } as u64; - let encrypt_mib_s = if enc_ticks == 0 { 0 } else { actual_mib * freq_val / enc_ticks }; - let decrypt_mib_s = if dec_ticks == 0 { 0 } else { actual_mib * freq_val / dec_ticks }; + let encrypt_mib_s = if enc_ticks == 0 { + 0 + } else { + actual_mib * freq_val / enc_ticks + }; + let decrypt_mib_s = if dec_ticks == 0 { + 0 + } else { + actual_mib * freq_val / dec_ticks + }; crate::vck_log!( "bench_aes: size={}MiB enc={}MiB/s dec={}MiB/s", diff --git a/lib/windrv/src/lib.rs b/lib/windrv/src/lib.rs index 262e346..ea99882 100644 --- a/lib/windrv/src/lib.rs +++ b/lib/windrv/src/lib.rs @@ -15,13 +15,13 @@ extern crate alloc; pub mod crypto; pub mod debug; pub mod device; -pub mod ntddk_ex; pub mod executor; pub mod filter; pub mod handover; pub mod io; pub mod ioctl; pub mod nt; +pub mod ntddk_ex; pub mod offset; pub mod provider; pub mod registry; diff --git a/lib/windrv/src/ntddk_ex.rs b/lib/windrv/src/ntddk_ex.rs index cf7ac8f..341ac7c 100644 --- a/lib/windrv/src/ntddk_ex.rs +++ b/lib/windrv/src/ntddk_ex.rs @@ -10,16 +10,14 @@ use core::ffi::c_void; use core::sync::atomic::{AtomicPtr, Ordering}; use wdk_sys::{ - ntddk::MmMapLockedPagesSpecifyCache, - DRIVER_CANCEL, LARGE_INTEGER, PIO_STACK_LOCATION, PIRP, PMDL, + ntddk::MmMapLockedPagesSpecifyCache, DRIVER_CANCEL, LARGE_INTEGER, PIO_STACK_LOCATION, PIRP, + PMDL, }; extern "C" { /// Query the performance counter. When `performance_frequency` is non-null /// the counter frequency (ticks/second) is written there. Callable at any IRQL. - pub fn KeQueryPerformanceCounter( - performance_frequency: *mut LARGE_INTEGER, - ) -> LARGE_INTEGER; + pub fn KeQueryPerformanceCounter(performance_frequency: *mut LARGE_INTEGER) -> LARGE_INTEGER; } /// Read the system clock as a Windows FILETIME (100-ns ticks since 1601-01-01). @@ -122,7 +120,7 @@ pub unsafe fn MmGetSystemAddressForMdlSafe(mdl: PMDL) -> *mut c_void { pub unsafe fn IoSetCancelRoutine(irp: PIRP, routine: DRIVER_CANCEL) -> DRIVER_CANCEL { let new_ptr = match routine { Some(r) => r as *mut c_void, - None => core::ptr::null_mut(), + None => core::ptr::null_mut(), }; let slot = &raw mut (*irp).CancelRoutine as *mut _ as *mut AtomicPtr; let old_ptr = (*slot).swap(new_ptr, Ordering::SeqCst); diff --git a/lib/windrv/src/provider.rs b/lib/windrv/src/provider.rs index d8c8ffe..63bd432 100644 --- a/lib/windrv/src/provider.rs +++ b/lib/windrv/src/provider.rs @@ -20,8 +20,7 @@ use alloc::boxed::Box; use alloc::sync::Arc; use vck_common::{ - types::Guid, EncryptedOffset, EncryptedOffsetStore, SectorIo, VckResult, VolumeCipher, - VolumeId, + types::Guid, EncryptedOffset, EncryptedOffsetStore, SectorIo, VckResult, VolumeCipher, VolumeId, }; use wdk_sys::{ ntddk::{PsDereferencePrimaryToken, PsReferencePrimaryToken, SeTokenIsAdmin}, @@ -99,7 +98,11 @@ impl IoConfig { encrypted_offset, offset_store, .. - } => Some((*offset_sector, encrypted_offset.clone(), offset_store.clone())), + } => Some(( + *offset_sector, + encrypted_offset.clone(), + offset_store.clone(), + )), } } } diff --git a/lib/windrv/src/registry.rs b/lib/windrv/src/registry.rs index e14ade6..2190e89 100644 --- a/lib/windrv/src/registry.rs +++ b/lib/windrv/src/registry.rs @@ -122,7 +122,8 @@ impl VolumeAttachRegistry { /// Store the boot ACPI handover essentials (called once at DriverEntry). pub fn set_handover(&self, info: HandoverInfo) { crate::vck_log!( - "registry: handover set partition_guid={}", info.partition_guid + "registry: handover set partition_guid={}", + info.partition_guid ); *self.handover.lock() = Some(info); } @@ -141,7 +142,12 @@ impl VolumeAttachRegistry { pdo_name: alloc::string::String, ) { crate::vck_log!("add_pdo_filter: name={} filter={:p}", pdo_name, filter_do); - self.pdo_filters.lock().push(PdoFilterEntry { pdo, filter_do, lower_do, pdo_name }); + self.pdo_filters.lock().push(PdoFilterEntry { + pdo, + filter_do, + lower_do, + pdo_name, + }); } /// Return the recorded PDO object name for the given filter device object @@ -157,7 +163,10 @@ impl VolumeAttachRegistry { } /// Look up the filter by device pointer address (fallback). - pub fn find_pdo_filter(&self, dev: *mut DEVICE_OBJECT) -> Option<(*mut DEVICE_OBJECT, *mut DEVICE_OBJECT)> { + pub fn find_pdo_filter( + &self, + dev: *mut DEVICE_OBJECT, + ) -> Option<(*mut DEVICE_OBJECT, *mut DEVICE_OBJECT)> { let map = self.pdo_filters.lock(); for e in map.iter() { if e.pdo == dev || e.lower_do == dev { @@ -170,7 +179,10 @@ impl VolumeAttachRegistry { /// Look up the filter by PDO name (e.g. `\Device\HarddiskVolume5`). /// This is the primary lookup since Volume class PDOs may not match /// the device object returned by IoGetDeviceObjectPointer. - pub fn find_pdo_filter_by_name(&self, nt_path: &str) -> Option<(*mut DEVICE_OBJECT, *mut DEVICE_OBJECT)> { + pub fn find_pdo_filter_by_name( + &self, + nt_path: &str, + ) -> Option<(*mut DEVICE_OBJECT, *mut DEVICE_OBJECT)> { // Normalize: trim trailing slashes and case-fold. let query = nt_path.trim_end_matches('\\').to_ascii_lowercase(); let map = self.pdo_filters.lock(); @@ -271,7 +283,9 @@ impl AttachedVolume { /// provisional (not-yet-keyed) attach. pub fn cipher(&self) -> Option<&dyn VolumeCipher> { match &self.io_config { - IoConfig::Encrypted { cipher: Some(c), .. } => Some(&**c), + IoConfig::Encrypted { + cipher: Some(c), .. + } => Some(&**c), _ => None, } } @@ -293,7 +307,12 @@ impl AttachedVolume { let io = self.sweep_io.lock().clone(); let result = { let mut engine = self.encryption.lock(); - engine.progress_step(io.as_ref(), cipher, self.offset_store.as_ref(), batch_sectors) + engine.progress_step( + io.as_ref(), + cipher, + self.offset_store.as_ref(), + batch_sectors, + ) }; // lock released here before sync_boundary if result.is_ok() { self.sync_boundary(); diff --git a/sample/common/Cargo.toml b/sample/common/Cargo.toml index b5ca798..0e778a2 100644 --- a/sample/common/Cargo.toml +++ b/sample/common/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-sample-common" -version = "0.1.0" +version = "0.0.1" edition.workspace = true license.workspace = true repository.workspace = true diff --git a/sample/common/src/config.rs b/sample/common/src/config.rs index ae79eda..1c0ce47 100644 --- a/sample/common/src/config.rs +++ b/sample/common/src/config.rs @@ -103,9 +103,9 @@ impl VckConfig { // Device path of the partition (device) our loader image came from. let li = open_protocol_exclusive::(image_handle()) .map_err(|e| VckError::Io(format!("open LoadedImage failed: {e:?}")))?; - let dev_handle = li - .device() - .ok_or(VckError::Io(String::from("loaded image has no device handle")))?; + let dev_handle = li.device().ok_or(VckError::Io(String::from( + "loaded image has no device handle", + )))?; let dev_path = open_protocol_exclusive::(dev_handle) .map_err(|e| VckError::Io(format!("open device DevicePath failed: {e:?}")))?; @@ -177,7 +177,9 @@ impl JsonObject { } i = skip_ws(bytes, i + 1); if bytes.get(i) != Some(&b'"') { - return Err(VckError::InvalidData("vck.json: only string values supported")); + return Err(VckError::InvalidData( + "vck.json: only string values supported", + )); } let (value, next) = parse_string(bytes, i)?; entries.push((key, value)); diff --git a/sample/crypto-test/Cargo.toml b/sample/crypto-test/Cargo.toml index 103fc11..2e780ae 100644 --- a/sample/crypto-test/Cargo.toml +++ b/sample/crypto-test/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-crypto-test-driver" -version = "0.1.0" +version = "0.0.1" edition.workspace = true license.workspace = true repository.workspace = true diff --git a/sample/loader/Cargo.toml b/sample/loader/Cargo.toml index 8633208..04b2d0d 100644 --- a/sample/loader/Cargo.toml +++ b/sample/loader/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-sample-loader" -version = "0.1.0" +version = "0.0.1" edition.workspace = true license.workspace = true repository.workspace = true diff --git a/sample/windrv/Cargo.toml b/sample/windrv/Cargo.toml index ac4c9de..195d637 100644 --- a/sample/windrv/Cargo.toml +++ b/sample/windrv/Cargo.toml @@ -4,7 +4,7 @@ [package] name = "vck-sample-driver" -version = "0.1.0" +version = "0.0.1" edition.workspace = true license.workspace = true repository.workspace = true diff --git a/sample/windrv/src/lib.rs b/sample/windrv/src/lib.rs index edf5d0f..81b6b9a 100644 --- a/sample/windrv/src/lib.rs +++ b/sample/windrv/src/lib.rs @@ -18,17 +18,6 @@ use core::ffi::c_void; use core::panic::PanicInfo; use spin::{Lazy, Mutex}; -use wdk_alloc::WdkAllocator; -use wdk_sys::{ - ntddk::{ - IoBuildDeviceIoControlRequest, IoGetRequestorProcess, IofCallDriver, IofCompleteRequest, - KeInitializeEvent, KeWaitForSingleObject, KeExpandKernelStackAndCallout, - }, - CCHAR, DRIVER_OBJECT, IO_NO_INCREMENT, IO_STATUS_BLOCK, IRP_MJ_CLEANUP, IRP_MJ_CLOSE, - IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL, IRP_MJ_SHUTDOWN, KEVENT, NTSTATUS, PDEVICE_OBJECT, - PDRIVER_OBJECT, PIRP, PIO_STACK_LOCATION, PCUNICODE_STRING, - _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, _MODE, -}; use vck_driver::{ device::{ControlDevice, DeviceExtension, DEVICE_KIND_FILTER}, filter::handle_filter_irp, @@ -38,6 +27,19 @@ use vck_driver::{ VolumeAttachRegistry, }; use vck_sample_common::VckHandoverPayload; +use wdk_alloc::WdkAllocator; +use wdk_sys::{ + ntddk::{ + IoBuildDeviceIoControlRequest, IoGetRequestorProcess, IofCallDriver, IofCompleteRequest, + KeExpandKernelStackAndCallout, KeInitializeEvent, KeWaitForSingleObject, + }, + CCHAR, DRIVER_OBJECT, IO_NO_INCREMENT, IO_STATUS_BLOCK, IRP_MJ_CLEANUP, IRP_MJ_CLOSE, + IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL, IRP_MJ_SHUTDOWN, KEVENT, NTSTATUS, PCUNICODE_STRING, + PDEVICE_OBJECT, PDRIVER_OBJECT, PIO_STACK_LOCATION, PIRP, + _EVENT_TYPE::NotificationEvent, + _KWAIT_REASON::Executive, + _MODE, +}; #[global_allocator] static GLOBAL_ALLOCATOR: WdkAllocator = WdkAllocator; @@ -98,7 +100,11 @@ pub unsafe extern "system" fn DriverEntry( vck_driver::vck_log!("DriverEntry: vck-sample-driver loading"); vck_driver::vck_log!( "DriverEntry: AES-NI {}", - if vck_common::cpu::has_aes_ni() { "supported" } else { "not supported" } + if vck_common::cpu::has_aes_ni() { + "supported" + } else { + "not supported" + } ); // Install the kernel RNG so the JVCK metadata store can generate a fresh // per-write salt when persisting encrypted_offset. @@ -186,18 +192,31 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> let mut buf = [0u8; 256]; let mut ret_len: u32 = 0; let st = wdk_sys::ntddk::ObQueryNameString( - pdo.cast::(), buf.as_mut_ptr().cast(), 256, &mut ret_len, + pdo.cast::(), + buf.as_mut_ptr().cast(), + 256, + &mut ret_len, ); if st >= 0 { let name_len = u16::from_le_bytes([buf[0], buf[1]]) as usize / 2; - let name_ptr = usize::from_le_bytes(buf[8..16].try_into().unwrap_or([0;8])); + let name_ptr = usize::from_le_bytes(buf[8..16].try_into().unwrap_or([0; 8])); if name_len > 0 && name_ptr != 0 { let chars = core::slice::from_raw_parts(name_ptr as *const u16, name_len.min(64)); let mut s = alloc::string::String::new(); - for &c in chars { if c >= 0x20 && c < 0x7F { s.push(c as u8 as char); } else { s.push('?'); } } + for &c in chars { + if c >= 0x20 && c < 0x7F { + s.push(c as u8 as char); + } else { + s.push('?'); + } + } s - } else { alloc::string::String::new() } - } else { alloc::string::String::new() } + } else { + alloc::string::String::new() + } + } else { + alloc::string::String::new() + } }; vck_driver::vck_log!("add_device: pdo={:p} name={}", pdo, pdo_name); @@ -207,7 +226,9 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> match vck_driver::filter::attach_filter_to_raw_device(driver, pdo) { Ok((filter_do, lower_do)) => { vck_driver::vck_log!( - "add_device: filter attached filter={:p} lower={:p}", filter_do, lower_do + "add_device: filter attached filter={:p} lower={:p}", + filter_do, + lower_do ); REGISTRY.add_pdo_filter(pdo, filter_do, lower_do, pdo_name); STATUS_SUCCESS @@ -285,9 +306,7 @@ unsafe extern "C" fn driver_unload(_driver: PDRIVER_OBJECT) { // Unloading would leave C: reading raw ciphertext → guaranteed corruption. // Refuse by bugchecking with STATUS_INVALID_DEVICE_STATE as a parameter. if REGISTRY.has_encrypted_os_volume() { - vck_driver::vck_log!( - "sample-driver: unload refused — OS volume encrypted; bugchecking" - ); + vck_driver::vck_log!("sample-driver: unload refused — OS volume encrypted; bugchecking"); const STATUS_INVALID_DEVICE_STATE: u64 = 0xC000_0184; // Custom bug check code "VCK\0" — identifiable in the crash dump. P1 is // STATUS_INVALID_DEVICE_STATE indicating why the unload was refused. @@ -416,9 +435,11 @@ unsafe extern "C" fn dispatch_device_control( ) }; let dispatch_result = if callout_status >= 0 { - ex.result - .take() - .unwrap_or_else(|| Err(vck_driver::VckError::Io("dispatch callout did not run".into()))) + ex.result.take().unwrap_or_else(|| { + Err(vck_driver::VckError::Io( + "dispatch callout did not run".into(), + )) + }) } else { let auth_ctx = IoctlAuthContext { ioctl_code, @@ -452,11 +473,7 @@ unsafe extern "C" fn dispatch_device_control( } } Err(err) => { - vck_driver::vck_log!( - "sample-driver: ioctl 0x{:08x} failed: {}", - ioctl_code, - err - ); + vck_driver::vck_log!("sample-driver: ioctl 0x{:08x} failed: {}", ioctl_code, err); complete_irp(irp, STATUS_UNSUCCESSFUL, 0); STATUS_UNSUCCESSFUL } @@ -464,7 +481,14 @@ unsafe extern "C" fn dispatch_device_control( } unsafe fn current_stack_location(irp: PIRP) -> PIO_STACK_LOCATION { - unsafe { (*irp).Tail.Overlay.__bindgen_anon_2.__bindgen_anon_1.CurrentStackLocation } + unsafe { + (*irp) + .Tail + .Overlay + .__bindgen_anon_2 + .__bindgen_anon_1 + .CurrentStackLocation + } } unsafe fn complete_irp(irp: PIRP, status: NTSTATUS, information: usize) { diff --git a/sample/windrv/src/provider.rs b/sample/windrv/src/provider.rs index a08e74e..05b1c22 100644 --- a/sample/windrv/src/provider.rs +++ b/sample/windrv/src/provider.rs @@ -126,6 +126,8 @@ fn require_administrator(ctx: &IoctlAuthContext<'_>) -> VckResult<()> { } match ctx.requestor_token { Some(token) if token.is_admin() => Ok(()), - _ => Err(VckError::PermissionDenied("administrator privilege required")), + _ => Err(VckError::PermissionDenied( + "administrator privilege required", + )), } } From 0a247028b03b48fc2fca5842be29b14f6cfa9ba3 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 12:27:29 +0900 Subject: [PATCH 2/9] style: fmt and clippy --- .github/workflows/cicd.yml | 8 ++--- Makefile | 4 +++ lib/common/src/handover/payload.rs | 8 ++--- lib/common/src/jvck/codec.rs | 5 +-- lib/common/src/jvck/store.rs | 8 ++--- lib/loader/src/chainload.rs | 2 +- lib/loader/src/hook/block_io.rs | 9 +++-- lib/windrv/src/debug.rs | 8 +---- lib/windrv/src/device.rs | 9 ++--- lib/windrv/src/executor.rs | 4 +-- lib/windrv/src/filter/handover_mount.rs | 2 +- lib/windrv/src/filter/io.rs | 2 -- lib/windrv/src/filter/manager.rs | 16 ++++++--- lib/windrv/src/filter/volume_thread.rs | 22 +++++++++--- lib/windrv/src/handover.rs | 2 +- lib/windrv/src/io.rs | 7 ++-- lib/windrv/src/ioctl/dispatch.rs | 47 +++++++++++-------------- lib/windrv/src/nt.rs | 2 +- lib/windrv/src/ntddk_ex.rs | 15 +++++++- sample/loader/src/main.rs | 1 + sample/windrv/src/lib.rs | 11 +++--- 21 files changed, 111 insertions(+), 81 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 07b36a7..5d6bc71 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -82,18 +82,18 @@ jobs: - name: install x86_64-unknown-uefi run: rustup target add x86_64-unknown-uefi - - name: fmt + - name: fmt (host) run: cargo fmt --check - name: clippy (vck-common, host) run: cargo clippy -p vck-common -- -D warnings - - name: clippy (vck-driver, kernel target) + - name: clippy (vck-driver) shell: msys2 {0} - run: cargo clippy -p vck-driver -p vck-sample-driver -p vck-crypto-test-driver -- -D warnings + run: | + make clippy-driver - name: clippy (vck-loader, UEFI target) - shell: msys2 {0} run: cargo clippy -p vck-loader -p vck-sample-loader --target x86_64-unknown-uefi -- -D warnings - name: cargo test (host) diff --git a/Makefile b/Makefile index df1d22c..9a210ea 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,10 @@ UEFI_RUSTFLAGS = -C target-feature=-soft-float # Built in release: unoptimized debug frames overflow the small (~12 KiB) # kernel stack on the metadata/crypto path. +clippy-driver: + $(LOAD_ENV) + cargo clippy -p vck-driver -p vck-sample-driver -p vck-crypto-test-driver -- -D warnings + build-driver: testing/signing/MyTestDriverCert.cer $(LOAD_ENV) RUSTFLAGS="$(DRIVER_RUSTFLAGS)" cargo build -p vck-sample-driver --target x86_64-pc-windows-msvc --release diff --git a/lib/common/src/handover/payload.rs b/lib/common/src/handover/payload.rs index 834ad44..e765c5d 100644 --- a/lib/common/src/handover/payload.rs +++ b/lib/common/src/handover/payload.rs @@ -26,13 +26,13 @@ pub trait HandoverPayload: Serialize + DeserializeOwned { } pub fn encode_payload(payload: &P) -> VckResult> { - Ok(messagepack_serde::to_vec(payload) - .map_err(|err| crate::VckError::MsgpackEncode(err.to_string()))?) + messagepack_serde::to_vec(payload) + .map_err(|err| crate::VckError::MsgpackEncode(err.to_string())) } pub fn decode_payload(bytes: &[u8]) -> VckResult

{ - Ok(messagepack_serde::from_slice(bytes) - .map_err(|err| crate::VckError::MsgpackDecode(err.to_string()))?) + messagepack_serde::from_slice(bytes) + .map_err(|err| crate::VckError::MsgpackDecode(err.to_string())) } #[cfg(test)] diff --git a/lib/common/src/jvck/codec.rs b/lib/common/src/jvck/codec.rs index fbf9d0a..e74aca3 100644 --- a/lib/common/src/jvck/codec.rs +++ b/lib/common/src/jvck/codec.rs @@ -125,7 +125,7 @@ impl<'a> ReplicaCtx<'a> { /// vendor-specific data region, starting at vendor-relative `rel_sector`. pub fn read_vendor_data(&self, rel_sector: u64, buf: &mut [u8]) -> VckResult<()> { let ss = self.sector_size as usize; - if ss == 0 || buf.is_empty() || buf.len() % ss != 0 { + if ss == 0 || buf.is_empty() || !buf.len().is_multiple_of(ss) { return Err(VckError::InvalidData( "vendor data buffer must be a non-zero multiple of the sector size", )); @@ -133,7 +133,7 @@ impl<'a> ReplicaCtx<'a> { let nsec = (buf.len() / ss) as u64; if rel_sector .checked_add(nsec) - .map_or(true, |end| end > self.vendor_sector_count) + .is_none_or(|end| end > self.vendor_sector_count) { return Err(VckError::ValidationFailed( "vendor data range exceeds the replica region", @@ -157,6 +157,7 @@ pub trait MetadataCodec: Send + Sync { /// Serialize `header` + the sensitive `secrets`/`encrypted_offset`/`state` /// into a 512-byte `out` block (encrypting the inner payload, computing /// auth). `salt` is the per-write random salt. + #[allow(clippy::too_many_arguments)] fn seal( &self, header: &JvckHeader, diff --git a/lib/common/src/jvck/store.rs b/lib/common/src/jvck/store.rs index 9bc76a1..e5a24ee 100644 --- a/lib/common/src/jvck/store.rs +++ b/lib/common/src/jvck/store.rs @@ -398,7 +398,7 @@ impl JvckMetadataStore { len: usize, ) -> VckResult { let ss = self.geometry.sector_size as usize; - if ss == 0 || len == 0 || len % ss != 0 { + if ss == 0 || len == 0 || !len.is_multiple_of(ss) { return Err(VckError::InvalidData( "vendor data buffer must be a non-zero multiple of the sector size", )); @@ -408,7 +408,7 @@ impl JvckMetadataStore { .vendor_data_base_lba(replica_index) .ok_or(VckError::NotFound("vendor data replica index out of range"))?; let count = self.vendor_data_sector_count(); - if rel_sector.checked_add(nsec).map_or(true, |end| end > count) { + if rel_sector.checked_add(nsec).is_none_or(|end| end > count) { return Err(VckError::ValidationFailed( "vendor data range exceeds the replica region", )); @@ -632,7 +632,7 @@ impl JvckMetadataReader { ) -> VckResult<()> { let sector_size = self.geometry.sector_size; let ss = sector_size as usize; - if ss == 0 || buf.is_empty() || buf.len() % ss != 0 { + if ss == 0 || buf.is_empty() || !buf.len().is_multiple_of(ss) { return Err(VckError::InvalidData( "vendor data buffer must be a non-zero multiple of the sector size", )); @@ -649,7 +649,7 @@ impl JvckMetadataReader { let nsec = (buf.len() / ss) as u64; if rel_sector .checked_add(nsec) - .map_or(true, |end| end > rs.saturating_sub(1)) + .is_none_or(|end| end > rs.saturating_sub(1)) { return Err(VckError::ValidationFailed( "vendor data range exceeds the replica region", diff --git a/lib/loader/src/chainload.rs b/lib/loader/src/chainload.rs index abf6e0f..dc03d8d 100644 --- a/lib/loader/src/chainload.rs +++ b/lib/loader/src/chainload.rs @@ -30,7 +30,7 @@ pub fn chainload_next(next_loader: &DevicePath) -> VckResult<()> { LoadImageSource::FromDevicePath { // `next_loader` is our owned `Box`; deref to the borrowed // `&uefi::proto::device_path::DevicePath` the FFI source expects. - device_path: &**next_loader, + device_path: next_loader, boot_policy: BootPolicy::ExactMatch, }, ) diff --git a/lib/loader/src/hook/block_io.rs b/lib/loader/src/hook/block_io.rs index 1a6ac18..7ede522 100644 --- a/lib/loader/src/hook/block_io.rs +++ b/lib/loader/src/hook/block_io.rs @@ -81,6 +81,7 @@ impl BlockIoHook { /// # Safety /// `protocol` must be a live `EFI_BLOCK_IO_PROTOCOL` instance and `engine` a /// pointer that outlives the hook (the loader leaks the engine to `'static`). + #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn install( protocol: *mut BlockIoProtocol, engine: *const BlockIoHookEngine, @@ -111,8 +112,7 @@ impl BlockIoHook { (*self.protocol).read_blocks = entry.original; let entries = &mut *HOOK_TABLE.entries.get(); for slot in entries.iter_mut() { - if matches!(slot, Some(e) if e.protocol == self.protocol as *const BlockIoProtocol) - { + if matches!(slot, Some(e) if core::ptr::eq(e.protocol, self.protocol)) { *slot = None; } } @@ -126,6 +126,11 @@ impl BlockIoHook { /// /// Calls the saved original read to fill `buffer`, then runs the engine's /// per-sector AES-XTS decryption decision over the bytes just read. +/// +/// # Safety +/// Called by UEFI firmware as an `EFI_BLOCK_IO_PROTOCOL.ReadBlocks` callback. +/// `this` must be a valid `BlockIoProtocol` registered in `HOOK_TABLE`. +/// `buffer` must point to at least `buffer_size` writable bytes. pub unsafe extern "efiapi" fn hooked_read_blocks( this: *const BlockIoProtocol, media_id: u32, diff --git a/lib/windrv/src/debug.rs b/lib/windrv/src/debug.rs index 303047a..6124114 100644 --- a/lib/windrv/src/debug.rs +++ b/lib/windrv/src/debug.rs @@ -86,13 +86,7 @@ pub fn panic_print(info: &PanicInfo<'_>) -> ! { let _ = writer.write_str(""); } let _ = writer.write_str(" "); - if let Some(message) = info.payload().downcast_ref::<&str>() { - let _ = writer.write_str(message); - } else if let Some(message) = info.payload().downcast_ref::() { - let _ = writer.write_str(message.as_str()); - } else { - let _ = writer.write_str(""); - } + let _ = write!(&mut writer, "{}", info.message()); if !writer.ends_with_newline() { let _ = writer.write_char('\n'); } diff --git a/lib/windrv/src/device.rs b/lib/windrv/src/device.rs index e5af91d..8828515 100644 --- a/lib/windrv/src/device.rs +++ b/lib/windrv/src/device.rs @@ -110,13 +110,14 @@ impl ControlDevice { /// `security` (supplied by the driver binary) controls the device's SDDL so /// the OS gates who may open it and with what access. Non-exclusive so the /// management app and the driver's own shutdown self-IOCTL can both reach it. + #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn create( driver_object: *mut DRIVER_OBJECT, security: &ControlDeviceSecurity<'_>, ) -> VckResult { - let device_name = UnicodeString::from_str(DEVICE_NAME); - let symlink_name = UnicodeString::from_str(SYMLINK_NAME); - let sddl = UnicodeString::from_str(security.sddl); + let device_name = UnicodeString::new(DEVICE_NAME); + let symlink_name = UnicodeString::new(SYMLINK_NAME); + let sddl = UnicodeString::new(security.sddl); let mut device_object = null_mut(); ntstatus_to_result( @@ -173,7 +174,7 @@ impl ControlDevice { /// Delete the symbolic link and device object. pub fn destroy(self) -> VckResult<()> { - let symlink_name = UnicodeString::from_str(SYMLINK_NAME); + let symlink_name = UnicodeString::new(SYMLINK_NAME); unsafe { IoUnregisterShutdownNotification(self.device_object); } diff --git a/lib/windrv/src/executor.rs b/lib/windrv/src/executor.rs index 5169718..3ae2084 100644 --- a/lib/windrv/src/executor.rs +++ b/lib/windrv/src/executor.rs @@ -22,13 +22,13 @@ impl KernelExecutor { /// Spawn a detached task driven by completion-callback wakers. pub fn spawn + Send + 'static>(&self, fut: F) { - let _ = fut; + drop(fut); todo!("enqueue future, poll on waker from IRP completion") } /// Block the current (PASSIVE_LEVEL) thread until `fut` resolves. pub fn block_on(&self, fut: F) -> F::Output { - let _ = fut; + drop(fut); todo!("poll loop with KeWaitForSingleObject on a waker event") } } diff --git a/lib/windrv/src/filter/handover_mount.rs b/lib/windrv/src/filter/handover_mount.rs index e2781fc..a8eaeac 100644 --- a/lib/windrv/src/filter/handover_mount.rs +++ b/lib/windrv/src/filter/handover_mount.rs @@ -35,11 +35,11 @@ use wdk_sys::{ ExAllocatePool2, ExFreePool, IoAllocateWorkItem, IoFreeWorkItem, IoQueueWorkItem, KeGetCurrentIrql, KeInitializeEvent, KeSetEvent, KeWaitForSingleObject, }, - KEVENT, NTSTATUS, PDEVICE_OBJECT, PIO_WORKITEM, PIRP, SL_PENDING_RETURNED, _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, _MODE::KernelMode, _WORK_QUEUE_TYPE::DelayedWorkQueue, + KEVENT, NTSTATUS, PDEVICE_OBJECT, PIO_WORKITEM, PIRP, SL_PENDING_RETURNED, }; use crate::{ diff --git a/lib/windrv/src/filter/io.rs b/lib/windrv/src/filter/io.rs index 1f1f434..5ee33e0 100644 --- a/lib/windrv/src/filter/io.rs +++ b/lib/windrv/src/filter/io.rs @@ -26,8 +26,6 @@ use wdk_sys::{ SL_INVOKE_ON_ERROR, SL_INVOKE_ON_SUCCESS, SL_PENDING_RETURNED, }; -use core::sync::atomic::Ordering; - use crate::{ device::DeviceExtension, filter::pass_through, nt::nt_success, registry::AttachedVolume, }; diff --git a/lib/windrv/src/filter/manager.rs b/lib/windrv/src/filter/manager.rs index d540484..a10c031 100644 --- a/lib/windrv/src/filter/manager.rs +++ b/lib/windrv/src/filter/manager.rs @@ -52,6 +52,7 @@ use crate::{ /// the FSD. For correct transparent encryption the caller should ensure the /// filter is attached below the FSD (via the `IoRegisterFsRegistrationChange` /// path) or accept size-interception-only semantics. +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn attach_filter( driver: *mut DRIVER_OBJECT, target_nt_path: &str, @@ -74,7 +75,7 @@ pub fn attach_filter( } // Resolve the target volume device (top of its stack right now). - let name = UnicodeString::from_str(target_nt_path); + let name = UnicodeString::new(target_nt_path); let mut file_obj: PFILE_OBJECT = null_mut(); let mut target_do: PDEVICE_OBJECT = null_mut(); let status = unsafe { @@ -132,6 +133,7 @@ pub fn attach_filter( /// `IoAttachDeviceToDeviceStackSafe` still works via `IoGetDeviceObjectPointer`). /// Caller must call [`filter_bind_volume`] to complete setup once the /// `AttachedVolume` is built. If setup fails, call [`detach_filter`] to clean up. +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn attach_filter_unbound( driver: *mut DRIVER_OBJECT, target_nt_path: &str, @@ -152,7 +154,7 @@ pub fn attach_filter_unbound( return Err(VckError::Io("IoCreateDevice(filter) failed".into())); } - let name = UnicodeString::from_str(target_nt_path); + let name = UnicodeString::new(target_nt_path); let mut file_obj: PFILE_OBJECT = null_mut(); let mut target_do: PDEVICE_OBJECT = null_mut(); let status = unsafe { @@ -200,6 +202,7 @@ pub fn attach_filter_unbound( /// instead of resolving one via `IoGetDeviceObjectPointer`. Use this when the /// device object is already known (e.g. obtained from an open file handle) and /// `IoGetDeviceObjectPointer` would fail (e.g. after the filesystem dismounts). +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn attach_filter_to_raw_device( driver: *mut DRIVER_OBJECT, target_do: PDEVICE_OBJECT, @@ -278,6 +281,7 @@ pub unsafe fn filter_bind_volume(filter_do: PDEVICE_OBJECT, volume: Arc` held /// in its extension. +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn detach_filter(filter_do: PDEVICE_OBJECT) { if filter_do.is_null() { return; @@ -412,7 +418,7 @@ where use wdk_sys::ntddk::{IoGetDeviceAttachmentBaseRef, ObfDereferenceObject}; use wdk_sys::{FILE_READ_DATA, PFILE_OBJECT}; - let name = UnicodeString::from_str(nt_path); + let name = UnicodeString::new(nt_path); let mut file_obj: PFILE_OBJECT = null_mut(); let mut vol_do: PDEVICE_OBJECT = null_mut(); let status = unsafe { @@ -447,7 +453,7 @@ where let chars = core::slice::from_raw_parts(name_ptr as *const u16, name_len.min(64)); let mut s = alloc::string::String::new(); for &c in chars { - if c >= 0x20 && c < 0x7F { + if (0x20..0x7F).contains(&c) { s.push(c as u8 as char); } else { s.push('?'); @@ -533,7 +539,7 @@ pub fn find_our_filter_in_stack(nt_path: &str) -> Option<(PDEVICE_OBJECT, PDEVIC use wdk_sys::ntddk::IoGetAttachedDeviceReference; - let name = UnicodeString::from_str(nt_path); + let name = UnicodeString::new(nt_path); let mut file_obj: PFILE_OBJECT = null_mut(); let mut vol_do: PDEVICE_OBJECT = null_mut(); let status = unsafe { diff --git a/lib/windrv/src/filter/volume_thread.rs b/lib/windrv/src/filter/volume_thread.rs index dd50218..c2764f7 100644 --- a/lib/windrv/src/filter/volume_thread.rs +++ b/lib/windrv/src/filter/volume_thread.rs @@ -33,11 +33,11 @@ use wdk_sys::{ IofCompleteRequest, KeInitializeEvent, KeSetEvent, KeWaitForSingleObject, ObReferenceObjectByHandle, ObfDereferenceObject, PsCreateSystemThread, ZwClose, }, - CCHAR, HANDLE, IO_NO_INCREMENT, KEVENT, LARGE_INTEGER, NTSTATUS, PDEVICE_OBJECT, - PIO_STACK_LOCATION, PIRP, SL_PENDING_RETURNED, _EVENT_TYPE::SynchronizationEvent, _KWAIT_REASON::Executive, _MODE::KernelMode, + CCHAR, HANDLE, IO_NO_INCREMENT, KEVENT, LARGE_INTEGER, NTSTATUS, PDEVICE_OBJECT, + PIO_STACK_LOCATION, PIRP, SL_PENDING_RETURNED, }; use crate::{ @@ -94,6 +94,10 @@ unsafe impl Sync for VolumeThread {} impl VolumeThread { /// Create and start the thread for `volume`. Returns a heap box whose raw /// pointer is stored in the filter's DeviceExtension. + /// + /// # Safety + /// Must be called at IRQL PASSIVE_LEVEL. The returned `Box` must outlive the + /// system thread it spawns; call [`VolumeThread::stop`] before dropping. pub unsafe fn start(volume: Arc) -> Box { let mut vt = Box::new(VolumeThread { queue: Mutex::new(VecDeque::new()), @@ -145,11 +149,16 @@ impl VolumeThread { /// Stop the thread, wait for it to exit, and complete any queued IRPs as /// cancelled. Safe to call once; consumes via Box drop afterwards. + /// + /// # Safety + /// Must be called at IRQL PASSIVE_LEVEL. Must not be called more than once + /// on the same `VolumeThread`; the caller must drop the owning `Box` + /// immediately after. pub unsafe fn stop(&self) { self.shutdown.store(true, Ordering::Release); self.signal(); if !self.thread.is_null() { - KeWaitForSingleObject(self.thread, Executive, KernelMode as i8, 0, null_mut()); + let _ = KeWaitForSingleObject(self.thread, Executive, KernelMode as i8, 0, null_mut()); ObfDereferenceObject(self.thread); } } @@ -232,6 +241,11 @@ unsafe fn claim_cancelled(irp: PIRP) -> bool { /// Wake the thread bound to `volume` (e.g. after start_encrypt transitions the /// engine state) so the sweep resumes promptly. +/// +/// # Safety +/// `volume.filter_device` must be a valid filter device object or null. +/// Caller must ensure the filter device and its extension are not concurrently +/// freed. pub unsafe fn wake_for(volume: &AttachedVolume) { let filter_do = volume.filter_device.load(Ordering::Acquire); if filter_do.is_null() { @@ -286,7 +300,7 @@ unsafe extern "C" fn thread_main(context: *mut c_void) { let mut timeout = LARGE_INTEGER { QuadPart: IDLE_TIMEOUT_100NS, }; - KeWaitForSingleObject( + let _ = KeWaitForSingleObject( &vt.wake as *const KEVENT as *mut c_void, Executive, KernelMode as i8, diff --git a/lib/windrv/src/handover.rs b/lib/windrv/src/handover.rs index 7dcd2d5..d5027b3 100644 --- a/lib/windrv/src/handover.rs +++ b/lib/windrv/src/handover.rs @@ -59,7 +59,7 @@ pub fn read_handover() -> VckResult

{ /// Read the raw value of the handover UEFI variable into an owned buffer. fn read_handover_variable(var_name: &str, var_guid: [u8; 16]) -> VckResult> { - let name = UnicodeString::from_str(var_name); + let name = UnicodeString::new(var_name); let mut guid = handover_guid(var_guid); // First call: discover the value length (pass a zero-length buffer). diff --git a/lib/windrv/src/io.rs b/lib/windrv/src/io.rs index 0bb75af..cab9bce 100644 --- a/lib/windrv/src/io.rs +++ b/lib/windrv/src/io.rs @@ -73,7 +73,7 @@ impl KernelVolumeIo { /// Open `volume_id.device_path` (an NT path such as `\??\D:`) for raw /// synchronous I/O and enable extended-DASD access on the handle. pub fn open(volume_id: &VolumeId, sector_size: u32, total_sectors: u64) -> VckResult { - let name = UnicodeString::from_str(&volume_id.device_path); + let name = UnicodeString::new(&volume_id.device_path); let mut oa: OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() }; oa.Length = size_of::() as u32; @@ -400,10 +400,10 @@ use wdk_sys::{ IoBuildDeviceIoControlRequest, IoBuildSynchronousFsdRequest, IofCallDriver, KeInitializeEvent, KeWaitForSingleObject, }, - IRP_MJ_READ, IRP_MJ_WRITE, KEVENT, _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, _MODE::KernelMode, + IRP_MJ_READ, IRP_MJ_WRITE, KEVENT, }; /// IRP-based sector I/O routed directly to `device_object`, bypassing any @@ -591,7 +591,7 @@ impl SectorIo for LowerDeviceIo { /// Open a synchronous kernel handle to `nt_path`. Returns `None` if the open /// fails (caller decides whether that is fatal). pub fn open_volume_handle_raw(nt_path: &str) -> Option { - let name = UnicodeString::from_str(nt_path); + let name = UnicodeString::new(nt_path); let mut oa: OBJECT_ATTRIBUTES = unsafe { core::mem::zeroed() }; oa.Length = size_of::() as u32; oa.ObjectName = name.as_ptr(); @@ -661,6 +661,7 @@ impl vck_common::SectorIo for OffsetSectorIo { } } +#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn send_fsctl(handle: wdk_sys::HANDLE, code: u32) -> i32 { let mut iosb: IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; unsafe { diff --git a/lib/windrv/src/ioctl/dispatch.rs b/lib/windrv/src/ioctl/dispatch.rs index 4aa909d..1152d80 100644 --- a/lib/windrv/src/ioctl/dispatch.rs +++ b/lib/windrv/src/ioctl/dispatch.rs @@ -231,7 +231,7 @@ fn handle_jvck_prepare_adddevice_path( registry: &VolumeAttachRegistry, req: JvckVolumePrepareReq, nt_path: String, - driver: *mut wdk_sys::DRIVER_OBJECT, + _driver: *mut wdk_sys::DRIVER_OBJECT, filter_do: wdk_sys::PDEVICE_OBJECT, lower_do: wdk_sys::PDEVICE_OBJECT, ) -> VckResult { @@ -354,7 +354,7 @@ fn handle_jvck_prepare_adddevice_path( if let Some(h) = open_volume_handle_raw(&nt_path) { let mut iosb: wdk_sys::IO_STATUS_BLOCK = unsafe { core::mem::zeroed() }; unsafe { - ZwDeviceIoControlFile( + let _ = ZwDeviceIoControlFile( h, null_mut(), None, @@ -366,7 +366,7 @@ fn handle_jvck_prepare_adddevice_path( sdn.as_mut_ptr().cast(), 12, ); - ZwDeviceIoControlFile( + let _ = ZwDeviceIoControlFile( h, null_mut(), None, @@ -408,10 +408,9 @@ fn handle_jvck_prepare_adddevice_path( // owns one clone while we keep none here. let probe_io: Arc = Arc::new(LowerDeviceIo::new(lower_do, sector_size, total_sectors)); - let io_config = on_attach_volume(&req.vmk, None, probe_io).map_err(|e| { + let io_config = on_attach_volume(&req.vmk, None, probe_io).inspect_err(|_| { registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); - e })?; let (store_offset, encrypted_offset, offset_store) = io_config.geometry().ok_or( VckError::ValidationFailed("on_attach returned passthrough for a VMK-provided volume"), @@ -980,11 +979,11 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu raw_disk_path, partition_start_lba ); - let disk_io = KernelVolumeIo::open(&disk_id, store_bps, disk_total).map_err(|e| { - registry.remove(&req.volume_path); - crate::filter::detach_filter(filter_do); - e - })?; + let disk_io = + KernelVolumeIo::open(&disk_id, store_bps, disk_total).inspect_err(|_| { + registry.remove(&req.volume_path); + crate::filter::detach_filter(filter_do); + })?; Arc::new(OffsetSectorIo::new( Arc::new(disk_io) as Arc, partition_start_lba, @@ -999,18 +998,16 @@ fn handle_jvck_prepare(registry: &VolumeAttachRegistry, input: &[u8]) -> VckResu raw_partition_path ); Arc::new( - KernelVolumeIo::open(&part_id, store_bps, store_data).map_err(|e| { + KernelVolumeIo::open(&part_id, store_bps, store_data).inspect_err(|_| { registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); - e })?, ) } else { Arc::new( - KernelVolumeIo::open(&io_volume_id, store_bps, store_data).map_err(|e| { + KernelVolumeIo::open(&io_volume_id, store_bps, store_data).inspect_err(|_| { registry.remove(&req.volume_path); crate::filter::detach_filter(filter_do); - e })?, ) }; @@ -1285,7 +1282,11 @@ pub fn detach_volume_with_dismount( QuadPart: DELAY_100MS, }; unsafe { - KeDelayExecutionThread(0 /*KernelMode*/, 0 /*FALSE*/, &mut interval); + let _ = KeDelayExecutionThread( + 0, /*KernelMode*/ + 0, /*FALSE*/ + &mut interval, + ); } } } @@ -1316,7 +1317,7 @@ pub fn detach_volume_with_dismount( QuadPart: -5_000_000, }; // 500 ms unsafe { - KeDelayExecutionThread(0, 0, &mut interval); + let _ = KeDelayExecutionThread(0, 0, &mut interval); } } @@ -1444,7 +1445,7 @@ fn handle_bench_aes(input: &[u8]) -> VckResult { } unsafe { core::ptr::write_bytes(buf, 0u8, CHUNK_BYTES) }; - let chunks = ((size_bytes as usize + CHUNK_BYTES - 1) / CHUNK_BYTES) as u64; + let chunks = (size_bytes as usize).div_ceil(CHUNK_BYTES) as u64; let actual_bytes = chunks * CHUNK_BYTES as u64; let actual_mib = actual_bytes / (1024 * 1024); @@ -1475,16 +1476,8 @@ fn handle_bench_aes(input: &[u8]) -> VckResult { let enc_ticks = unsafe { enc_end.QuadPart - enc_start.QuadPart } as u64; let dec_ticks = unsafe { dec_end.QuadPart - dec_start.QuadPart } as u64; - let encrypt_mib_s = if enc_ticks == 0 { - 0 - } else { - actual_mib * freq_val / enc_ticks - }; - let decrypt_mib_s = if dec_ticks == 0 { - 0 - } else { - actual_mib * freq_val / dec_ticks - }; + let encrypt_mib_s = (actual_mib * freq_val).checked_div(enc_ticks).unwrap_or(0); + let decrypt_mib_s = (actual_mib * freq_val).checked_div(dec_ticks).unwrap_or(0); crate::vck_log!( "bench_aes: size={}MiB enc={}MiB/s dec={}MiB/s", diff --git a/lib/windrv/src/nt.rs b/lib/windrv/src/nt.rs index d61b3a0..6257b63 100644 --- a/lib/windrv/src/nt.rs +++ b/lib/windrv/src/nt.rs @@ -41,7 +41,7 @@ pub struct UnicodeString { } impl UnicodeString { - pub fn from_str(value: &str) -> Self { + pub fn new(value: &str) -> Self { let mut buffer: Vec = value.encode_utf16().collect(); buffer.push(0); diff --git a/lib/windrv/src/ntddk_ex.rs b/lib/windrv/src/ntddk_ex.rs index 341ac7c..0e9ae70 100644 --- a/lib/windrv/src/ntddk_ex.rs +++ b/lib/windrv/src/ntddk_ex.rs @@ -54,6 +54,11 @@ const NORMAL_PAGE_PRIORITY: u32 = 16; /// Returns a pointer to the caller's I/O stack location in the specified IRP. /// /// Equivalent to the `IoGetCurrentIrpStackLocation` macro in ntddk.h. +/// +/// # Safety +/// `irp` must be a valid, fully-initialised IRP. Caller must hold the IRP +/// from a dispatch routine or completion callback and must not use the +/// returned pointer after the IRP has been completed or freed. #[allow(non_snake_case)] pub unsafe fn IoGetCurrentIrpStackLocation(irp: PIRP) -> PIO_STACK_LOCATION { (*irp) @@ -67,6 +72,11 @@ pub unsafe fn IoGetCurrentIrpStackLocation(irp: PIRP) -> PIO_STACK_LOCATION { /// Returns a pointer to the next-lower driver's I/O stack location. /// /// Equivalent to `IoGetNextIrpStackLocation` in ntddk.h. +/// +/// # Safety +/// `irp` must be a valid IRP with at least one additional stack location +/// allocated below the current location (i.e. the IRP stack size must be +/// greater than the current stack index). #[allow(non_snake_case)] pub unsafe fn IoGetNextIrpStackLocation(irp: PIRP) -> PIO_STACK_LOCATION { (*irp) @@ -127,6 +137,9 @@ pub unsafe fn IoSetCancelRoutine(irp: PIRP, routine: DRIVER_CANCEL) -> DRIVER_CA if old_ptr.is_null() { None } else { - Some(core::mem::transmute(old_ptr)) + Some(core::mem::transmute::< + *mut c_void, + unsafe extern "C" fn(*mut wdk_sys::_DEVICE_OBJECT, *mut wdk_sys::_IRP), + >(old_ptr)) } } diff --git a/sample/loader/src/main.rs b/sample/loader/src/main.rs index 5a83ddb..3e1769d 100644 --- a/sample/loader/src/main.rs +++ b/sample/loader/src/main.rs @@ -14,6 +14,7 @@ //! 4. install the transparent Block IO decrypt hook (handing it the cipher); //! 5. publish the ACPI/UEFI-variable handover for the driver; //! 6. chainload the next OS loader. +//! //! See docs/architecture.md "sample/loader" and "시스템 볼륨 부팅 흐름". #![no_std] diff --git a/sample/windrv/src/lib.rs b/sample/windrv/src/lib.rs index 81b6b9a..8554b50 100644 --- a/sample/windrv/src/lib.rs +++ b/sample/windrv/src/lib.rs @@ -33,12 +33,11 @@ use wdk_sys::{ IoBuildDeviceIoControlRequest, IoGetRequestorProcess, IofCallDriver, IofCompleteRequest, KeExpandKernelStackAndCallout, KeInitializeEvent, KeWaitForSingleObject, }, - CCHAR, DRIVER_OBJECT, IO_NO_INCREMENT, IO_STATUS_BLOCK, IRP_MJ_CLEANUP, IRP_MJ_CLOSE, - IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL, IRP_MJ_SHUTDOWN, KEVENT, NTSTATUS, PCUNICODE_STRING, - PDEVICE_OBJECT, PDRIVER_OBJECT, PIO_STACK_LOCATION, PIRP, _EVENT_TYPE::NotificationEvent, _KWAIT_REASON::Executive, - _MODE, + _MODE, CCHAR, DRIVER_OBJECT, IO_NO_INCREMENT, IO_STATUS_BLOCK, IRP_MJ_CLEANUP, IRP_MJ_CLOSE, + IRP_MJ_CREATE, IRP_MJ_DEVICE_CONTROL, IRP_MJ_SHUTDOWN, KEVENT, NTSTATUS, PCUNICODE_STRING, + PDEVICE_OBJECT, PDRIVER_OBJECT, PIO_STACK_LOCATION, PIRP, }; #[global_allocator] @@ -140,7 +139,7 @@ pub unsafe extern "system" fn DriverEntry( // Publish the registry so the filter's PnP work item (a C callback that only // receives a device object) can reach it to auto-attach the OS volume. - vck_driver::set_global_registry(&*REGISTRY); + vck_driver::set_global_registry(®ISTRY); // Best-effort: read the boot ACPI handover (`VCKD` table) published by the // UEFI loader. Absent (NotFound) when booting without the loader — the OS @@ -204,7 +203,7 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> let chars = core::slice::from_raw_parts(name_ptr as *const u16, name_len.min(64)); let mut s = alloc::string::String::new(); for &c in chars { - if c >= 0x20 && c < 0x7F { + if (0x20..0x7F).contains(&c) { s.push(c as u8 as char); } else { s.push('?'); From a5af9f0239c4d8b2f578b6ccf79cfda0a870b505 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 12:29:59 +0900 Subject: [PATCH 3/9] rename vck-driver to vck-windrv --- .github/workflows/cicd.yml | 8 ++--- Cargo.lock | 36 +++++++++---------- Makefile | 6 ++-- docs/build-and-test.md | 4 +-- lib/windrv/Cargo.toml | 2 +- lib/windrv/src/debug.rs | 6 ++-- sample/crypto-test/Cargo.toml | 2 +- sample/windrv/Cargo.toml | 4 +-- sample/windrv/src/lib.rs | 2 +- .../data-volume-decrypt.yaml | 6 ++-- testing/recipes/data-volume/data-volume.yaml | 6 ++-- testing/recipes/os-encrypt/os-encrypt.yaml | 6 ++-- 12 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 5d6bc71..f785f6b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -88,7 +88,7 @@ jobs: - name: clippy (vck-common, host) run: cargo clippy -p vck-common -- -D warnings - - name: clippy (vck-driver) + - name: clippy (vck-windrv) shell: msys2 {0} run: | make clippy-driver @@ -107,7 +107,7 @@ jobs: - uses: actions/upload-artifact@v7 with: name: vck-artifacts-${{ github.run_id }} - # vck-sample-driver.sys, vck-driver-pkg/, vck-sample-loader.efi, vck-app.exe + # vck-sample-driver.sys, vck-windrv-pkg/, vck-sample-loader.efi, vck-app.exe path: testing/artifacts/ # ── VM tests (parallel matrix) ───────────────────────────────────────────── @@ -213,11 +213,11 @@ jobs: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} run: cargo publish -p vck-common - - name: Publish vck-driver + - name: Publish vck-windrv shell: msys2 {0} env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} - run: cargo publish -p vck-driver + run: cargo publish -p vck-windrv - name: Publish vck-loader shell: msys2 {0} diff --git a/Cargo.lock b/Cargo.lock index 381f3f3..dab203f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,28 +1045,12 @@ name = "vck-crypto-test-driver" version = "0.0.1" dependencies = [ "vck-common", - "vck-driver", + "vck-windrv", "wdk-alloc", "wdk-build", "wdk-sys", ] -[[package]] -name = "vck-driver" -version = "0.0.1" -dependencies = [ - "aes", - "irql", - "messagepack-serde", - "serde", - "serde_bytes", - "spin", - "vck-common", - "wdk-build", - "wdk-sys", - "xts-mode", -] - [[package]] name = "vck-loader" version = "0.0.1" @@ -1094,8 +1078,8 @@ version = "0.0.1" dependencies = [ "spin", "vck-common", - "vck-driver", "vck-sample-common", + "vck-windrv", "wdk-alloc", "wdk-build", "wdk-sys", @@ -1112,6 +1096,22 @@ dependencies = [ "vck-sample-common", ] +[[package]] +name = "vck-windrv" +version = "0.0.1" +dependencies = [ + "aes", + "irql", + "messagepack-serde", + "serde", + "serde_bytes", + "spin", + "vck-common", + "wdk-build", + "wdk-sys", + "xts-mode", +] + [[package]] name = "version_check" version = "0.9.5" diff --git a/Makefile b/Makefile index 9a210ea..65f993b 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ UEFI_RUSTFLAGS = -C target-feature=-soft-float clippy-driver: $(LOAD_ENV) - cargo clippy -p vck-driver -p vck-sample-driver -p vck-crypto-test-driver -- -D warnings + cargo clippy -p vck-windrv -p vck-sample-driver -p vck-crypto-test-driver -- -D warnings build-driver: testing/signing/MyTestDriverCert.cer $(LOAD_ENV) @@ -50,7 +50,7 @@ build-driver: testing/signing/MyTestDriverCert.cer powershell -NoProfile -ExecutionPolicy Bypass -File ./testing/signing/sign-driver-package.ps1 \ -DriverSys ./testing/artifacts/vck-sample-driver.sys \ -DriverInf ./testing/artifacts/vck-sample-driver.inf \ - -OutputDir ./testing/artifacts/vck-driver-pkg + -OutputDir ./testing/artifacts/vck-windrv-pkg build-crypto-test-driver: testing/signing/MyTestDriverCert.cer $(LOAD_ENV) @@ -77,7 +77,7 @@ build-driver-package: testing/signing/MyTestDriverCert.cer testing/artifacts/vck powershell -NoProfile -ExecutionPolicy Bypass -File ./testing/signing/sign-driver-package.ps1 \ -DriverSys ./testing/artifacts/vck-sample-driver.sys \ -DriverInf ./testing/artifacts/vck-sample-driver.inf \ - -OutputDir ./testing/artifacts/vck-driver-pkg + -OutputDir ./testing/artifacts/vck-windrv-pkg test-vm-smoke: $(TEST_VM_DIR) test-foundry.exe --vm-name=win11 test --headless --output ./testing/results/smoke-guest-exec --test ./testing/recipes/smoke-guest-exec/smoke.yaml diff --git a/docs/build-and-test.md b/docs/build-and-test.md index f3ddc9f..d37b309 100644 --- a/docs/build-and-test.md +++ b/docs/build-and-test.md @@ -33,7 +33,7 @@ SPDX-License-Identifier: Apache-2.0 | `make test-vm-os-encrypt` | OS 볼륨 암호화→로더 경유 부팅→런타임 복호화 E2E | win11 VM | 드라이버 빌드 검증은 반드시 드라이버 바이너리 크레이트(`vck-sample-driver`)를 통해 합니다 -(`cargo build -p vck-driver` 단독은 `wdk-sys` 바인딩이 비어 빌드되지 않음). +(`cargo build -p vck-windrv` 단독은 `wdk-sys` 바인딩이 비어 빌드되지 않음). 예시(msys2 셸): @@ -66,7 +66,7 @@ qemu: - "isa-debugcon,iobase=0xe9,chardev=debugout" ``` -드라이버와 로더는 동일한 `vck_log!` 매크로로 `{timestamp} vck-driver: …` / `{timestamp} vck-loader: …` +드라이버와 로더는 동일한 `vck_log!` 매크로로 `{timestamp} vck-windrv: …` / `{timestamp} vck-loader: …` 형식의 줄을 이 포트에 출력합니다(`lib/windrv/src/debug.rs`, `lib/loader/src/debug.rs`). ### 크래시 덤프 분석 (cdb) diff --git a/lib/windrv/Cargo.toml b/lib/windrv/Cargo.toml index a0bac3b..063da7a 100644 --- a/lib/windrv/Cargo.toml +++ b/lib/windrv/Cargo.toml @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 [package] -name = "vck-driver" +name = "vck-windrv" version.workspace = true edition.workspace = true license.workspace = true diff --git a/lib/windrv/src/debug.rs b/lib/windrv/src/debug.rs index 6124114..04a0135 100644 --- a/lib/windrv/src/debug.rs +++ b/lib/windrv/src/debug.rs @@ -28,7 +28,7 @@ fn get_timestamp() -> (u64, u32) { pub fn debug_print(args: fmt::Arguments<'_>) { let (secs, millis) = get_timestamp(); let mut line = String::new(); - let _ = write!(&mut line, "{secs}.{millis:03} vck-driver: "); + let _ = write!(&mut line, "{secs}.{millis:03} vck-windrv: "); if line.write_fmt(args).is_err() { return; } @@ -73,7 +73,7 @@ pub fn remaining_stack() -> u64 { pub fn panic_print(info: &PanicInfo<'_>) -> ! { let mut writer = PanicWriter::new(); let (secs, millis) = get_timestamp(); - let _ = write!(&mut writer, "{secs}.{millis:03} vck-driver: panic: "); + let _ = write!(&mut writer, "{secs}.{millis:03} vck-windrv: panic: "); if let Some(location) = info.location() { let _ = write!( &mut writer, @@ -165,7 +165,7 @@ fn write_debugcon(bytes: &[u8]) { #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] fn write_debugcon(_bytes: &[u8]) {} -/// Print a timestamped, `vck-driver:`-prefixed line to the kernel debugger +/// Print a timestamped, `vck-windrv:`-prefixed line to the kernel debugger /// (`DbgPrint`) and, with the `debugcon` feature, the 0xE9 debug console. /// /// This is the driver-side counterpart of the loader's `vck_log!` diff --git a/sample/crypto-test/Cargo.toml b/sample/crypto-test/Cargo.toml index 2e780ae..bc7cc6b 100644 --- a/sample/crypto-test/Cargo.toml +++ b/sample/crypto-test/Cargo.toml @@ -20,6 +20,6 @@ crate-type = ["cdylib"] [dependencies] vck-common = { path = "../../lib/common", default-features = false } -vck-driver = { path = "../../lib/windrv" } +vck-windrv = { path = "../../lib/windrv" } wdk-alloc.workspace = true wdk-sys = "0.5.1" diff --git a/sample/windrv/Cargo.toml b/sample/windrv/Cargo.toml index 195d637..157c253 100644 --- a/sample/windrv/Cargo.toml +++ b/sample/windrv/Cargo.toml @@ -21,12 +21,12 @@ crate-type = ["cdylib"] [features] # Always enable debugcon so VM test logs are always captured. default = ["debugcon"] -debugcon = ["vck-driver/debugcon"] +debugcon = ["vck-windrv/debugcon"] [dependencies] spin = { workspace = true, features = ["lazy", "once", "mutex", "spin_mutex"] } vck-common = { path = "../../lib/common", default-features = false } -vck-driver = { path = "../../lib/windrv" } +vck-windrv = { path = "../../lib/windrv" } vck-sample-common = { path = "../common" } wdk-alloc.workspace = true wdk-sys = "0.5.1" diff --git a/sample/windrv/src/lib.rs b/sample/windrv/src/lib.rs index 8554b50..a8742ca 100644 --- a/sample/windrv/src/lib.rs +++ b/sample/windrv/src/lib.rs @@ -4,7 +4,7 @@ //! `vck-sample-driver`: the JVCK sample kernel driver (builds to a `.sys`). //! -//! Wires the sample `VckVolumeProvider` into the `vck-driver` framework. +//! Wires the sample `VckVolumeProvider` into the `vck-windrv` framework. #![no_std] #![allow(non_snake_case)] diff --git a/testing/recipes/data-volume-decrypt/data-volume-decrypt.yaml b/testing/recipes/data-volume-decrypt/data-volume-decrypt.yaml index 1d73c8f..7bffd67 100644 --- a/testing/recipes/data-volume-decrypt/data-volume-decrypt.yaml +++ b/testing/recipes/data-volume-decrypt/data-volume-decrypt.yaml @@ -45,17 +45,17 @@ steps: - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.sys" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.sys" dst: "C:\\Temp\\vck-sample-driver.sys" - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.inf" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.inf" dst: "C:\\Temp\\vck-sample-driver.inf" - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.cat" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.cat" dst: "C:\\Temp\\vck-sample-driver.cat" - action: file-upload timeout: 30s diff --git a/testing/recipes/data-volume/data-volume.yaml b/testing/recipes/data-volume/data-volume.yaml index 5c6e6d2..23fbd32 100644 --- a/testing/recipes/data-volume/data-volume.yaml +++ b/testing/recipes/data-volume/data-volume.yaml @@ -32,7 +32,7 @@ steps: action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.sys" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.sys" dst: "C:\\Temp\\vck-sample-driver.sys" - id: "upload-driver-inf" @@ -40,7 +40,7 @@ steps: action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.inf" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.inf" dst: "C:\\Temp\\vck-sample-driver.inf" - id: "upload-driver-cat" @@ -48,7 +48,7 @@ steps: action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.cat" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.cat" dst: "C:\\Temp\\vck-sample-driver.cat" - id: "upload-vck-app" diff --git a/testing/recipes/os-encrypt/os-encrypt.yaml b/testing/recipes/os-encrypt/os-encrypt.yaml index 49ed849..09fecb2 100644 --- a/testing/recipes/os-encrypt/os-encrypt.yaml +++ b/testing/recipes/os-encrypt/os-encrypt.yaml @@ -47,17 +47,17 @@ steps: - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.sys" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.sys" dst: "C:\\Temp\\vck-sample-driver.sys" - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.inf" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.inf" dst: "C:\\Temp\\vck-sample-driver.inf" - action: file-upload timeout: 30s params: - src: "./testing/artifacts/vck-driver-pkg/vck-sample-driver.cat" + src: "./testing/artifacts/vck-windrv-pkg/vck-sample-driver.cat" dst: "C:\\Temp\\vck-sample-driver.cat" - action: file-upload timeout: 30s From 69e9f3f816b5ed3f21870c804da2a59a178cdf37 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 13:21:06 +0900 Subject: [PATCH 4/9] docs: add description --- lib/common/Cargo.toml | 1 + lib/loader/Cargo.toml | 1 + lib/windrv/Cargo.toml | 1 + 3 files changed, 3 insertions(+) diff --git a/lib/common/Cargo.toml b/lib/common/Cargo.toml index f0f1713..f845951 100644 --- a/lib/common/Cargo.toml +++ b/lib/common/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +description = "Shared types, JVCK metadata format, and crypto primitives for VolumeCrypt-Kit" [features] default = ["std"] diff --git a/lib/loader/Cargo.toml b/lib/loader/Cargo.toml index 79e18ee..4205b01 100644 --- a/lib/loader/Cargo.toml +++ b/lib/loader/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +description = "UEFI pre-boot loader framework for VolumeCrypt-Kit" [dependencies] vck-common = { path = "../common", default-features = false, features = ["uefi"] } diff --git a/lib/windrv/Cargo.toml b/lib/windrv/Cargo.toml index 063da7a..f554513 100644 --- a/lib/windrv/Cargo.toml +++ b/lib/windrv/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +description = "Windows kernel-mode driver framework for VolumeCrypt-Kit" [build-dependencies] wdk-build.workspace = true From d1c8a3abc2a57cf7ab20198ec4963cb96bf509e1 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 13:25:53 +0900 Subject: [PATCH 5/9] fix: Cargo.toml --- Cargo.toml | 2 ++ lib/loader/Cargo.toml | 3 ++- lib/windrv/Cargo.toml | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c24b2c0..6f98349 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,3 +49,5 @@ xts-mode = { version = "0.5.1", default-features = false } zeroize = { version = "1", default-features = false, features = ["alloc", "derive"] } wdk-alloc = "0.4.1" wdk-build = "0.5.1" + +vck-common = { version = "0.0.1", path = "lib/common", default-features = false } diff --git a/lib/loader/Cargo.toml b/lib/loader/Cargo.toml index 4205b01..7ea4a7f 100644 --- a/lib/loader/Cargo.toml +++ b/lib/loader/Cargo.toml @@ -11,7 +11,8 @@ repository.workspace = true description = "UEFI pre-boot loader framework for VolumeCrypt-Kit" [dependencies] -vck-common = { path = "../common", default-features = false, features = ["uefi"] } +vck-common = { workspace = true, default-features = false, features = ["uefi"] } + uefi = { version = "0.37", default-features = false } # Raw protocol structs (BlockIoProtocol) for vtable patching in the read hook. uefi-raw = "0.14" diff --git a/lib/windrv/Cargo.toml b/lib/windrv/Cargo.toml index f554513..47db0bd 100644 --- a/lib/windrv/Cargo.toml +++ b/lib/windrv/Cargo.toml @@ -19,6 +19,8 @@ std = [] debugcon = [] [dependencies] +vck-common = { workspace = true, default-features = false } + aes.workspace = true irql.workspace = true messagepack-serde = { workspace = true, default-features = false } @@ -26,5 +28,4 @@ serde = { workspace = true, default-features = false } serde_bytes = { workspace = true } spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex"] } xts-mode.workspace = true -vck-common = { path = "../common", default-features = false } wdk-sys = "0.5.1" From 3b8a8514ebb008645af8d0406e2edfe3692bd103 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 13:28:49 +0900 Subject: [PATCH 6/9] fix --- .github/workflows/cicd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f785f6b..8e2d047 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -217,10 +217,10 @@ jobs: shell: msys2 {0} env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} - run: cargo publish -p vck-windrv + run: cargo publish -p vck-windrv --no-verify - name: Publish vck-loader shell: msys2 {0} env: CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} - run: cargo publish -p vck-loader + run: cargo publish -p vck-loader --no-verify From 70fbf90947a254026e8f58b4c6575139c171be55 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 18:57:17 +0900 Subject: [PATCH 7/9] chore: update deps --- Cargo.lock | 166 ++++++++++++++++++++++++----------------------------- Cargo.toml | 18 +++--- 2 files changed, 85 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dab203f..dd7527e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,12 +4,12 @@ version = 4 [[package]] name = "aes" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" dependencies = [ - "cfg-if", "cipher", + "cpubits", "cpufeatures", ] @@ -118,28 +118,22 @@ checksum = "84d7ced0ae9557296835c32bf1b1e02b44c746701f898460fb000d7eaa84f00a" [[package]] name = "block-buffer" -version = "0.10.4" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] name = "block-padding" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068b" dependencies = [ - "generic-array", + "hybrid-array", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "camino" version = "1.2.2" @@ -169,14 +163,14 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror", ] [[package]] name = "cbc" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +checksum = "ce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896" dependencies = [ "cipher", ] @@ -208,9 +202,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cipher" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c" dependencies = [ "crypto-common", "inout", @@ -277,17 +271,29 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +[[package]] +name = "cmov" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a" + [[package]] name = "colorchoice" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" -version = "0.2.17" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" dependencies = [ "libc", ] @@ -309,23 +315,31 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "generic-array", - "typenum", + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", ] [[package]] name = "digest" -version = "0.10.7" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer", "crypto-common", - "subtle", + "ctutils", ] [[package]] @@ -360,16 +374,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "glob" version = "0.3.3" @@ -384,30 +388,39 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hkdf" -version = "0.12.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ "digest", ] +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "inout" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" dependencies = [ "block-padding", - "generic-array", + "hybrid-array", ] [[package]] @@ -758,9 +771,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.9" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures", @@ -801,16 +814,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "strsim" -version = "0.11.1" +name = "spin" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +checksum = "1527984ca054dfca79333baec451042863f485fbee01b7bf6d911de915cac865" [[package]] -name = "subtle" -version = "2.6.1" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" @@ -823,33 +836,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1032,8 +1025,8 @@ dependencies = [ "messagepack-serde", "serde", "sha2", - "spin", - "thiserror 1.0.69", + "spin 0.12.0", + "thiserror", "uefi", "uuid", "xts-mode", @@ -1076,7 +1069,7 @@ dependencies = [ name = "vck-sample-driver" version = "0.0.1" dependencies = [ - "spin", + "spin 0.12.0", "vck-common", "vck-sample-common", "vck-windrv", @@ -1105,19 +1098,13 @@ dependencies = [ "messagepack-serde", "serde", "serde_bytes", - "spin", + "spin 0.9.8", "vck-common", "wdk-build", "wdk-sys", "xts-mode", ] -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wdk-alloc" version = "0.4.1" @@ -1149,7 +1136,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror", "tracing", "windows", ] @@ -1184,7 +1171,7 @@ dependencies = [ "cfg-if", "rustversion", "serde_json", - "thiserror 2.0.18", + "thiserror", "tracing", "tracing-subscriber", "wdk-build", @@ -1345,11 +1332,10 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "xts-mode" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520" +checksum = "e2acb658219ff8afdcedc1ea506709b2b32812719eb6ac36f47ae43f50404157" dependencies = [ - "byteorder", "cipher", ] diff --git a/Cargo.toml b/Cargo.toml index 6f98349..361d73c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,21 +31,21 @@ panic = "abort" panic = "abort" [workspace.dependencies] -aes = { version = "0.8", default-features = false } -cbc = { version = "0.1", default-features = false } -cipher = { version = "0.4", default-features = false, features = ["block-padding"] } +aes = { version = "0.9", default-features = false } +cbc = { version = "0.2", default-features = false } +cipher = { version = "0.5", default-features = false, features = ["block-padding"] } crc = { version = "3", default-features = false } -hkdf = { version = "0.12", default-features = false } -hmac = { version = "0.12", default-features = false } +hkdf = { version = "0.13", default-features = false } +hmac = { version = "0.13", default-features = false } irql = { version = "0.1.6" } messagepack-serde = { version = "0.2", default-features = false, features = ["alloc"] } serde = { version = "1", default-features = false, features = ["derive", "alloc"] } serde_bytes = { version = "0.11", default-features = false, features = ["alloc"] } -sha2 = { version = "0.10", default-features = false, features = ["force-soft"] } -spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex"] } -thiserror = { version = "1", default-features = false } +sha2 = { version = "0.11", default-features = false } +spin = { version = "0.12", default-features = false, features = ["mutex", "spin_mutex"] } +thiserror = { version = "2", default-features = false } uuid = { version = "1", default-features = false, features = ["serde"] } -xts-mode = { version = "0.5.1", default-features = false } +xts-mode = { version = "0.6.0", default-features = false } zeroize = { version = "1", default-features = false, features = ["alloc", "derive"] } wdk-alloc = "0.4.1" wdk-build = "0.5.1" From a890c2b1aaf0243564a28ddb6175c0ffcd0f60e1 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 21:04:42 +0900 Subject: [PATCH 8/9] fix --- Makefile | 4 ++- sample/crypto-test/src/lib.rs | 4 +-- sample/crypto-test/src/tests.rs | 2 +- sample/windrv/src/lib.rs | 46 ++++++++++++++++----------------- sample/windrv/src/provider.rs | 4 +-- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Makefile b/Makefile index 65f993b..905a106 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ SHELL := /bin/bash .SHELLFLAGS := -eu -o pipefail -c -.PHONY: build-common build-driver build-driver-package build-crypto-test-driver build-loader build-app test $(TEST_VM_DIR) test-vm-smoke test-vm-driver-load test-vm-os-volume-prepare test-vm-data-volume test-vm-data-volume-decrypt test-vm-crypto-test test-vm-os-handover test-vm-os-encrypt clean +.PHONY: build-common build-driver build-driver-package build-crypto-test-driver build-loader build-app test test-vm-setup test-vm-smoke test-vm-driver-load test-vm-os-volume-prepare test-vm-data-volume test-vm-data-volume-decrypt test-vm-crypto-test test-vm-os-handover test-vm-os-encrypt clean TEST_VM_DIR = .testfoundry/win11 LOAD_ENV := source ./.ci/scripts/load-wdk-env.sh @@ -72,6 +72,8 @@ test: $(TEST_VM_DIR): testing/signing/MyTestDriverCert.cer test-foundry.exe --vm-name="win11" vm-setup --image ./testing/images/windows-11.yaml +test-vm-setup: $(TEST_VM_DIR) + build-driver-package: testing/signing/MyTestDriverCert.cer testing/artifacts/vck-sample-driver.sys testing/artifacts/vck-sample-driver.inf build-driver $(LOAD_ENV) powershell -NoProfile -ExecutionPolicy Bypass -File ./testing/signing/sign-driver-package.ps1 \ diff --git a/sample/crypto-test/src/lib.rs b/sample/crypto-test/src/lib.rs index f3ee676..463c171 100644 --- a/sample/crypto-test/src/lib.rs +++ b/sample/crypto-test/src/lib.rs @@ -33,7 +33,7 @@ pub unsafe extern "system" fn DriverEntry( ) -> NTSTATUS { let _ = (driver, registry_path); let report = tests::run_all(); - vck_driver::vck_log!( + vck_windrv::vck_log!( "crypto-test: {} passed, {} failed", report.passed, report.failed @@ -45,5 +45,5 @@ pub unsafe extern "system" fn DriverEntry( #[panic_handler] fn panic(info: &PanicInfo<'_>) -> ! { - vck_driver::debug::panic_print(info) + vck_windrv::debug::panic_print(info) } diff --git a/sample/crypto-test/src/tests.rs b/sample/crypto-test/src/tests.rs index 73d1371..0bd777d 100644 --- a/sample/crypto-test/src/tests.rs +++ b/sample/crypto-test/src/tests.rs @@ -49,6 +49,6 @@ fn check_encrypted_metadata_roundtrip() -> bool { /// AES-XTS encrypt then decrypt a sector returns the original plaintext. fn check_aes_xts_sector_roundtrip() -> bool { - // TODO(crypto-test): vck_driver::crypto::AesXtsCipher encrypt/decrypt. + // TODO(crypto-test): vck_windrv::crypto::AesXtsCipher encrypt/decrypt. false } diff --git a/sample/windrv/src/lib.rs b/sample/windrv/src/lib.rs index a8742ca..c17405a 100644 --- a/sample/windrv/src/lib.rs +++ b/sample/windrv/src/lib.rs @@ -18,7 +18,7 @@ use core::ffi::c_void; use core::panic::PanicInfo; use spin::{Lazy, Mutex}; -use vck_driver::{ +use vck_windrv::{ device::{ControlDevice, DeviceExtension, DEVICE_KIND_FILTER}, filter::handle_filter_irp, ioctl::codes::{IOCTL_VCK_DETACH_ALL_VOLUMES, IOCTL_VCK_PAUSE_OS_VOLUME}, @@ -67,7 +67,7 @@ struct ExpandCtx { requestor_token: *const AccessToken, input_ptr: *const u8, input_len: usize, - result: Option>>, + result: Option>>, } /// Runs the actual IOCTL dispatch on the expanded stack. @@ -96,8 +96,8 @@ pub unsafe extern "system" fn DriverEntry( registry_path: PCUNICODE_STRING, ) -> NTSTATUS { let _ = registry_path; - vck_driver::vck_log!("DriverEntry: vck-sample-driver loading"); - vck_driver::vck_log!( + vck_windrv::vck_log!("DriverEntry: vck-sample-driver loading"); + vck_windrv::vck_log!( "DriverEntry: AES-NI {}", if vck_common::cpu::has_aes_ni() { "supported" @@ -107,11 +107,11 @@ pub unsafe extern "system" fn DriverEntry( ); // Install the kernel RNG so the JVCK metadata store can generate a fresh // per-write salt when persisting encrypted_offset. - vck_common::set_random_source(&vck_driver::rng::KERNEL_RNG); + vck_common::set_random_source(&vck_windrv::rng::KERNEL_RNG); // Install the sample's volume bind policy. on_attach opens + decrypts the // metadata and builds the cipher; the framework owns no crypto policy. Reached // from both the boot OS-volume mount (PnP callback) and IOCTL attach. - vck_driver::set_volume_provider(&PROVIDER); + vck_windrv::set_volume_provider(&PROVIDER); let driver = match driver.as_mut() { Some(driver) => driver, None => return STATUS_INVALID_PARAMETER, @@ -125,7 +125,7 @@ pub unsafe extern "system" fn DriverEntry( *CONTROL_DEVICE.lock() = Some(control_device); } Err(err) => { - vck_driver::vck_log!("sample-driver: control device create failed: {}", err); + vck_windrv::vck_log!("sample-driver: control device create failed: {}", err); return STATUS_UNSUCCESSFUL; } } @@ -139,24 +139,24 @@ pub unsafe extern "system" fn DriverEntry( // Publish the registry so the filter's PnP work item (a C callback that only // receives a device object) can reach it to auto-attach the OS volume. - vck_driver::set_global_registry(®ISTRY); + vck_windrv::set_global_registry(®ISTRY); // Best-effort: read the boot ACPI handover (`VCKD` table) published by the // UEFI loader. Absent (NotFound) when booting without the loader — the OS // volume auto-attach path then stays dormant and data volumes are unaffected. - match vck_driver::handover::read_handover::() { + match vck_windrv::handover::read_handover::() { Ok(payload) => { - vck_driver::vck_log!( + vck_windrv::vck_log!( "DriverEntry: loader handover present, OS partition {}", payload.partition_guid ); - REGISTRY.set_handover(vck_driver::HandoverInfo { + REGISTRY.set_handover(vck_windrv::HandoverInfo { partition_guid: payload.partition_guid, vmk: payload.vmk, }); } Err(err) => { - vck_driver::vck_log!("DriverEntry: no ACPI handover ({})", err); + vck_windrv::vck_log!("DriverEntry: no ACPI handover ({})", err); } } @@ -217,14 +217,14 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> alloc::string::String::new() } }; - vck_driver::vck_log!("add_device: pdo={:p} name={}", pdo, pdo_name); + vck_windrv::vck_log!("add_device: pdo={:p} name={}", pdo, pdo_name); // Attach an UNBOUND filter to the physical device object before any FSD mounts. // The filter has volume=NULL → all IRPs pass through transparently. // IOCTL_JVCK_PREPARE will later find this filter by name in the PDO map. - match vck_driver::filter::attach_filter_to_raw_device(driver, pdo) { + match vck_windrv::filter::attach_filter_to_raw_device(driver, pdo) { Ok((filter_do, lower_do)) => { - vck_driver::vck_log!( + vck_windrv::vck_log!( "add_device: filter attached filter={:p} lower={:p}", filter_do, lower_do @@ -233,7 +233,7 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> STATUS_SUCCESS } Err(err) => { - vck_driver::vck_log!("add_device: attach failed: {}", err); + vck_windrv::vck_log!("add_device: attach failed: {}", err); STATUS_SUCCESS // Non-fatal: device still works without filter } } @@ -241,7 +241,7 @@ unsafe extern "C" fn add_device(driver: PDRIVER_OBJECT, pdo: PDEVICE_OBJECT) -> #[panic_handler] fn panic(info: &PanicInfo<'_>) -> ! { - vck_driver::debug::panic_print(info) + vck_windrv::debug::panic_print(info) } /// Send a no-input/no-output IOCTL to our own control device synchronously @@ -293,7 +293,7 @@ unsafe fn driver_shutdown() { let device = CONTROL_DEVICE.lock().as_ref().map(|cd| cd.device_object()); if let Some(device) = device { let st = send_self_ioctl(device, IOCTL_VCK_DETACH_ALL_VOLUMES); - vck_driver::vck_log!("driver_shutdown: detach-all status=0x{:08x}", st); + vck_windrv::vck_log!("driver_shutdown: detach-all status=0x{:08x}", st); } } @@ -305,7 +305,7 @@ unsafe extern "C" fn driver_unload(_driver: PDRIVER_OBJECT) { // Unloading would leave C: reading raw ciphertext → guaranteed corruption. // Refuse by bugchecking with STATUS_INVALID_DEVICE_STATE as a parameter. if REGISTRY.has_encrypted_os_volume() { - vck_driver::vck_log!("sample-driver: unload refused — OS volume encrypted; bugchecking"); + vck_windrv::vck_log!("sample-driver: unload refused — OS volume encrypted; bugchecking"); const STATUS_INVALID_DEVICE_STATE: u64 = 0xC000_0184; // Custom bug check code "VCK\0" — identifiable in the crash dump. P1 is // STATUS_INVALID_DEVICE_STATE indicating why the unload was refused. @@ -316,7 +316,7 @@ unsafe extern "C" fn driver_unload(_driver: PDRIVER_OBJECT) { // Cleanup: tear down the control device. if let Some(control_device) = CONTROL_DEVICE.lock().take() { if let Err(err) = control_device.destroy() { - vck_driver::vck_log!("sample-driver: control device destroy failed: {}", err); + vck_windrv::vck_log!("sample-driver: control device destroy failed: {}", err); } } } @@ -353,7 +353,7 @@ unsafe extern "C" fn dispatch_any(device_object: PDEVICE_OBJECT, irp: PIRP) -> N STATUS_SUCCESS } IRP_MJ_SHUTDOWN => { - vck_driver::vck_log!("sample-driver: IRP_MJ_SHUTDOWN"); + vck_windrv::vck_log!("sample-driver: IRP_MJ_SHUTDOWN"); // 1. Pause the OS volume sweep (stops the boundary advancing; waits // for any in-flight batch). The OS volume filter stays bound so // live shutdown writes remain encrypted. @@ -435,7 +435,7 @@ unsafe extern "C" fn dispatch_device_control( }; let dispatch_result = if callout_status >= 0 { ex.result.take().unwrap_or_else(|| { - Err(vck_driver::VckError::Io( + Err(vck_windrv::VckError::Io( "dispatch callout did not run".into(), )) }) @@ -472,7 +472,7 @@ unsafe extern "C" fn dispatch_device_control( } } Err(err) => { - vck_driver::vck_log!("sample-driver: ioctl 0x{:08x} failed: {}", ioctl_code, err); + vck_windrv::vck_log!("sample-driver: ioctl 0x{:08x} failed: {}", ioctl_code, err); complete_irp(irp, STATUS_UNSUCCESSFUL, 0); STATUS_UNSUCCESSFUL } diff --git a/sample/windrv/src/provider.rs b/sample/windrv/src/provider.rs index 05b1c22..b2cec3f 100644 --- a/sample/windrv/src/provider.rs +++ b/sample/windrv/src/provider.rs @@ -12,7 +12,7 @@ use vck_common::{ jvck::{JvckCbcCodec, JvckMetadataReader, MetadataCodec}, EncryptedOffset, EncryptedOffsetStore, VckError, VckResult, VolumeCipher, }; -use vck_driver::{ +use vck_windrv::{ crypto::AesXtsCipher, device::ControlDeviceSecurity, ioctl::codes::IOCTL_VCK_GET_PROGRESS, AttachContext, DetachContext, IoConfig, IoctlAuthContext, IoctlAuthorization, RequestorMode, VolumeProvider, @@ -100,7 +100,7 @@ impl VolumeProvider for VckVolumeProvider { impl IoctlAuthorization for VckVolumeProvider { fn authorize(&self, ctx: &IoctlAuthContext<'_>) -> VckResult<()> { if ctx.ioctl_code == IOCTL_VCK_GET_PROGRESS - || ctx.ioctl_code == vck_driver::ioctl::codes::IOCTL_VCK_GET_STATUS + || ctx.ioctl_code == vck_windrv::ioctl::codes::IOCTL_VCK_GET_STATUS { return Ok(()); } From af3cada8257e18142b983d03711cf7c24e0f710f Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 15 Jun 2026 21:32:27 +0900 Subject: [PATCH 9/9] fix --- lib/common/src/jvck/metadata.rs | 8 ++++---- lib/common/src/xts.rs | 10 +++++----- sample/windrv/src/lib.rs | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/common/src/jvck/metadata.rs b/lib/common/src/jvck/metadata.rs index 48b156c..33da23f 100644 --- a/lib/common/src/jvck/metadata.rs +++ b/lib/common/src/jvck/metadata.rs @@ -16,10 +16,10 @@ use aes::Aes256; use cbc::{Decryptor, Encryptor}; -use cipher::{block_padding::NoPadding, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +use cipher::{block_padding::NoPadding, BlockModeDecrypt, BlockModeEncrypt, KeyIvInit}; use crc::{Crc, CRC_32_ISO_HDLC}; use hkdf::Hkdf; -use hmac::{Hmac, Mac}; +use hmac::{Hmac, KeyInit, Mac}; use sha2::Sha256; use zeroize::{Zeroize, ZeroizeOnDrop}; @@ -228,7 +228,7 @@ impl JvckHeader { let result = (|| { let enc = Encryptor::::new_from_slices(&keys.enc_key, &keys.enc_iv) .map_err(|_| VckError::CryptoFailed("invalid ENC key/iv length"))?; - enc.encrypt_padded_mut::(&mut plain, ENCRYPTED_METADATA_SIZE) + enc.encrypt_padded::(&mut plain, ENCRYPTED_METADATA_SIZE) .map_err(|_| VckError::CryptoFailed("EncryptedMetadata CBC encrypt failed"))?; out[OFF_ENCRYPTED_METADATA..OFF_ENCRYPTED_METADATA + ENCRYPTED_METADATA_SIZE] .copy_from_slice(&plain); @@ -305,7 +305,7 @@ pub fn decrypt_payload(block: &[u8], vmk: &[u8]) -> VckResult<(u64, VolumeState, let dec = Decryptor::::new_from_slices(&keys.enc_key, &keys.enc_iv) .map_err(|_| VckError::CryptoFailed("invalid ENC key/iv length"))?; let plain = dec - .decrypt_padded_mut::(&mut buf) + .decrypt_padded::(&mut buf) .map_err(|_| VckError::CryptoFailed("EncryptedMetadata CBC decrypt failed"))?; // Verify the inner signature + zero field (wrong VMK -> garbage here). diff --git a/lib/common/src/xts.rs b/lib/common/src/xts.rs index f558139..d07bcf7 100644 --- a/lib/common/src/xts.rs +++ b/lib/common/src/xts.rs @@ -37,7 +37,7 @@ //! with a caller's frame. (A prior build with `+aes` inlined the unrolled AES //! into the IOCTL path and double-faulted on stack overflow.) -use aes::cipher::{BlockDecrypt, BlockEncrypt, KeyInit}; +use aes::cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit}; use aes::{Aes256, Block}; use crate::{VckError, VckResult}; @@ -138,7 +138,7 @@ impl XtsVolumeCipher { #[inline(never)] fn encrypt_sector_inner(&self, rel_sector: u64, sector: &mut [u8]) { // T_0 = AES_K2(sector_number as 128-bit little-endian) - let mut tw = *Block::from_slice(&(rel_sector as u128).to_le_bytes()); + let mut tw: Block = (rel_sector as u128).to_le_bytes().into(); self.cipher_2.encrypt_block(&mut tw); let n = sector.len() / 16; @@ -177,7 +177,7 @@ impl XtsVolumeCipher { for j in 0..16 { block[j] ^= tw[j]; } - let mut ga = *Block::from_slice(block); + let mut ga: Block = Block::try_from(&block[..]).unwrap(); self.cipher_1.encrypt_block(&mut ga); block.copy_from_slice(&ga); for j in 0..16 { @@ -191,7 +191,7 @@ impl XtsVolumeCipher { #[inline(never)] fn decrypt_sector_inner(&self, rel_sector: u64, sector: &mut [u8]) { // Tweak is always encrypted with K2 (even during decryption). - let mut tw = *Block::from_slice(&(rel_sector as u128).to_le_bytes()); + let mut tw: Block = (rel_sector as u128).to_le_bytes().into(); self.cipher_2.encrypt_block(&mut tw); let n = sector.len() / 16; @@ -227,7 +227,7 @@ impl XtsVolumeCipher { for j in 0..16 { block[j] ^= tw[j]; } - let mut ga = *Block::from_slice(block); + let mut ga: Block = Block::try_from(&block[..]).unwrap(); self.cipher_1.decrypt_block(&mut ga); block.copy_from_slice(&ga); for j in 0..16 { diff --git a/sample/windrv/src/lib.rs b/sample/windrv/src/lib.rs index c17405a..26f1d72 100644 --- a/sample/windrv/src/lib.rs +++ b/sample/windrv/src/lib.rs @@ -17,7 +17,8 @@ pub use provider::VckVolumeProvider; use core::ffi::c_void; use core::panic::PanicInfo; -use spin::{Lazy, Mutex}; +use spin::{LazyLock, Mutex}; +use vck_sample_common::VckHandoverPayload; use vck_windrv::{ device::{ControlDevice, DeviceExtension, DEVICE_KIND_FILTER}, filter::handle_filter_irp, @@ -26,7 +27,6 @@ use vck_windrv::{ provider::{AccessToken, IoctlAuthContext, RequestorMode}, VolumeAttachRegistry, }; -use vck_sample_common::VckHandoverPayload; use wdk_alloc::WdkAllocator; use wdk_sys::{ ntddk::{ @@ -44,7 +44,7 @@ use wdk_sys::{ static GLOBAL_ALLOCATOR: WdkAllocator = WdkAllocator; static CONTROL_DEVICE: Mutex> = Mutex::new(None); -static REGISTRY: Lazy = Lazy::new(VolumeAttachRegistry::new); +static REGISTRY: LazyLock = LazyLock::new(VolumeAttachRegistry::new); static PROVIDER: VckVolumeProvider = VckVolumeProvider; const STATUS_SUCCESS: NTSTATUS = 0;