-
Notifications
You must be signed in to change notification settings - Fork 49
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 possible use-after-free in chained future implementation #5927
Conversation
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.
Excellent!
@@ -432,6 +442,18 @@ static void chained_future_init (flux_future_t *f, void *arg) | |||
fulfill_next (f, cf->next); | |||
} | |||
|
|||
void cf_next_destroy (struct chained_future *cf) |
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.
Make static?
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.
ooh, good catch 👍
Problem: There is trailing whitespace and other formatting in src/common/libflux/composite_future.c that does not meet current project norms. Remove trailing whitespace. Split long function arguments and conditionals one per line where necessary.
Problem: There is redundant code in chained_future_create() when the constructor is called for the same future twice, e.g. when using flux_future_and_then(3) _and_ flux_future_or_then(3). Just skip the redundant code by returning early if the chained future aux item is already present in the target future.
Problem: If the next future in a chained future is destroyed before the previous future, then a use-after-free can occur if the previous future is eventually fulfilled, since chained_continuation() and/or flux_future_continue(3) reference the now destroyed cf->next. This condition can occur, for example, if a timeout is specified in a call to flux_future_then(3) on cf->next, but can also occur in cases where cf->next is destroyed early for other reasons. Additionally, a reasonable expectation is that when next is destroyed, this prevents prev (and any other previous futures) callbacks from being called, either by destroying them or by other means. Arrange to notify the chained_future object when cf->next is destroyed by using an aux_item destructor. Destroy cf->prev if it is non-NULL. Ensure cf->prev is not subject to double-free by nullifying it when cf->prev is destroyed. This allows cf->prev and cf->next to be destroyed in any order safely. Since cf is associated in the aux_item list of cf->prev, and it is now referenced by cf->next as well, add a refcount to this object and take a reference for both cf->prev and cf->next. When both references are released the object will be destroyed. Fixes flux-framework#5923
Problem: A function in test/composite_future.c is called flux_future_timeout() which is confusing since this is not an exported libflux symbol. Rename the function simply future_timeout().
Problem: No tests in the testsuite ensure that there is not a use-after-free when the next future in a chain is destroyed before the previous if fulfilled. Add a repoducer to the composite_future unit tests.
Ok, fixed the non-static function and will set MWP. Thanks! |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5927 +/- ##
=======================================
Coverage 83.34% 83.34%
=======================================
Files 514 514
Lines 82974 83001 +27
=======================================
+ Hits 69158 69181 +23
- Misses 13816 13820 +4
|
This PR fixes the problem with chained futures described in #5923: if a
next
future in a chain is destroyed before theprev
future, then a segfault due to use-after-free could occur later ifprev
is eventually fulfilled since this references next.The case where this is likely to occur is when
flux_future_then(3)
is called with a timeout on anext
future, since the timeout will fulfillnext
with an error without even touching theprev
future.The fix is not exactly elegant. A reference to the
chained_future
object is placed in thenext
future'saux
list, so that when it is destroyed it can (optionally) destroyprev
. However, this needs to be avoided in the common case whereprev
is destroyed beforenext
, so an aux_item destructor is also placed inprev
to nullifycf->prev
so that future is not double freed. Finally, since the underlyingchained_future
object is now referenced in multiple places, and these are not called in any guaranteed order, that structure gets a reference counter and a reference count of 3:prev
aux_item list asflux::chained
next
aux_item listprev
aux_item list to callnullify_prev
A test is added and was tested under valgrind etc.