Skip to content

Conversation

Zero-Tang
Copy link
Contributor

@Zero-Tang Zero-Tang commented May 13, 2025

Now the WDK allocator can allocate aligned memory according to the layout argument.

Note that this allocator won't waste additional space for alignment if the alignment is normal (e.g.: less than or equal to 16-byte boundary on 64-bit platform). In average scenario, this is better than _aligned_malloc and _aligned_free in msvcrt, in that I guess we rarely need big alignments in drivers for some stuff in the pool. Still, some performance-critical codes might require cache-aligned (e.g.: 64-byte in x86) objects, which is usually bigger than ExAllocatePool2's alignment.

Close #303.

Now the WDK allocator can allocate aligned memory according to the layout argument.
Copy link
Collaborator

@wmmc88 wmmc88 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @Zero-Tang!

How did you go about validating these changes? I think some concrete examples of the non-aligned case should be added the example drivers (ie. sample wdm & sample kmdf)

let q = ((p as usize & mask) + layout.align()) as *mut *mut u8;
// Store the original pointer right before the pointer to return,
// so that ExFreePoolWithTag can receive the correct pointer.
// This is also how `_aligned_malloc` is implemented in msvcrt.dll
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this documented?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not documented, but _aligned_malloc is actually somewhat open-source. Check the %ProgramFiles%\Windows Kits\10\Source\<Your SDK version>\ucrt\heap\align.cpp file, and you can find its implementation.

@wmmc88
Copy link
Collaborator

wmmc88 commented May 13, 2025

related to #303? @gurry could you also take a look at this and provide feedback :)

@Zero-Tang
Copy link
Contributor Author

Thanks for the feedback!
I did wrote an example, but in a local separate crate, because rust-analyzer marks most parts of wdk-alloc crate as inactive codes for some strange reasons. It won't be marked as inactive if I check it in a separate crate.
I will copy'n'paste my validation example here soon.

@gurry
Copy link
Contributor

gurry commented May 14, 2025

related to #303? @gurry could you also take a look at this and provide feedback :)

Thanks @wmmc88. I will have a look.

Also address Melvin's reviews.
@Zero-Tang Zero-Tang requested a review from wmmc88 May 15, 2025 04:07
Copy link
Contributor

@gurry gurry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Zero-Tang The overall logic looks good to me. Thanks. Added comments for some small improvements.

Comment on lines 58 to 73
{
// Example of using WDK allocator.
// Allocations will be properly aligned on their boundaries!
// Allocate a single instance.
let ah=Box::new(AlignedHigher(1234));
println!("ah is allocated at {:p}, value={ah:?}",&raw const *ah);
// Verify its alignment.
assert_eq!((&raw const *ah) as usize & (align_of::<AlignedHigher>() - 1), 0);
// Allocate a vector that occupies more than a page.
let vh=vec![AlignedHigher(1234), AlignedHigher(5678), AlignedHigher(9012), AlignedHigher(3456), AlignedHigher(7890)];
println!("vh is allocated at {:p}, value={vh:?}",vh.as_ptr());
// Verify their alignments.
for x in &vh {
assert_eq!((core::ptr::from_ref::<AlignedHigher>(x) as usize) & (align_of::<AlignedHigher>() - 1), 0);
}
}
Copy link
Contributor

@gurry gurry May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmmc88 Is there a more preferred way to write tests?

How about we write unit tests in the crates/wdk-alloc/src/lib.rs file itself. We can mock APIs like ExAllocatePool2 by putting them in global statics and then then in the test replacing them with mock implementations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can mock ExAllocatePool2 by calling VirtualAlloc on Windows or mmap on Linux, then add the returned pointer by 16 or 8. In this case, we can guarantee no coincidental big alignments returned by CRT's malloc.

Copy link
Contributor

@gurry gurry May 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be easier for the mock ExAllocatePool2 to use a global array as its source of allocations instead of VirtualAlloc or malloc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be preferable if there are unit-tests with mocked wdk api calls in the wdk-alloc.

Ideally, integration tests for the allocator would be facilitated in wdk-alloc/examples folder, but i don't think either our cargo-make scripts, nor the new cargo-wdk tool support packaging drivers from examples right now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be preferable if there are unit-tests with mocked wdk api calls in the wdk-alloc.

In that case @Zero-Tang can you please see if you can write some key unit tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Emmmmmm, I'm not so sure how to do this.

Copy link
Contributor

@gurry gurry May 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An approach like this can work:

  1. Declare a static pointing either to the real ExAllocatePool2 or a mock depending on whether we're running tests or not:
type ExAllocatePool2Fn = unsafe extern "system" fn(pool_flags: u32, number_of_bytes: usize, tag: u32) -> *mut c_void;

#[cfg(not(test))]
pub static mut EX_ALLOCATE_POOL2_FN: ExAllocatePool2Fn = ExAllocatePool2;
#[cfg(test)]
pub static mut EX_ALLOCATE_POOL2_FN: ExAllocatePool2Fn = mock_ex_allocate_pool2;
  1. Declare a static buffer that mock_ex_allocate_pool2 will "allocate" from:
static ALLOC_BUFFER:  [u8; 5120] = [0; 5120]; // 5 Pages worth of buffer to allocate from
static mut FREE_INDEX: usize = 0; // Index to keep track of available memory in buffer
  1. Implement mock_ex_allocate_pool2 using this buffer as the source of allocations.

  2. In WdkAllcator call EX_ALLOCATE_POOL2_FN instead of calling ExAllocatePoo2 directly.

Now you should be ready to write your tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda give up on this in that I couldn't make the test link to wdk_alloc::kernel_mode module due to the #[cfg(any(driver_model__driver_type = "WDM", driver_model__driver_type = "KMDF"))] attribute.
I tried cargo test --config driver_model__driver_type=\"WDM\" but it doesn't work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wmmc88 Can you please help with this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda give up on this in that I couldn't make the test link to wdk_alloc::kernel_mode module due to the #[cfg(any(driver_model__driver_type = "WDM", driver_model__driver_type = "KMDF"))] attribute.
I tried cargo test --config driver_model__driver_type="WDM" but it doesn't work.

I don't think this will work as currently structured. I am working on some tooling to enable better unit testing of code that relies on the metadata section. unfortunately, since this is such a high impact change, I think we'll wait for that tooling to finish before merging this (with tests).

Copy link
Contributor

@gurry gurry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for making the requested changes @Zero-Tang. Here are a few more suggestions.

Copy link
Contributor

@gurry gurry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments on comments.

Further clarify how the original pointer is stored and why it's safe.
@Zero-Tang
Copy link
Contributor Author

Just FYI that I edited the first comment of this PR thread so that this PR will close #303 on merge.

@gurry
Copy link
Contributor

gurry commented May 21, 2025

Just FYI that I edited the first comment of this PR thread so that this PR will close #303 on merge.

Thanks. Yes, #303 should be closed.

I am done with my review. Will wait for inputs from @wmmc88.

@wmmc88 wmmc88 self-assigned this May 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ensure correct alignment in custom allocator
3 participants