This is an archive of the discontinued LLVM Phabricator instance.

Check return address stored in normal stack and CET shadow stack in unwind process phase2
ClosedPublic

Authored by xiongji90 on Oct 25 2022, 1:30 AM.

Details

Summary

If CET shadow stack is enabled, we will count the number of stack frames skipped and adjust CET shadow stack based on the number in libunwind unwind_phase2. When unwinding stack, we can compare the return address in normal stack against counterpart in CET shadow stack, if they don't match, it means the return address stored in normal stack has been corrupted, we will return _URC_FATAL_PHASE2_ERROR in that case. We don't check for non-catchable exception since such exceptions will lead to process termination.

Diff Detail

Event Timeline

xiongji90 created this revision.Oct 25 2022, 1:30 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptOct 25 2022, 1:30 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
xiongji90 requested review of this revision.Oct 25 2022, 1:30 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 25 2022, 1:30 AM

Hi, @hjl.tools
Could you take a look at this patch, we aim to align with libgcc's behavior in CET shadow stack unwinding process in order to enhance security.

Thanks very much.

MaskRay added a subscriber: MaskRay.EditedOct 25 2022, 4:07 PM

Is there any way to validate correctness without a CET-capable machine?
Is there any emulator

Is there any way to validate correctness without a CET-capable machine?
Is there any emulator

Hi, @MaskRay
I am afraid we don't have anyway to validate correctness without a CET-enable machine since the intrinisc "_get_ssp" will return 0 in machine without CET and all the check based on comparison between normal stack ret addr and CET shadow stack ret addr will be skipped.
Currently, I set up a CET enabled machine and ran all libunwind, libcxx, libcxxabi tests in CET mode to validate this patch. In testing, we need to also build libcxx, libcxxabi and corresponding test with CET options on.

Thanks very much.

thanks, I assume @MaskRay is a good reviewer for it.

MaskRay added a comment.EditedOct 31 2022, 2:20 PM

Is this check a libgcc behavior? Is it intended for just C++ exceptions (regular phase2) or also for _Unwind_ForcedUnwind?

Can you craft an example to demonstrate a failure caught by the new check? It is going to be brittle so I can understand that a test is likely inappropriate.

For C++ exceptions exception_object->exception_class != 0 is always true while for _Unwind_ForcedUnwind exception_class is typically zero, so I am not sure having the code in unwind_phase2_forced is useful...

libunwind/src/UnwindLevel1.c
317

#ifdef

In libgcc, the same macro is used for both _Unwind_RaiseException_Phase2 and _Unwind_ForcedUnwind_Phase2.
_Unwind_ForcedUnwind usages in glibc always terminate and don't unwind stack. We may not need to check
return address on shadow stack in this case.

libunwind/src/UnwindLevel1.c
317

#ifdef

Is this check a libgcc behavior? Is it intended for just C++ exceptions (regular phase2) or also for _Unwind_ForcedUnwind?

Can you craft an example to demonstrate a failure caught by the new check? It is going to be brittle so I can understand that a test is likely inappropriate.

For C++ exceptions exception_object->exception_class != 0 is always true while for _Unwind_ForcedUnwind exception_class is typically zero, so I am not sure having the code in unwind_phase2_forced is useful...

Hi, @MaskRay
If so, I will remove unnecessary check in unwind_phase2_forced and where can I find any document or description about
"For C++ exceptions exception_object->exception_class != 0 is always true while for _Unwind_ForcedUnwind exception_class is typically zero"?
Thanks very much.

Thanks very much.

xiongji90 updated this revision to Diff 472225.Nov 1 2022, 1:10 AM
xiongji90 marked 2 inline comments as done.
xiongji90 added inline comments.
libunwind/src/UnwindLevel1.c
317

Done.

xiongji90 updated this revision to Diff 472226.Nov 1 2022, 1:13 AM
xiongji90 edited the summary of this revision. (Show Details)
xiongji90 added a comment.EditedNov 1 2022, 1:58 AM

Hi, @MaskRay
Unnecessary check in forced unwind is removed. This patch is to align with libgcc's behavior in stack unwinding process on CET enabled platform. libgcc added check in stacking unwinding phase2, we need to go through the stacks to count how many stack frames to skip when CET is enabled and we can enhance security by comparing return addr in normal stack against CET shadow stack at the same time. If return addr in the 2 stacks don't match, it means the normal stack has been corrupted by someone and we can report fatal error in advance.
We also ran libunwind, libcxx, libcxxabi tests with CET enabled with latest patch, all tests passed.

Thanks very much.

MaskRay accepted this revision.Nov 3 2022, 11:04 PM

LGTM.

This revision is now accepted and ready to land.Nov 3 2022, 11:04 PM
This revision was landed with ongoing or failed builds.Nov 8 2022, 10:12 PM
This revision was automatically updated to reflect the committed changes.