Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chacha20: SIGSEGV in CI #304

Closed
tarcieri opened this issue Aug 29, 2022 · 8 comments · Fixed by #305
Closed

chacha20: SIGSEGV in CI #304

tarcieri opened this issue Aug 29, 2022 · 8 comments · Fixed by #305
Labels
security Security vulnerabilities

Comments

@tarcieri
Copy link
Member

See: https://github.com/RustCrypto/stream-ciphers/runs/7970722385?check_suite_focus=true#step:8:28

It occurred running the integration tests in the autodetect CI job for this dependabot PR to bump cpufeatures from 0.2.3 to 0.2.4, so that's possibly implicated: #303

cc @newpavlov @str4d

@tarcieri tarcieri added the security Security vulnerabilities label Aug 29, 2022
@newpavlov
Copy link
Member

It looks like an i686-specific issue. cpufeatures v0.2.4 should be identical to v0.2.3. so a more likely PR to look into is RustCrypto/utils#792. Also the master branch CI fails as well.

Interestingly enough, the 1.56 job passes without issues, so it could be a compiler bug.

@newpavlov
Copy link
Member

I tested it a bit on my PC. The error happens spuriously, approximately every 4th run. It does not get triggered if zeroize feature is not enabled, as well as reverting cpufeatures to v0.2.2.

@tarcieri
Copy link
Member Author

Huh, that's strange regarding zeroize. I believe there's only a single usage in the entire crate:

https://github.com/RustCrypto/stream-ciphers/blob/400a398/chacha20/src/lib.rs#L310

...where state is a [u32; 16].

@newpavlov since you can reproduce it, could you attach gdb or lldb and get a backtrace?

I'll see if I can poke at it if I have some time.

@newpavlov
Copy link
Member

I was unable to trigger the error in gdb and I can not attach gdb to already launched tests since they end too quickly.

@newpavlov
Copy link
Member

Maybe it's worth to release a temporary hotfix which would pin cpufeatures to v0.2.2 on i686?

@aumetra
Copy link

aumetra commented Sep 2, 2022

For what it's worth, I got it to segfault in GDB after a bunch of tries (it's way rarer than 1 in 4 on my machine; the text scrolled too fast to count the tries).
I've modified the test only insofar that I manually expanded the macro. The segfault occurs on the same line with the original test code.

Kernel: Linux 5.10.16.3-microsoft-standard-WSL2

Backtrace (1.63)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

running 9 tests
[New Thread 0xf7c8bb40 (LWP 22208)]
[New Thread 0xf7a8ab40 (LWP 22209)]
[New Thread 0xf74ffb40 (LWP 22210)]

Thread 3 "chacha20_seek" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xf7a8ab40 (LWP 22209)]
0x5655c1f0 in memset@plt ()
(gdb) backtrace
#0  0x5655c1f0 in memset@plt ()
#1  0x5656294d in mod::chacha20_seek () at chacha20/tests/mod.rs:24
#2  mod::chacha20_seek::{closure#0} () at chacha20/tests/mod.rs:10
#3  core::ops::function::FnOnce::call_once<mod::chacha20_seek::{closure_env#0}, ()> () at /rustc/4b91a6ea7258a947e59c6522cd5898e7c0a6a88f/library/core/src/ops/function.rs:248
#4  0x5659f3a2 in core::ops::function::FnOnce::call_once<fn(), ()> () at library/core/src/ops/function.rs:248
#5  test::__rust_begin_short_backtrace<fn()> () at library/test/src/lib.rs:572
#6  0x5659e470 in alloc::boxed::{impl#44}::call_once<(), (dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global> () at library/alloc/src/boxed.rs:1951
#7  core::panic::unwind_safe::{impl#23}::call_once<(), alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>> () at library/core/src/panic/unwind_safe.rs:271
#8  std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>, ()> () at library/std/src/panicking.rs:492
#9  std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>> () at library/std/src/panicking.rs:456
#10 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>, ()> () at library/std/src/panic.rs:137
#11 test::run_test_in_process () at library/test/src/lib.rs:595
#12 test::run_test::run_test_inner::{closure#0} () at library/test/src/lib.rs:489
#13 0x56568806 in test::run_test::run_test_inner::{closure#1} () at library/test/src/lib.rs:516
#14 std::sys_common::backtrace::__rust_begin_short_backtrace<test::run_test::run_test_inner::{closure_env#1}, ()> () at library/std/src/sys_common/backtrace.rs:122
#15 0x5656e18f in std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure#0}<test::run_test::run_test_inner::{closure_env#1}, ()> () at library/std/src/thread/mod.rs:505
#16 core::panic::unwind_safe::{impl#23}::call_once<(), std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<test::run_test::run_test_inner::{closure_env#1}, ()>> () at library/core/src/panic/unwind_safe.rs:271
#17 std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<test::run_test::run_test_inner::{closure_env#1}, ()>>, ()> () at library/std/src/panicking.rs:492
#18 std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<test::run_test::run_test_inner::{closure_env#1}, ()>>> () at library/std/src/panicking.rs:456
#19 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<test::run_test::run_test_inner::{closure_env#1}, ()>>, ()> () at library/std/src/panic.rs:137
#20 std::thread::{impl#0}::spawn_unchecked_::{closure#1}<test::run_test::run_test_inner::{closure_env#1}, ()> () at library/std/src/thread/mod.rs:504
#21 core::ops::function::FnOnce::call_once<std::thread::{impl#0}::spawn_unchecked_::{closure_env#1}<test::run_test::run_test_inner::{closure_env#1}, ()>, ()> () at library/core/src/ops/function.rs:248
#22 0x565cd31b in alloc::boxed::{impl#44}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1951
#23 alloc::boxed::{impl#44}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1951
#24 std::sys::unix::thread::{impl#2}::new::thread_start () at library/std/src/sys/unix/thread.rs:108
#25 0xf7f890b4 in start_thread () from /lib32/libpthread.so.0
#26 0xf7d92256 in clone () from /lib32/libc.so.6
Backtrace (1.59; last version before LLVM 14)
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

running 9 tests
[New Thread 0xf7c8bb40 (LWP 30440)]
[New Thread 0xf7a8ab40 (LWP 30441)]
[New Thread 0xf74ffb40 (LWP 30442)]

Thread 3 "chacha20_seek" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xf7a8ab40 (LWP 30441)]
0x5655b1f0 in memset@plt ()
(gdb) backtrace
#0  0x5655b1f0 in memset@plt ()
#1  0x565620f1 in mod::chacha20_seek () at chacha20/tests/mod.rs:24
#2  mod::chacha20_seek::{closure#0} () at chacha20/tests/mod.rs:10
#3  core::ops::function::FnOnce::call_once<mod::chacha20_seek::{closure#0}, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/ops/function.rs:227
#4  0x565932e2 in core::ops::function::FnOnce::call_once<fn(), ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/ops/function.rs:227
#5  test::__rust_begin_short_backtrace<fn()> () at library/test/src/lib.rs:574
#6  0x56591f64 in alloc::boxed::{impl#44}::call_once<(), (dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/alloc/src/boxed.rs:1854
#7  core::panic::unwind_safe::{impl#23}::call_once<(), alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panic/unwind_safe.rs:271
#8  std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:406
#9  std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:370
#10 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<alloc::boxed::Box<(dyn core::ops::function::FnOnce<(), Output=()> + core::marker::Send), alloc::alloc::Global>>, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panic.rs:133
#11 test::run_test_in_process () at library/test/src/lib.rs:597
#12 test::run_test::run_test_inner::{closure#0} () at library/test/src/lib.rs:491
#13 0x5659b429 in test::run_test::run_test_inner::{closure#1} () at library/test/src/lib.rs:518
#14 std::sys_common::backtrace::__rust_begin_short_backtrace<test::run_test::run_test_inner::{closure#1}, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/sys_common/backtrace.rs:123
#15 0x56568d6d in std::thread::{impl#0}::spawn_unchecked::{closure#1}::{closure#0}<test::run_test::run_test_inner::{closure#1}, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/thread/mod.rs:477
#16 core::panic::unwind_safe::{impl#23}::call_once<(), std::thread::{impl#0}::spawn_unchecked::{closure#1}::{closure#0}> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panic/unwind_safe.rs:271
#17 std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked::{closure#1}::{closure#0}>, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:406
#18 std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked::{closure#1}::{closure#0}>> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:370
#19 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked::{closure#1}::{closure#0}>, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panic.rs:133
#20 std::thread::{impl#0}::spawn_unchecked::{closure#1}<test::run_test::run_test_inner::{closure#1}, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/thread/mod.rs:476
#21 core::ops::function::FnOnce::call_once<std::thread::{impl#0}::spawn_unchecked::{closure#1}, ()> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/ops/function.rs:227
#22 0x565c8e2c in alloc::boxed::{impl#44}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/alloc/src/boxed.rs:1854
#23 alloc::boxed::{impl#44}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> () at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/alloc/src/boxed.rs:1854
#24 std::sys::unix::thread::{impl#2}::new::thread_start () at library/std/src/sys/unix/thread.rs:108
#25 0xf7f890b4 in start_thread () from /lib32/libpthread.so.0
#26 0xf7d92256 in clone () from /lib32/libc.so.6
Test code
//! Tests for ChaCha20 (IETF and "djb" versions) as well as XChaCha20
use chacha20::{ChaCha20, ChaCha20Legacy, XChaCha20};

// IETF version of ChaCha20 (96-bit nonce)
cipher::stream_cipher_test!(chacha20_core, "chacha20", ChaCha20);
cipher::stream_cipher_seek_test!(xchacha20_seek, XChaCha20);
cipher::stream_cipher_seek_test!(chacha20legacy_seek, ChaCha20Legacy);

#[test]
fn chacha20_seek() {
    use cipher::generic_array::GenericArray;
    use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
    fn get_cipher() -> ChaCha20 {
        <ChaCha20>::new(&Default::default(), &Default::default())
    }
    const MAX_SEEK: usize = 512;
    let mut ct = [0u8; MAX_SEEK];
    get_cipher().apply_keystream(&mut ct[..]);
    for n in 0..MAX_SEEK {
        let mut cipher = get_cipher();
        assert_eq!(cipher.current_pos::<usize>(), 0);
        cipher.seek(n);
        assert_eq!(cipher.current_pos::<usize>(), n);
        let mut buf = [0u8; MAX_SEEK];
        cipher.apply_keystream(&mut buf[n..]);
        assert_eq!(cipher.current_pos::<usize>(), MAX_SEEK);
        assert_eq!(&buf[n..], &ct[n..]);
    }
    const MAX_CHUNK: usize = 128;
    const MAX_LEN: usize = 1024;
    let mut buf = [0u8; MAX_CHUNK];
    let mut cipher = get_cipher();
    assert_eq!(cipher.current_pos::<usize>(), 0);
    cipher.apply_keystream(&mut []);
    assert_eq!(cipher.current_pos::<usize>(), 0);
    for n in 1..MAX_CHUNK {
        assert_eq!(cipher.current_pos::<usize>(), 0);
        for m in 1.. {
            cipher.apply_keystream(&mut buf[..n]);
            assert_eq!(cipher.current_pos::<usize>(), (n * m));
            if n * m > MAX_LEN {
                break;
            }
        }
        cipher.seek(0);
    }
}

mod chacha20test {
    use chacha20::{ChaCha20, Key, Nonce};
    use cipher::{KeyIvInit, StreamCipher};
    use hex_literal::hex;

    //
    // ChaCha20 test vectors from:
    // <https://datatracker.ietf.org/doc/html/rfc8439#section-2.4.2>
    //

    const KEY: [u8; 32] = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");

    const IV: [u8; 12] = hex!("000000000000004a00000000");

    const PLAINTEXT: [u8; 114] = hex!(
        "
        4c616469657320616e642047656e746c
        656d656e206f662074686520636c6173
        73206f66202739393a20496620492063
        6f756c64206f6666657220796f75206f
        6e6c79206f6e652074697020666f7220
        746865206675747572652c2073756e73
        637265656e20776f756c642062652069
        742e
        "
    );

    const KEYSTREAM: [u8; 114] = hex!(
        "
        224f51f3401bd9e12fde276fb8631ded8c131f823d2c06
        e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b
        9334794cba40c63e34cdea212c4cf07d41b769a6749f3f
        630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53a
        c40c5945398b6eda1a832c89c167eacd901d7e2bf363
        "
    );

    const CIPHERTEXT: [u8; 114] = hex!(
        "
        6e2e359a2568f98041ba0728dd0d6981
        e97e7aec1d4360c20a27afccfd9fae0b
        f91b65c5524733ab8f593dabcd62b357
        1639d624e65152ab8f530c359f0861d8
        07ca0dbf500d6a6156a38e088a22b65e
        52bc514d16ccf806818ce91ab7793736
        5af90bbf74a35be6b40b8eedf2785e42
        874d
        "
    );

    #[test]
    fn chacha20_keystream() {
        let mut cipher = ChaCha20::new(&Key::from(KEY), &Nonce::from(IV));

        // The test vectors omit the first 64-bytes of the keystream
        let mut prefix = [0u8; 64];
        cipher.apply_keystream(&mut prefix);

        let mut buf = [0u8; 114];
        cipher.apply_keystream(&mut buf);
        assert_eq!(&buf[..], &KEYSTREAM[..]);
    }

    #[test]
    fn chacha20_encryption() {
        let mut cipher = ChaCha20::new(&Key::from(KEY), &Nonce::from(IV));
        let mut buf = PLAINTEXT.clone();

        // The test vectors omit the first 64-bytes of the keystream
        let mut prefix = [0u8; 64];
        cipher.apply_keystream(&mut prefix);

        cipher.apply_keystream(&mut buf);
        assert_eq!(&buf[..], &CIPHERTEXT[..]);
    }
}

