jsonbuilder: attempt to handle memory allocation errors - v2#8855
jsonbuilder: attempt to handle memory allocation errors - v2#8855jasonish wants to merge 6 commits into
Conversation
Some very minor changes to formatting.
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. Ticket: OISF#6057
Provide a wrapper around "push" and "push_str" on the internal buffer that will "try_reserve" data before growing in an attempt to handle memory allocation errors. Ticket: OISF#6057
Convert encode_string() to use try_reserve to catch memory allocation errors.
As base64 encoding is done by a 3rd party library directly into the buffer and there is no "try" style function, first reserve enough data to fit the complete base64 encoded string (and then some).
Required minor updates to users of the base64 crate.
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## master #8855 +/- ##
==========================================
+ Coverage 82.25% 82.26% +0.01%
==========================================
Files 969 969
Lines 273176 273207 +31
==========================================
+ Hits 224689 224752 +63
+ Misses 48487 48455 -32
Flags with carried forward coverage won't be shown. Click here to find out more. |
|
WARNING:
Pipeline 13706 |
|
Have you tried running jsonbuilder unit tests with an allocation failure framework ? (ie a LD_PRELOAD overload malloc to fail, running the tests X times, X being the number of allocations, and making each allocation fail) |
No. Any existing examples of this? |
|
Like https://github.com/tavianator/oomify but there may be others (and it is quite easy to recode) |
|
Jason, I had created this https://redmine.openinfosecfoundation.org/issues/5851 for your info |
Will look. I did test in a low memory Docker container, unfortunately the Linux OOM kicks in before Rust is able to detect an allocation error. So while this may test if the code works OK, I wonder if it would actually ever error out in production on Linux, or just get killed by the kernel anyways. |
Quick test with The panic there is just a result on my unwrap, so that shows that if Rust isn't killed by the kernel, the error is rippled back to the application. |
|
Where can I see test-jsonbuilder.rs source ? |
Its just in my working directory, not something suitable to check in, so no PR... |
| /// than building onto the buffer. | ||
| /// | ||
| /// TODO: Allocate memory in a fallible way. | ||
| /// TODO: Revisit this, would be nice to build directly onto the |
There was a problem hiding this comment.
Is it to do in this PR ?
There was a problem hiding this comment.
In a way, that one todo had to be removed, and another way came to mind at the same time.
| fn encode_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> { | ||
| // Before base64 encoding, make sure enough space is reserved. | ||
| if self.buf.capacity() < self.buf.len() + val.len() * 2 { | ||
| self.buf.try_reserve(val.len() * 2)?; |
There was a problem hiding this comment.
base64 is rather ((val.len()+2)/3)*4 no ?
There was a problem hiding this comment.
like one byte gets encoded as 4 bytes
There was a problem hiding this comment.
Ah yeah, mines too small. base64 has a function for this, will just use that.
|
Do not you need to check also |
|
And make |
|
|
catenacyber
left a comment
There was a problem hiding this comment.
Some nits to address ;-)
I wonder if some cases should do a big reserve before multiple push_str like open_array doing
self.push('"')?;
self.push_str(key)?;
self.push_str("\":[")?;
Maybe. But as the reserve behind these does 4k size, the first that needs it will allocate enough. My first iteration did try to estimate the size and reserve at once, but it did seem a little error prone. But not harm to estimate it, reserve once so the reserve shouldn't be triggered again during the call into jsonbuilder. Not sure if it'll have a noticeable affect other than fail a little earlier if it was going to fail. |
|
Commented addressed (well most of them) here: #8901 |
Previous PR: #8847
Ticket: https://redmine.openinfosecfoundation.org/issues/6057
Attempt to handle all "normal" memory allocation errors in JsonBuilder where
the Rust APIs allow us. This primarily means growing the underlying String
buffer as data is added.
As the JsonBuilder already used a Result for most methods, most users of
JsonBuilder already handle errors.
Changes from last PR:
Things to investigate:
experimental API that will allow us to handle this better.