-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[draft] Example of catching panics in Rust #8757
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #8757 +/- ##
==========================================
- Coverage 82.25% 82.24% -0.01%
==========================================
Files 969 969
Lines 273176 273215 +39
==========================================
+ Hits 224689 224714 +25
- Misses 48487 48501 +14
Flags with carried forward coverage won't be shown. Click here to find out more. |
WARNING:
Pipeline 13305 |
let _ = catch_unwind(|| { | ||
let state = cast_pointer!(state, DNSState); | ||
state.parse_request_udp(flow, stream_slice); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to understand why we're using it here. I'm reading Don't build your programs to unwind under normal circumstances. Ideally, you should only panic for programming errors or extreme problems.
at https://doc.rust-lang.org/nomicon/unwinding.html where I also see You must absolutely catch any panics at the FFI boundary!
so I kind of see why we want to do this. But,
- What points of panic/undefined behavior can happen on the enclosed code?
- How do we know that the panic caused here for whatever reason would be implemented with unwinding? Or, do we not care about it as it is not incurring any runtime cost in either case?
Thank you for this! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference https://redmine.openinfosecfoundation.org/issues/3333
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to understand why we're using it here. I'm reading
Don't build your programs to unwind under normal circumstances. Ideally, you should only panic for programming errors or extreme problems.
at https://doc.rust-lang.org/nomicon/unwinding.html where I also seeYou must absolutely catch any panics at the FFI boundary!
so I kind of see why we want to do this. But,
- What points of panic/undefined behavior can happen on the enclosed code?
Simple ones that we can catch would be programming errors like walking off the end of an array. In C this might create a segfault, but is a catchable error in Rust. These warnings are more to prevent people from using unwinding as a flow control mechanism, or treating them like exceptions when coming from C++, Python, etc.
I also disagree about the must on FFI boundaries. Currently, we crash if the Rust code panics at the FFI boundary, and that's a reasonable outcome.
- How do we know that the panic caused here for whatever reason would be implemented with unwinding? Or, do we not care about it as it is not incurring any runtime cost in either case?
We don't care. If the panic is unwindable we'll catch it and not crash. If it's not catchable, perhaps a deep-rooted segfault, or an illegal instruction the program should abort and crash.
All the Rust web frameworks like Axum, Rocket, Warp, implement such unwinding in their request handlers, so one bad request handler won't take down the server, this is a very similar approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the explanation!
Do I get it right that if there is actually an error that can be unwinded, it'll affect the performance negatively?
Do we log it in stats somehow to indicate what's causing the possible slowdown?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do I get it right that if there is actually an error that can be unwinded, it'll affect the performance negatively?
Yes, it should a little as it has to do some resetting of data. Plus that line that gets log that we can't control is slow. All prints to the console are slow though.
The alternative is crashing.
rust/src/dns/dns.rs
Outdated
} else if !stream_slice.is_empty() { | ||
return state.parse_response_tcp(flow, stream_slice); | ||
} | ||
if x[99] {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I didn't mean to commit this introduce error, this is why QA is so far off.
c10b896
to
dfa6101
Compare
WARNING:
Pipeline 13347 |
I think this looks good for using with big expensive calls like going into the parser. What are your thoughts about the frequent much smaller calls like into json builder? Can we wrap these at minimal cost too, or should we rework those to not be able to panic (to a reasonable level of course)? IIRC @catenacyber was seeing panics in jsonbuilder when experimenting with alloc failures. |
Indeed cf google/oss-fuzz#9902 |
I will have to do some tests around |
For What is the solution ? |
I think the idea is for more than just allocations, just any panic that can be unwound. As all the That crate could be interesting to look at, but this unwinding is a little more generic. |
Indeed I get it |
Rust 1.57.0 as |
dfa6101
to
a84a836
Compare
Wrap DNS probing/parsing in catch_unwind as an example how to gracefully handle panics from Rust.
Some very minor changes to formatting.
a84a836
to
66bd9c1
Compare
WARNING:
Pipeline 13683 |
rust/src/jsonbuilder.rs
Outdated
@@ -604,7 +647,11 @@ impl JsonBuilder { | |||
/// than building onto the buffer. | |||
#[inline(always)] | |||
fn encode_string(&mut self, val: &str) -> Result<(), JsonError> { | |||
let mut buf = vec![0; val.len() * 2 + 2]; | |||
let size = val.len() * 2 + 2; | |||
let mut buf = Vec::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this fail as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, flagged as a TODO
in my new branch/pr specifically for JsonBuilder.
rust/src/jsonbuilder.rs
Outdated
let boxed = Box::new(JsonBuilder::new_object()); | ||
Box::into_raw(boxed) | ||
match JsonBuilder::try_new_object() { | ||
Ok(js) => Box::into_raw(Box::new(js)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is Box::new()
also doing an alloc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is. Box::try_new
is an experimental API that would be ideal here, but until then it should probably be wrapped in a panic handler. I need to actually look into if Rust memory allocations panic or abort, the latter not being catchable.
Information: QA ran without warnings. Pipeline 13684 |
Convert "new_object" and "new_array" functions that return a Result and use "try_reserve" to allocate the amount of data requested. This should allow memory allocation errors to be detected and handled in a Rust-ful matter without resorting to catching a panic.
35e2cd1
to
9cbae4e
Compare
I guess the issue of handling memory allocation errors is still in question. Rust has these So when it comes to allocated memory for data, we should do our best with |
Information: QA ran without warnings. Pipeline 13705 |
@catenacyber are you able to test how this behaves in your alloc tests? |
This would probably be the better one to test: #8855 I'm going to close this one and and this branch of pull requests will become more for catching panics in parsers.. |
https://redmine.openinfosecfoundation.org/issues/3333