ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

ci(x509): smoke test x509 via fuzzing - #446

Merged
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz
Feb 28, 2022
Merged

ci(x509): smoke test x509 via fuzzing#446
tarcieri merged 1 commit into
RustCrypto:masterfrom
bstrie:fuzz

Conversation

@bstrie

@bstriebstrie commented Feb 23, 2022

Copy link
Copy Markdown
Contributor

This adds fuzzing targets to the x509 crate that make use of libfuzzer (via cargo-fuzz) to fuzz types whose parsers are expected to be exposed to untrusted inputs: x509::request::{CertReq, CertReqInfo}.

On their own, the fuzzing targets can be run manually via cargo fuzz run <name of binary>. Anyone hunting for parser bugs in this crate needs only to fire up the fuzzer and let it run for as long as they like.

This commit also adds a CI action that runs each fuzzer for 30 seconds. This action serves as a smoke test to provide a basic degree of confidence in the quality of any PR that touches this crate.

Note: this PR is absolutely going to fail CI for now. It turns out that the parsers in question have a number of panics that are quite trivial to surface. The fact that it's so easy to crash these parsers should hopefully demonstrate the value of these smoke tests. However, obviously this should not be merged until these panics are fixed. :P

@bstrie

Copy link
Copy Markdown
ContributorAuthor

As expected, fuzzing has caused CertReq::try_from to panic. The problematic input:

[48, 130, 3, 9, 48, 130, 1, 241, 2, 100, 49, 1, 48, 0, 20, 48, 18, 6, 3, 85, 4, 3, 12, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 49, 20, 48, 18, 6, 3, 85, 4, 7, 12, 11, 76, 111, 115, 32, 65, 110, 103, 101, 110, 101, 115, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, 10, 67, 97, 108, 105, 102, 111, 114, 110, 105, 97, 49, 20, 48, 18, 6, 3, 85, 4, 10, 12, 11, 69, 120, 97, 109, 112, 108, 101, 32, 73, 110, 99, 49, 11, 48, 9, 6, 3, 2, 19, 6, 85, 4, 85, 83, 48, 130, 1, 34, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0, 3, 130, 1, 15, 0, 48, 130, 1, 48, 2, 130, 1, 1, 0, 191, 89, 247, 254, 113, 109, 222, 71, 199, 53, 121, 202, 132, 110, 250, 141, 48, 171, 54, 18, 224, 214, 165, 36, 32, 74, 114, 202, 142, 80, 201, 244, 89, 81, 61, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 240, 215, 51, 49, 190, 211, 215, 162, 218, 122, 54, 39, 25, 228, 113, 238, 106, 157, 135, 130, 125, 16, 36, 237, 68, 96, 90, 185, 180, 143, 59, 128, 140, 94, 23, 59, 159, 62, 196, 0, 61, 87, 241, 113, 132, 137, 245, 199, 160, 66, 28, 70, 251, 213, 39, 164, 10, 180, 186, 107, 157, 177, 106, 84, 93, 30, 207, 110, 42, 86, 51, 189, 128, 89, 78, 186, 74, 254, 231, 31, 99, 225, 211, 87, 198, 78, 154, 63, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 114, 240, 175, 33, 163, 95, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 189, 199, 97, 32, 13, 251, 39, 218, 246, 149, 98, 49, 29, 139, 25, 27, 126, 239, 170, 226, 248, 214, 246, 184, 55, 70, 168, 133, 195, 115, 243, 82, 121, 135, 228, 194, 180, 175, 127, 228, 212, 234, 22, 64, 94, 94, 21, 40, 93, 217, 56, 130, 58, 161, 142, 38, 52, 186, 254, 132, 122, 118, 28, 175, 171, 176, 64, 29, 63, 160, 58, 7, 169, 208, 151, 203, 176, 199, 113, 86, 204, 254, 54, 19, 29, 173, 241, 193, 9, 194, 130, 57, 240, 163, 114, 95, 175, 33, 53, 142, 120, 131, 4, 192, 199, 139, 149, 23, 57, 217, 31, 171, 255, 208, 122, 168, 205, 79, 105, 116, 107, 61, 14, 180, 88, 116, 105, 249, 211, 159, 79, 2, 48, 0, 48, 11, 6, 3, 85, 29, 15, 4, 4, 3, 2, 5, 160, 48, 29, 6, 3, 85, 29, 37, 4, 22, 48, 20, 6, 8, 43, 6, 1, 5, 5, 7, 3, 1, 6, 8, 43, 6, 1, 5, 5, 7, 3, 2, 48, 22, 6, 3, 85, 29, 17, 4, 15, 48, 35, 130, 11, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 48, 13, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 11, 5, 0, 3, 130, 1, 1, 0, 43, 5, 60, 254, 129, 198, 84, 33, 118, 189, 112, 179, 115, 165, 252, 141, 197, 7, 136, 201, 194, 52, 19, 39, 3, 41, 10, 49, 172, 39, 0, 229, 83, 57, 89, 2, 38, 213, 229, 130, 236, 97, 134, 152, 98, 118, 159, 216, 91, 69, 242, 135, 255, 221, 109, 181, 48, 153, 93, 49, 249, 77, 125, 44, 38, 239, 63, 72, 161, 130, 195, 2, 108, 198, 152, 243, 130, 167, 47, 26, 17, 227, 198, 137, 149, 48, 85, 218, 192, 223, 235, 233, 205, 177, 99, 202, 58, 243, 63, 252, 77, 160, 246, 184, 75, 157, 124, 221, 67, 33, 15, 255, 255, 255, 197, 40, 222, 255, 151, 21, 255, 217, 212, 115, 10]

