Thread (82 messages) 82 messages, 10 authors, 2021-05-21

Re: extending ucontext (Re: [PATCH v26 25/30] x86/cet/shstk: Handle signals for shadow stack)

From: Andy Lutomirski <luto@kernel.org>
Date: 2021-05-06 23:31:24
Also in: linux-arch, linux-doc, linux-mm, lkml

On Thu, May 6, 2021 at 3:05 PM Yu, Yu-cheng [off-list ref] wrote:
On 5/4/2021 1:49 PM, Yu, Yu-cheng wrote:
quoted
On 4/30/2021 11:32 AM, Yu, Yu-cheng wrote:
quoted
On 4/30/2021 10:47 AM, Andy Lutomirski wrote:
quoted
On Fri, Apr 30, 2021 at 10:00 AM Yu, Yu-cheng [off-list ref]
wrote:
quoted
On 4/28/2021 4:03 PM, Andy Lutomirski wrote:
quoted
On Tue, Apr 27, 2021 at 1:44 PM Yu-cheng Yu [off-list ref]
wrote:
quoted
When shadow stack is enabled, a task's shadow stack states must be
saved
along with the signal context and later restored in sigreturn.
However,
currently there is no systematic facility for extending a signal
context.
There is some space left in the ucontext, but changing ucontext is
likely
to create compatibility issues and there is not enough space for
further
extensions.

Introduce a signal context extension struct 'sc_ext', which is
used to save
shadow stack restore token address.  The extension is located
above the fpu
states, plus alignment.  The struct can be extended (such as the
ibt's
wait_endbr status to be introduced later), and sc_ext.total_size
field
keeps track of total size.
I still don't like this.
[...]
quoted
quoted
quoted
quoted
quoted
That's where we are right now upstream.  The kernel has a parser for
the FPU state that is bugs piled upon bugs and is going to have to be
rewritten sometime soon.  On top of all this, we have two upcoming
features, both of which require different kinds of extensions:

1. AVX-512.  (Yeah, you thought this story was over a few years ago,
but no.  And AMX makes it worse.)  To make a long story short, we
promised user code many years ago that a signal frame fit in 2048
bytes with some room to spare.  With AVX-512 this is false.  With AMX
it's so wrong it's not even funny.  The only way out of the mess
anyone has come up with involves making the length of the FPU state
vary depending on which features are INIT, i.e. making it more compact
than "compact" mode is.  This has a side effect: it's no longer
possible to modify the state in place, because enabling a feature with
no space allocated will make the structure bigger, and the stack won't
have room.  Fortunately, one can relocate the entire FPU state, update
the pointer in mcontext, and the kernel will happily follow the
pointer.  So new code on a new kernel using a super-compact state
could expand the state by allocating new memory (on the heap? very
awkwardly on the stack?) and changing the pointer.  For all we know,
some code already fiddles with the pointer.  This is great, except
that your patch sticks more data at the end of the FPU block that no
one is expecting, and your sigreturn code follows that pointer, and
will read off into lala land.
Then, what about we don't do that at all.  Is it possible from now
on we
don't stick more data at the end, and take the relocating-fpu approach?
quoted
2. CET.  CET wants us to find a few more bytes somewhere, and those
bytes logically belong in ucontext, and here we are.
Fortunately, we can spare CET the need of ucontext extension.  When the
kernel handles sigreturn, the user-mode shadow stack pointer is
right at
the restore token.  There is no need to put that in ucontext.
That seems entirely reasonable.  This might also avoid needing to
teach CRIU about CET at all.
quoted
However, the WAIT_ENDBR status needs to be saved/restored for signals.
Since IBT is now dependent on shadow stack, we can use a spare bit of
the shadow stack restore token for that.
That seems like unnecessary ABI coupling.  We have plenty of bits in
uc_flags, and we have an entire reserved word in sigcontext.  How
about just sticking this bit in one of those places?
Yes, I will make it UC_WAIT_ENDBR.
Personally, I think an explicit flag is cleaner than using a reserved
word somewhere.  However, there is a small issue: ia32 has no uc_flags.

This series can support legacy apps up to now.  But, instead of creating
too many special cases, perhaps we should drop CET support of ia32?

Thoughts?
I'm really not thrilled about coupling IBT and SHSTK like this.

Here are a couple of possible solutions:

- Don't support IBT in 32-bit mode, or maybe just don't support IBT
with legacy 32-bit signals.  The actual mechanics of this could be
awkward.  Maybe we would reject the sigaction() call or the
IBT-enabling request if they conflict?

- Find some space in the signal frame for these flags.  Looking around
a bit, sigframe_ia32 has fpstate_unused, but I can imagine things like
CRIU getting very confused if it stops being unused.  sigframe_ia32
uses sigcontext_32, which has a bunch of reserved space in __gsh,
__fsh, etc.

rt_sigframe_ia32 has uc_flags, so this isn't a real problem.

I don't have a brilliant solution here.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help