Thread (54 messages) 54 messages, 6 authors, 2023-07-12

Re: [PATCH v9 23/42] Documentation/x86: Add CET shadow stack description

From: H.J. Lu <hidden>
Date: 2023-06-21 23:06:04
Also in: linux-arch, linux-doc, linux-mm, lkml

On Wed, Jun 21, 2023 at 3:23 PM Edgecombe, Rick P
[off-list ref] wrote:
On Wed, 2023-06-21 at 11:54 -0700, Rick Edgecombe wrote:
quoted
quoted
quoted
quoted
quoted
quoted
quoted
there is no magic, longjmp should be implemented as:

        target_ssp = read from jmpbuf;
        current_ssp = read ssp;
        for (p = target_ssp; p != current_ssp; p--) {
                if (*p == restore-token) {
                        // target_ssp is on a different
shstk.
                        switch_shstk_to(p);
                        break;
                }
        }
        for (; p != target_ssp; p++)
                // ssp is now on the same shstk as
target.
                inc_ssp();

this is what setcontext is doing and longjmp can do the
same:
for programs that always longjmp within the same shstk
the
first
loop is just p = current_ssp, but it also works when
longjmp
target is on a different shstk assuming nothing is
running
on
that shstk, which is only possible if there is a restore
token
on top.

this implies if the kernel switches shstk on signal entry
it has
to add a restore-token on the switched away shstk.
Wait a second, the claim is that the kernel should add a restore token
on the current shadow stack before handling a signal, to allow to
unwind from an alt shadow stack, right? But in this series there is not
an alt shadow stack, so signal will be handled on the current shadow
stack. If the user stays on the current shadow stack, the existing
simple INCSSP based solution will work.

If the user swapcontext()'s away while handling a signal (which *is*
currently supported) they will leave their own restore token on the old
stack. Hypothetically glibc could unwind back through a series of
ucontext stacks by pivoting, if it kept some metadata somewhere about
where to restore to. So there are actually already enough tokens to
make it back in this case, glibc just doesn't do this.

But how does the proposed token placed by the kernel on the original
stack help this problem? The longjmp() would have to be able to find
the location of the restore tokens somehow, which would not necessarily
be near the setjmp() point. The signal token could even be on a
different shadow stack.

So I think the above is short of a design for a universally compatible
longjmp().

Which makes me think if we did want to make a more compatible longjmp()
a better the way to do it might be an arch_prctl that emits a token at
the current SSP. This would be loosening up the security somewhat (have
to be an opt-in), but less so then enabling WRSS. But it would also be
way simpler, work for all cases (I think), and be faster (maybe?) than
INCSSPing through a bunch of stacks.
Since longjmp isn't required to be called after setjmp, leaving a restore
token doesn't work when longjmp isn't called.
I'm also not sure leaving a token on signal doesn't weaken the security
it it's own way as well. Any thread could then swap to that token.
Where as the shadow stack signal frame ssp pointer can only be used
from the shadow stack the signal was handled on.

So I think, in addition to blocking the shadow stack overflow use case
in the future, leaving a token behind on signal will not really help
longjmp(). (or at least I'm not following)

-- 
H.J.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help