zowens / crc32c

Fast CRC-32-Castagnoli implementation in Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

incorrect use of unsafe

BurntSushi opened this issue · comments

Most of the functions in hw.rs either need to be marked unsafe, or guarateed by the type system to be safe. Neither are true today.

From the std::arch docs:

The CPU the program is currently running on supports the function being called. For example it is unsafe to call an AVX2 function on a CPU that doesn't actually support AVX2.

The simplest way that I can tell to fix this in your code is to create a type that can only be constructed when the correct target features are enabled. e.g.,

struct SSE42(());

impl SSE42 {
    fn new() -> Option<SSE42> {
        if is_x86_feature_detected!("sse4.2") {
            Some(SSE42(()))
        } else {
            None
        }
    }
}

Then any function needing to use SSE4.2 can require a SSE42 parameter. And your crc32c_append function then looks like this:

pub fn crc32c_append(crc: u32, data: &[u8]) -> u32 {
    if let Some(receipt) = SSE42::new() {
        hw::crc32c(receipt, crc, data)
    } else {
        sw::crc32c(crc, data)
    }
}

The reason why the code today is wrong is because any user of hw.rs can call hw::crc32c as a safe function without actually verifying the assumptions required by the use of unsafe inside hw::crc32c.

Now, it is true that this is not exposed in the public crate API, which is good. But internally, this is still a violation of unsafe.

Thanks for the feedback.

The entire point of the crate is to make the determination of software vs. hardware and abstract that via the top-level func. I'll admit there is some internal mess around unsafe, which I'll address.

I'm not sure I follow why a top-level type is necessary in this case. Could you elaborate as to why your proposed SSE42 type is necessary?

My suggestion is one possible solution. What's necessary is to ensure that you do not define a safe function that can be called that doesn't satisfy the assumptions that its use of unsafe requires.

Another possible solution is to make every function that transitively uses intrinsics in std::arch unsafe, with the exception of your public functions, which are themselves responsible for satisfying the assumptions that std::arch requires.

Gotcha. That was my initial thought on fixing the unsafe behavior.