#[rustfmt::skip]
mod xchacha20 {
    use chacha20::{Key, XChaCha20, XNonce};
    use cipher::{KeyIvInit, StreamCipher};
    use hex_literal::hex;

    cipher::stream_cipher_seek_test!(xchacha20_seek, XChaCha20);

    //
    // XChaCha20 test vectors from:
    // <https://tools.ietf.org/id/draft-arciszewski-xchacha-03.html#rfc.appendix.A.3.2>
    //

    const KEY: [u8; 32] = hex!("
        808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f
    ");

    const IV: [u8; 24] = hex!("
        404142434445464748494a4b4c4d4e4f5051525354555658
    ");

    const PLAINTEXT: [u8; 304] = hex!("
        5468652064686f6c65202870726f6e6f756e6365642022646f6c652229206973
        20616c736f206b6e6f776e2061732074686520417369617469632077696c6420
        646f672c2072656420646f672c20616e642077686973746c696e6720646f672e
        2049742069732061626f7574207468652073697a65206f662061204765726d61
        6e20736865706865726420627574206c6f6f6b73206d6f7265206c696b652061
        206c6f6e672d6c656767656420666f782e205468697320686967686c7920656c
        757369766520616e6420736b696c6c6564206a756d70657220697320636c6173
        736966696564207769746820776f6c7665732c20636f796f7465732c206a6163
        6b616c732c20616e6420666f78657320696e20746865207461786f6e6f6d6963
        2066616d696c792043616e696461652e
    ");

    const KEYSTREAM: [u8; 304] = hex!("
        29624b4b1b140ace53740e405b2168540fd7d630c1f536fecd722fc3cddba7f4
        cca98cf9e47e5e64d115450f9b125b54449ff76141ca620a1f9cfcab2a1a8a25
        5e766a5266b878846120ea64ad99aa479471e63befcbd37cd1c22a221fe46221
        5cf32c74895bf505863ccddd48f62916dc6521f1ec50a5ae08903aa259d9bf60
        7cd8026fba548604f1b6072d91bc91243a5b845f7fd171b02edc5a0a84cf28dd
        241146bc376e3f48df5e7fee1d11048c190a3d3deb0feb64b42d9c6fdeee290f
        a0e6ae2c26c0249ea8c181f7e2ffd100cbe5fd3c4f8271d62b15330cb8fdcf00
        b3df507ca8c924f7017b7e712d15a2eb5c50484451e54e1b4b995bd8fdd94597
        bb94d7af0b2c04df10ba0890899ed9293a0f55b8bafa999264035f1d4fbe7fe0
        aafa109a62372027e50e10cdfecca127
    ");

    const CIPHERTEXT: [u8; 304] = hex!("
        7d0a2e6b7f7c65a236542630294e063b7ab9b555a5d5149aa21e4ae1e4fbce87
        ecc8e08a8b5e350abe622b2ffa617b202cfad72032a3037e76ffdcdc4376ee05
        3a190d7e46ca1de04144850381b9cb29f051915386b8a710b8ac4d027b8b050f
        7cba5854e028d564e453b8a968824173fc16488b8970cac828f11ae53cabd201
        12f87107df24ee6183d2274fe4c8b1485534ef2c5fbc1ec24bfc3663efaa08bc
        047d29d25043532db8391a8a3d776bf4372a6955827ccb0cdd4af403a7ce4c63
        d595c75a43e045f0cce1f29c8b93bd65afc5974922f214a40b7c402cdb91ae73
        c0b63615cdad0480680f16515a7ace9d39236464328a37743ffc28f4ddb324f4
        d0f5bbdc270c65b1749a6efff1fbaa09536175ccd29fb9e6057b307320d31683
        8a9c71f70b5b5907a66f7ea49aadc409
    ");

    #[test]
    fn xchacha20_keystream() {
        let mut cipher = XChaCha20::new(&Key::from(KEY), &XNonce::from(IV));

        // The test vectors omit the first 64-bytes of the keystream
        let mut prefix = [0u8; 64];
        cipher.apply_keystream(&mut prefix);

        let mut buf = [0u8; 304];
        cipher.apply_keystream(&mut buf);
        assert_eq!(&buf[..], &KEYSTREAM[..]);
    }

    #[test]
    fn xchacha20_encryption() {
        let mut cipher = XChaCha20::new(&Key::from(KEY), &XNonce::from(IV));
        let mut buf = PLAINTEXT.clone();

        // The test vectors omit the first 64-bytes of the keystream
        let mut prefix = [0u8; 64];
        cipher.apply_keystream(&mut prefix);

        cipher.apply_keystream(&mut buf);
        assert_eq!(&buf[..], &CIPHERTEXT[..]);
    }
}

// Legacy "djb" version of ChaCha20 (64-bit nonce)
#[cfg(feature = "legacy")]
#[rustfmt::skip]
mod legacy {
    use chacha20::{ChaCha20Legacy, Key, LegacyNonce};
    use cipher::{NewCipher, StreamCipher, StreamCipherSeek};
    use hex_literal::hex;

    cipher::stream_cipher_test!(chacha20_legacy_core, ChaCha20Legacy, "chacha20-legacy");
    cipher::stream_cipher_seek_test!(chacha20_legacy_seek, ChaCha20Legacy);

    const KEY_LONG: [u8; 32] = hex!("
        0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20
    ");

    const IV_LONG: [u8; 8] = hex!("0301040105090206");

    const EXPECTED_LONG: [u8; 256] = hex!("
        deeb6b9d06dff3e091bf3ad4f4d492b6dd98246f69691802e466e03bad235787
        0f1c6c010b6c2e650c4bf58d2d35c72ab639437069a384e03100078cc1d735a0
        db4e8f474ee6291460fd9197c77ed87b4c64e0d9ac685bd1c56cce021f3819cd
        13f49c9a3053603602582a060e59c2fbee90ab0bf7bb102d819ced03969d3bae
        71034fe598246583336aa744d8168e5dfff5c6d10270f125a4130e719717e783
        c0858b6f7964437173ea1d7556c158bc7a99e74a34d93da6bf72ac9736a215ac
        aefd4ec031f3f13f099e3d811d83a2cf1d544a68d2752409cc6be852b0511a2e
        32f69aa0be91b30981584a1c56ce7546cca24d8cfdfca525d6b15eea83b6b686
    ");

    #[test]
    #[ignore]
    fn chacha20_offsets() {
        for idx in 0..256 {
            for middle in idx..256 {
                for last in middle..256 {
                    let mut cipher =
                        ChaCha20Legacy::new(&Key::from(KEY_LONG), &LegacyNonce::from(IV_LONG));
                    let mut buf = [0; 256];

                    cipher.seek(idx as u64);
                    cipher.apply_keystream(&mut buf[idx..middle]);
                    cipher.apply_keystream(&mut buf[middle..last]);

                    for k in idx..last {
                        assert_eq!(buf[k], EXPECTED_LONG[k])
                    }
                }
            }
        }
    }
}

So the line causing the segfault is

let mut buf = [0u8; MAX_SEEK];

And sure enough, after compiling it with cpufeatures v0.2.2 or with zeroize disabled, the segfault disappeared.

@newpavlov
Copy link
Member

Hm, interesting. I think it makes the compiler bug hypothesis more probable.

@talchas
Copy link

talchas commented Sep 3, 2022

Running the test with coredumps enabled (ulimit -c unlimited, where they end up depends on /proc/sys/kernel/core_pattern) is the easy way of getting a gdb session with the crash. This shows the crash as being in the plt jmp for memset, which is spicy:

(gdb) disas
Dump of assembler code for function memset@plt:
=> 0x565b1200 <+0>:     jmp    *0x80(%ebx)
   0x565b1206 <+6>:     push   $0xe8
   0x565b120b <+11>:    jmp    0x565b1020
(gdb) print $ebx
$2 = 0

[in the calling function _ZN4core3ops8function6FnOnce9call_once17hc696d536ea7e3abaE]
   0x565babc2 <+1618>:  sub    $0x4,%esp
   0x565babc5 <+1621>:  mov    0x8(%esp),%ebx
   0x565babc9 <+1625>:  push   $0x200
   0x565babce <+1630>:  push   $0x0
   0x565babd0 <+1632>:  lea    0xfc(%esp),%eax
   0x565babd7 <+1639>:  push   %eax
   0x565babd8 <+1640>:  call   0x565b1200 <memset@plt>
=> 0x565babdd <+1645>:  add    $0x10,%esp

(gdb) x/20 $esp
0xf79ec91c:     0x565babdd      0xf79eca20      0x00000000      0x00000200
0xf79ec92c:     0x565ba57f      0x00000000      0x00000000      0x00000000
This one right here was put in %ebx --^

Looking at the rest of the function I am extremely suspicious of the cpuid calls that might clobber clang's/i686's use of %ebx. This is the stdlib function, and it has a somewhat subtle error, so moving to the stdlib issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
security Security vulnerabilities
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants