I am relatively new to Rust and trying to get a clean, lint-free version of a project working that I ported from C/C++. I managed to get everything ported, compiled and lightly tested on my machine. Before performing additional refactoring I wanted to try out fmt and clippy in order to see how well I did. While working through all of the issues I have come across one that has spiraled a bit and I want to see what I am missing and get advice from more experienced Rust users.
The issue I am dealing are a several pieces of code working with the Windows API with an idiom of where one calls a function twice. The first time requests the size of the memory for a struct (or some other blob of bytes) which will be the output parameter holding the result after the second call. In order to allocate this memory I have been using Vec<u8>
and passing in a pointer to the memory through as_mut_ptr()
and in some cases I need to cast to the type pointer and others it will be a pointer to a void* in yet others a pointer to a DWORD (Windows APIs are not particularly consistent). Depending on the cast this will trigger the cast_ptr_alignment lint. In some cases, after verifying the call succeeded I was trying to cast the Vec<u8>
memory array through as_ptr()
to the struct type and interact with the members of the struct like I would any other struct in rust. In others, I am just dumping the memory out to a file. I mention this as I am uncertain if each of these scenarios leads to different solutions or not.
Here's a portion of one piece of code that issued the lint, it won't compile on it's own but hopefully this is enough to give an idea
//-------------------------------------------------------------------
// Call CryptExportPublicKeyInfo to get the size of the returned
// information.
let mut public_key_info_size: DWORD = 0;
if unsafe {
wincrypt::CryptExportPublicKeyInfo(
key.handle, // Provider handle
AT_SIGNATURE, // Key spec
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, // Encoding type
ptr::null_mut(), // pbPublicKeyInfo
&mut public_key_info_size,
)
} == 0
{
// Size of PublicKeyInfo
let last_error = unsafe { errhandlingapi::GetLastError() };
return crypt_error!(last_error, "Error exporting public key info");
}
info!("The keyinfo structure is {} bytes.", public_key_info_size);
// set-up the blob
let mut public_key_blob: Vec<u8> = Vec::with_capacity(public_key_info_size as usize);
//-------------------------------------------------------------------
// Call CryptExportPublicKeyInfo to get pbPublicKeyInfo.
unsafe {
if wincrypt::CryptExportPublicKeyInfo(
key.handle,
AT_SIGNATURE,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
public_key_blob.as_mut_ptr() as PCERT_PUBLIC_KEY_INFO,
&mut public_key_info_size,
) == 0
{
let last_error = errhandlingapi::GetLastError();
return crypt_error!(last_error, "Error exporting public key info");
}
public_key_blob.set_len(public_key_info_size as usize);
}
info!("The public key info has been exported.");
The lint is on this line:
public_key_blob.as_mut_ptr() as PCERT_PUBLIC_KEY_INFO,
with error message:
error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut util::winapi::um::wincrypt::CERT_PUBLIC_KEY_INFO`) (1 < 8 bytes)
Based on the lints, I understand the code I have now is incorrect (or unsound or both). But what is the proper way of doing this? Do I use ptr:read_unaligned() for the case I need to read from a field of the struct? What is the correct way of handling the situation above? Do I need to allocate the memory in some other way? I had hit upon Vec since it seemed to work with a contiguous array of bytes which seemed to implement the semantics I needed, but based on the documentation I have read it appears that what I am doing is not correct/sound Rust.