muja/unrar.rs

Archive extraction panics on armv7-unknown-linux-musleabihf architecture

Closed this issue · 4 comments

lmkra commented

When trying to extract .rar file on armv7-unknown-linux-musleabihf architecture, library panics with:

thread \'main\' panicked at \'attempt to shift left with overflow\', /cargo/registry/src/index.crates.io-6f17d22bba15001f/unrar-0.5.1/src/open_archive.rs:647:5

Used code:

pub fn unpack_archive(archive_path: &Path, output_folder: &Path) -> crate::Result<usize> {
    assert!(output_folder.read_dir().expect("dir exists").count() == 0);

    let mut archive = Archive::new(archive_path).open_for_processing()?;
    let mut unpacked = 0;

    while let Some(header) = archive.read_header()? {
        let entry = header.entry();
        archive = if entry.is_file() {
            unpacked += 1;
            header.extract_with_base(output_folder)?
        } else {
            header.skip()?
        };
    }

    Ok(unpacked)
}

Same code is working fine on x86_64.

muja commented

Thank you for opening an issue. Seems this has to do with 32-bit architecture.

The problem is the code in the specified line which translates to let foo: u64 = 0 << 8 * std::mem::size_of::<u32>() on 64-bit architecture which works fine and to let x: u32 = 0 << 8 * std::mem::size_of::<u32>() on 32-bit architecture which panics (playground link

May I ask what behavior would you expect if the file is larger than u32::MAX? Would it be even possible to extract?

lmkra commented

Your playground link panics for me even on 64-bit architecture :)

I've looked at the code as well, here's the panicking function for reference:

fn unpack_unp_size(unp_size: c_uint, unp_size_high: c_uint) -> usize {
    ((unp_size_high as usize) << (8 * std::mem::size_of::<c_uint>())) | (unp_size as usize)
}

I think that the culprit here is that above code implicitly assumes that size_of::<usize>() > size_of::<c_uint>().
Unfortunately, since usize size is target-dependent, that implicit assumption is not true on 32-bit architecture.
(Actually, c_uint size also target-dependent, but usually is 32-bit long)

Do you see any issue with explicitly using u64 instead of usize to avoid this pitfall ?

May I ask what behavior would you expect if the file is larger than u32::MAX? Would it be even possible to extract?

Speaking from the user perspective:
I'd say that if underlying filesystem allows it (looking at you, FAT32), extraction should be possible.

muja commented

Ah, sorry, I've corrected the first snippet now (should've been u64). If you change let _foo: u32 to let _foo: u64 in the playground link, it doesn't panic and that is the behavior on 64-bit architecture.
Yes changing to u64 is probably the best solution, I'm just not sure if a patch release is sufficient, but probably is.

lmkra commented

Taking into account that such solution won't change anything for 64-bit arch, and on 32-bit it didn't work in the first place, I think a patch release would be a good way forward.

Would you like me to propose a PR for this or will you change it yourself?