Skip to content
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

c18n: Fix rtld_bind reentry bug #2134

Merged
merged 8 commits into from
Jul 5, 2024
Merged

c18n: Fix rtld_bind reentry bug #2134

merged 8 commits into from
Jul 5, 2024

Conversation

dpgao
Copy link
Contributor

@dpgao dpgao commented Jun 29, 2024

Just for CI. Do not merge yet.

This should fix #2130.

@dpgao dpgao changed the title [WIP] c18n: Fix rtld_bind reentry bug c18n: Fix rtld_bind reentry bug Jun 30, 2024
@dpgao dpgao requested a review from jrtc27 July 1, 2024 13:50
* stack be used because the signal handler uses the recorded size of
* the stack to determine whether it has been allocated.
*/
static char dummy_stk;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ugly, surely there's a better way. Where does this end up being used to determine that the compartment is RTLD, anyway? Isn't the trusted stack tracking the compartment ID?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the trusted stack tracking the compartment ID?

This is generally true except at certain points in the trampoline where the trusted stack becomes temporarily out-of-sync with the currently installed untrusted stack.

The signal handler needs to handle such situations and makes a few guesses about the actual compartment that the current untrusted stack belongs to. It does so by checking whether the current untrusted stack is a subset of one of the candidate compartments' stack. Hence the dummy stack must be tagged and have positive length to prevent compartments from impersonating as RTLD.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"makes a few guesses about the actual compartment that the current untrusted stack belongs to" doesn't fill me with confidence as to the security of this mechanism

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The security argument definitely has lots of room for improvement. But at least signal handling should be functionally correct now.

static inline struct trusted_frame *
pop_dummy_rtld_trusted_frame(struct trusted_frame *tf)
{
assert(get_trusted_stk() == tf);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why not just make it implicit?...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to retain the assertion because it would be very hard to debug if something goes wrong here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does asserting that the input is the one valid value achieve? The function can just use get_trusted_stk() itself, it doesn't need an argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main worry is that somebody pushed to the trusted stack and forgot to pop. The assertion helps to catch that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And how does this code help catch that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, if somebody pushed but forgot to pop, then we will have get_trusted_stk() < tf.

Copy link
Contributor Author

@dpgao dpgao Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in, this assertion checks that nobody's made unbalanced push/pops to the trusted stack between the pair of push and pop that I am doing right now.

* current compartment is RTLD must be pushed.
*/
static inline struct trusted_frame *
push_dummy_rtld_trusted_frame(struct trusted_frame *tf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only case when this isn't get_trusted_stk() is when you first create the stack. Implicitly use get_trusted_stk(), and then upon creation you just need to set_trusted_stk() before calling this.

@jrtc27
Copy link
Member

jrtc27 commented Jul 1, 2024

Please also add a regression test to cheribsdtest_ifunc.c

@dpgao
Copy link
Contributor Author

dpgao commented Jul 4, 2024

@jrtc27 I've added a regression test. Regarding the use of implicit arguments, I'm still in favour of explicit arguments because then we are able to track canonical state of the trusted stack instead of relying on a special register, which might be corrupted between a push and a pop.

dpgao and others added 8 commits July 4, 2024 15:34
The check for benchmark ABI was erroneously added and conflicts with benchmark ABI-only code below.
This is a common pattern and warrants shared code.
The dummy frame indicates that the current compartment is RTLD. Because
_rtld_bind may cause domain switches (e.g., via a ifunc resolver which
calls a lazily-bound symbol), a frame that correctly identifies the
current compartment as RTLD is necessary.

The signal handler has also been updated to handle the temporary
inconsistency between the untrusted stack and the callee field of the
topmost truted frame that result from the changes above.
The current method of popping the topmost trusted frame and restoring it
at the end of the function does not correctly identify the current
compartment as RTLD.

Instead, push a dummy frame that does so.
This prevents a compartment from being able to set its stack to NULL and
then trigger a signal that would cause it to be mis-identified as RTLD.
The dummy frame indicates that the current compartment is RTLD. Because
_rtld_tlsdesc_dynamic may cause domain transitions (e.g., locking), a
frame that correctly identifies the current compartment as RTLD is
necessary.
@dpgao dpgao merged commit 905deea into dev Jul 5, 2024
29 checks passed
@dpgao dpgao deleted the dpgao-patch-1 branch July 5, 2024 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants