-
Notifications
You must be signed in to change notification settings - Fork 225
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
Fix use-after-free in unit tests #686
Conversation
I suspect this was behind the elusive hangs we were seeing in CI as well... |
Need to fix pool setup/teardown in server.c as well. |
This prevents a use-after-free of the pool's async handle. Signed-off-by: Cole Miller <[email protected]>
The loop must run to completion before calling pool_fini. Signed-off-by: Cole Miller <[email protected]>
It's unsound to uv_close the async handle and then immediately free the pool_impl_t, because libuv will still hold a pointer to the handle. Avoid the need to do this by calling uv_async_init last, so there's no need to roll it back when a later operation fails. Signed-off-by: Cole Miller <[email protected]>
Signed-off-by: Cole Miller <[email protected]>
0c19a1e
to
1a8cd58
Compare
I discussed this offline with @just-now, who pointed out that the pool was designed based on the assumption that pool_fini would not be called until the associated event loop had been run to completion. This makes it sound to free the However, there is a bigger issue: |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #686 +/- ##
==========================================
- Coverage 77.87% 77.87% -0.01%
==========================================
Files 197 197
Lines 27953 27955 +2
Branches 5534 5532 -2
==========================================
+ Hits 21768 21769 +1
+ Misses 4346 4345 -1
- Partials 1839 1841 +2 ☔ View full report in Codecov by Sentry. |
There's a race here that we haven't figured out how to address. Signed-off-by: Cole Miller <[email protected]>
* released, until pool_fini is called. Before calling pool_fini, the event loop | ||
* that was used to create this pool must be run to completion (that is, until | ||
* uv_run returns 0). |
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.
Just to check my understanding, here we are exploiting the fact that (source):
Handles that wrap file descriptors are closed immediately
So calling uv_close will close the handle and we can free it immediately after. However, we still have to run the event loop to completion prior to doing that because the handles might be in use resulting in UAF.
Is that right?
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.
So calling uv_close will close the handle and we can free it immediately after.
No, it won't be closed immediately (uv_async_t
doesn't wrap an fd). It'll be closed asynchronously, but we don't free the allocation until after the event loop has exited, which implies that all handles including the pool's async handle have finished closing.
Free the pool's
pool_impl_t
containing theoutq_async
handle only after the callback passed touv_close
has fired, in line with libuv rules, and in contrast to the original code, which could free this allocation while libuv was still holding a pointer into it. This UAF showed up in CI runs for #682 but is hard to reproduce otherwise.Note that dqlite as used in production is not affected by this bug since the thread pool is not even created in that case.
Signed-off-by: Cole Miller [email protected]