-
Notifications
You must be signed in to change notification settings - Fork 2.2k
tidb_query_vec_executors: Fixed stack-borrowing undefined-behavior #7709
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
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 <andersrb@gmail.com>
nrc
left a comment
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 <andersrb@gmail.com>
|
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 <andersrb@gmail.com>
…ikv#7709) Signed-off-by: Brian Anderson <andersrb@gmail.com>
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 andersrb@gmail.com