-
Notifications
You must be signed in to change notification settings - Fork 5.7k
SERVER-12006 Fixed potential pointer overflow leading to an infinite loop in db/storage/record.cpp #570
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
Conversation
Jared, can you be more precise about why you believe this is potentially undefined behavior? What specifically may overflow? In what scenarios? Also, you're calling _netLength() on every pass through the loop. Awful from a performance perspective. Finally, please read CONTRIBUTING.rst. We'll need a SERVER ticket and for you to sign the contributor's agreement. |
I'm absolutely sorry. I should have checked for the contributing file before doing this. Hopefully the updated message above is more precise. If it is not, please let me know. Here is my account: https://jira.mongodb.org/browse/SERVER-12006?jql=Participants%20%3D%20jaredlwong Not sure how to allow you to see that I signed the contributing form. Maybe you're able to see it on jira? |
I also updated the commit such that _netLength is not called on every iteration of the for loop. |
src/mongo/db/storage/record.cpp
Outdated
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 you mean 2048 here?
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.
Sorry, I'll fix that. I did mean 2048.
Jared, I confirmed that you signed the agreement. Thanks for the ticket! I've assigned it to Andy for review. |
…e loop in db/storage/record.cpp In order to iterate over the data in the record there was a previous loop that compared pointers. This will not work as expected because the addr pointer may overflow. In C, it is undefined behavior to increment a pointer (in this case addr) more than 1 past the end of the array (in this case _data) to which it points. If the compiler so chooses, it may wrap around the pointer if it increases past _data + SliceSize + 1. This would cause this loop to never exit because addr would always be less than end.
Hi Jared, Matt - Matt, I believe the undefined behavior referred to here is from C++11 5.7.5 [expr.add], which states
The situation is complicated by the 'break' statement in the loop. Right now, the increment of 'addr' never actually occurs, since we always break out of the loop after one iteration. However, if we were to remove the 'break' statement, then this could elicit undefined behavior when incrementing 'addr' past the end of the array after the loop body. The fact that this invalid pointer will not be dereferenced is irrelevant, just synthesizing it is UB. So I think Jared's proposed change makes sense, in that with his change in place, if the 'break' statement is ever removed, then the code will not suddenly elicit UB. |
@acmorrow This discussion reminded me of a blog post by Chris Westin, where he describes how he picked up his habit of "incrementing the pointer" when writing for loops https://www.bookofbrilliantthings.com/blog/revisiting-some-old-for-loop-lore It used to generate much faster code; now only slightly faster. |
Did you read his addendum? I think when he ran his test in GCC 4, the On Fri, Dec 13, 2013 at 3:57 PM, Matt Kangas notifications@github.comwrote:
|
@andy10gen Yes. Also, he's only single-stepping a pointer, rather than advancing by 2048. |
Merged in 9ebb366 Thank you for contributing to MongoDB, Jared! |
In summary: we don't copy hot-backup files; when we load the hot backup metadata file during WiredTiger startup, if there are entries in the metadata file without a valid checkpoint, we assume they're bulk-loaded files and re-create them with no contents. Implement Alex's idea: don't walk the metadata file in the hot-backup code looking for 'file:' objects, instead use __wt_meta_btree_apply() plus a callback instead. Clarify __wt_conn_btree_apply() and __wt_meta_btree_apply(): we talk about EBUSY errors, but all we can handle are bulk-loaded files. Rename __wt_conn_btree_apply_single() to __wt_conn_btree_apply_bulk(), and change it to only operate on bulk-loaded files. Increase the size of the metadata backup line we can handle from 512B to 5KB; this should be a dynamic value, but I'm not going to do it right now. Reference: mongodb#570, mongodb#653.
Fixed potential pointer overflow leading to an infinite loop in
db/storage/record.cpp
In order to iterate over the data in the record there was a previous loop that
compared pointers:
this will not work as expected because the addr pointer may overflow.
Consider the size of _data, where it is defined in mongo::ps::Slice as:
Here _netLength is defined as:
in mongo/db/pdfile.h. Where HeaderSize = 16 and _lengthWithHeaders is set in a
number of places through the function:
in mongo/db/pdfile.h.
Assuming we can't guarantee anything about the exact value of _netLength (for
example, that it's always exactly a multiple of 2048) then there will be an
issue with the addr pointer overflowing the bounds of _data.
In C, it is undefined behavior to increment a pointer (in this case addr) more
than 1 past the end of the array (in this case _data) to which it points.
If the compiler so chooses, it may wrap around the pointer if it increases past
_data + SliceSize + 1. This would cause this loop to never exit because addr
would always be less than end.