@bstrie
bstrie marked this pull request as draft February 23, 2022 15:19
@bstriebstrie mentioned this pull request Feb 23, 2022
@tarcieri

Copy link
Copy Markdown
Member

Can you rebase now that #445 is merged?

@bstriebstrie changed the title ci(pkcs10): smoke test pkcs10 via fuzzingci(x509): smoke test x509 via fuzzingFeb 24, 2022
@tarcieri

tarcieri commented Feb 24, 2022

Copy link
Copy Markdown
Member

I managed to reproduce the panic and get the stack trace:

---- decode_rsa_2048_der stdout ----
thread 'decode_rsa_2048_der' panicked at 'source slice length (100) does not match destination slice length (1)', der/src/asn1/integer/uint.rs:33:45
stack backtrace:
0: rust_begin_unwind
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/std/src/panicking.rs:498:5
1: core::panicking::panic_fmt
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/panicking.rs:116:14
2: core::slice::<impl [T]>::copy_from_slice::len_mismatch_fail
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3205:13
3: core::slice::<impl [T]>::copy_from_slice
at /rustc/9d1b2106e23b1abd32fce1f17267604a5102f57a/library/core/src/slice/mod.rs:3212:13
4: der::asn1::integer::uint::decode_to_array
at ../der/src/asn1/integer/uint.rs:33:5
5: der::asn1::integer::<impl der::value::DecodeValue for u8>::decode_value
at ../der/src/asn1/integer.rs:80:54
6: <x509::request::Version as der::value::DecodeValue>::decode_value
at ./src/request.rs:12:45
7: <T as der::decodable::Decodable>::decode
at ../der/src/decodable.rs:34:9
8: der::decoder::Decoder::decode
at ../der/src/decoder.rs:54:9
9: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value::{{closure}}
at ./src/request.rs:32:39
10: der::asn1::sequence::SequenceRef::decode_body
at ../der/src/asn1/sequence.rs:72:22
11: <x509::request::CertReqInfo as der::value::DecodeValue>::decode_value
at ./src/request.rs:32:39

It's failing on parsing x509::request::Version, or more specifically inside of der::asn1::integer::uint::decode_to_array which is being called with a bogus length.

The implementation is not validating that there is sufficient space in the output buffer when attempting to add leading zeroes. So this is ultimately a bug in the der crate's integer parser.

Edit: prospective fix in #447

tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
tarcieri added a commit that referenced this pull request Feb 24, 2022
The previous implementation used `saturating_sub` rather than
`checked_sub` to compute the number of leading zeroes to use, which
would cause a panic if the input exceeded the output (see #446).
This commit switches to `checked_sub`, returning `ErrorKind::Length` in
the event the output buffer is too small for the given input. It also
adds unit tests for this behavior as well as the happy paths.
@tarcieri

Copy link
Copy Markdown
Member

@bstrie if you rebase, #447 should ensure you get a der::Result::Err for that input instead of a panic

This adds fuzzing targets to the x509 crate that make use of libfuzzer
(via cargo-fuzz) to fuzz types whose parsers are expected to be exposed
to untrusted inputs: x509::request::{CertReq, CertReqInfo}.
On their own, the fuzzing targets can be run manually via
`cargo fuzz run <name of binary>`. Anyone hunting for parser bugs in this
crate needs only to fire up the fuzzer and let it run for as long as they like.
This commit also adds a CI action that runs each fuzzer for 30 seconds.
This action serves as a smoke test to provide a basic degree of confidence in
the quality of any PR that touches this crate.
@bstrie
bstrie marked this pull request as ready for review February 25, 2022 17:39
@bstrie

Copy link
Copy Markdown
ContributorAuthor

@tarcieri I actually had a patch fixing the bug on my end, but I was letting the fuzzer run on it for a few hours to see if it would find anything else and you beat me to the punch. :) In any case, this looks fairly solid now. How do you feel about having this run in CI? I'm happy to increase or reduce the time as you see fit; they're your CI minutes, after all. :P

@tarcieri

Copy link
Copy Markdown
Member

@bstrie might be interesting to e.g. run on a schedule so it doesn’t block the normal CI flow, but in general it seems like a good idea

@npmccallum

Copy link
Copy Markdown
Contributor

@bstrie I think fuzzing both CertReq and CertReqInfo is redundant (just do the former). OTOH, we probably want fuzzing on Certificate.

@tarcieri

Copy link
Copy Markdown
Member

Will go ahead and land this.

If it turns out to take up too much CI time, we can convert it to a scheduled job.

@tarcieri
tarcieri merged commit 46ed300 into RustCrypto:masterFeb 28, 2022
Sign up for freeto 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.

3 participants

@bstrie@tarcieri@npmccallum