-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
tidb_query_vec_executors: Fixed stack-borrowing undefined-behavior #7709
Conversation
I don't understand the stacked borrowing rules enough to understand why the existing code is a violation. It is creating a mutable unsafe pointer; then taking some immutable borrows to the same data while doing nothing with that pointer; then later using the pointer. Simply moving the creation of the unsafe pointer to immediately before it is used removes the error. Found with miri, which reports: ``` error: Undefined Behavior: trying to reborrow for SharedReadOnly, but parent tag <1121039> does not have an appropriate item in the borrow sta ck --> components/tidb_query_vec_executors/src/slow_hash_aggr_executor.rs:281:32 | 281 | let offset_begin = self.group_key_buffer.len(); | ^^^^^^^^^^^^^^^^^^^^^ trying to reborrow for SharedReadOnly, but parent tag <1121039> does not have an ap propriate item in the borrow stack | = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information ``` Signed-off-by: Brian Anderson <[email protected]>
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 believe it is UB because the old line 280 takes a reference to self.group_key_buffer
, but then inside the for
loop self.group_key_buffer
is mutated. This is equivalent to holding two &mut
pointers, which is of course not allowed. This is only possible at all because the into
creates a NonNull
, which is basically a *mut
pointer. It is not unsafe to have multiple *mut
pointers as long as they are not dereferenced, but I guess miri is still treating it as UB.
Anyway, the fix LGTM
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.
The code is trying to inserting string keys to a HashMap. Strings are generated dynamically. To avoid allocating memory for each string, strings are appended to a memory buffer first. And then, the pointer to the buffer, as well as the appending offset are inserted to the HashMap instead.
In this pattern, a non-mut reference (however implemented by using NonNull
) to the buffer is always kept and stored in HashMap, while the buffer is meanwhile mutated to append more strings.
/merge |
Your auto merge job has been accepted, waiting for:
|
/run-all-tests |
@brson merge failed. |
/merge |
/run-all-tests |
@brson merge failed. |
/run-cherry-picker |
…ikv#7709) Signed-off-by: Brian Anderson <[email protected]>
cherry pick to release-4.0 in PR #7714 |
If you truly have two (Actually isn't |
Thanks @RalfJung !
|
It is, but you are creating it from a shared reference it looks like, which means you must not use it for mutation.
OTOH, Miri catches those bugs, so I am not entirely sure what is actually happening here. Unfortunately the
|
I should clarify: Raw pointers, once created, do not care about their type. But how you create a raw pointer initially (from a safe reference) matters. Casting a reference to
|
Thanks @RalfJung ! |
…ikv#7709) Signed-off-by: Brian Anderson <[email protected]>
…ikv#7709) Signed-off-by: Brian Anderson <[email protected]>
…7709) (#7714) Signed-off-by: Brian Anderson <[email protected]> Co-authored-by: Brian Anderson <[email protected]>
What problem does this PR solve?
This fixes undefined behavior in tidb_query_vec_executors. Miri reports a stacked borrowing error as described below.
Note that stacked borrowing is a way to model Rust's use of unsafe pointers, but is not officially part of Rust's memory model. Thus, this is not truly undefined behavior today, but could be eventually.
I don't understand the stacked borrowing rules enough to understand why the
existing code is a violation. It is creating a mutable unsafe pointer; then
taking some immutable borrows to the same data while doing nothing with that
pointer; then later using the pointer.
Simply moving the creation of the unsafe pointer to immediately before it
is used removes the error.
cc @RalfJung not sure if you like being tagged on miri stacked borrow reports. If you have time though maybe you could explain what's going on here.
Found with miri, which reports:
Signed-off-by: Brian Anderson [email protected]