[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v5 4/4] x86: prefer shadow stack for producing call traces


  • To: Jan Beulich <jbeulich@xxxxxxxx>, "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
  • Date: Tue, 10 Nov 2026 13:15:29 +0000
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=citrix.com; dmarc=pass action=none header.from=citrix.com; dkim=pass header.d=citrix.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=e4ZgCJOANt4PVPCl77/++w9gsNSig0s4H7Z/JmYf6Is=; b=H3o6MfOxDnPpdXXwvg6bQ4861Jm1OfnjOJ1VMe6eQhP7cKgduJe7lH/jkKB2Js5nUc4tCxpfPvNtNVefHqmiZIvzZvtjnbgO73o/HngiUsT5FbDunXu068BCob/w+lPpL4PQS60a4pSuz2IdrwsiEuM20RxcUqIgO2eQn6NvgL6lKNta8FMImLn237z6WYUR64QaJ4nCltnes+JgCfhLkriZdUG7uYPMXE01TptNWwlRGoJ6B5jLijn+dd+w/EBC5vNTkZRGj2qG4ijb0MOlr6bNwjklXFZhaWtkWY6rGrmUOL27Y3EsKR75Z1PofUC+ZYAyppOAKYJ0wo2VAyeC3w==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=B7X99LjmSSvsFcSvDNOLQkLB0QFNSdv0ezmK5DmwFtP9iZIL4e1I5XLKQW+82vhxBvum+yPf56LIKdaVmfMo4Nv9dypGwMzb/EryI3VSj/RjEZgov5OxYpSIEM0HHN6Cn3ROoeQlwdDZvT1VHgNM6F60ZicsAKCPEa6mLh1yTfob/7EXSj9Y9OECmjISZG41hX/7LTBzf4C1cN1uvpQdIybhKks21nbwRGPJ4Yawk6WDvTaIxEpE77rwpPM40uWVKHKGLgvSYgoDdyPZugDymPrq0V/+zdWLWZU1XSldN6yem0NPab6v9PdPCEfZh9KiaqxNM2da2FUD10ZejDKYpw==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=citrix.com header.i="@citrix.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=citrix.com;
  • Autocrypt: addr=andrew.cooper3@xxxxxxxxxx; keydata= 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 6+ahAA==
  • Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>, Teddy Astie <teddy.astie@xxxxxxxxxx>, Roger Pau Monné <roger@xxxxxxxxxxxxxx>
  • Delivery-date: Tue, 10 Nov 2026 13:15:40 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 09/11/2026 1:09 pm, Jan Beulich wrote:
> Shadow stacks contain little more than return addresses, and they in
> particular allow precise call traces also with FRAME_POINTER=n:
>
> (XEN) Xen call trace:
> (XEN)    [<ffff82d04032d730>] R extable.c#search_one_extable+0x70/0x73
> (XEN)    [<ffff82d04032d802>] C search_exception_table+0xc2/0x177
> (XEN)    [<ffff82d040358378>] C traps.c#extable_fixup.isra.0+0x18/0x6c
> (XEN)    [<ffff82d040358e3b>] C do_invalid_op+0xab/0x106
> (XEN)    [<ffff82d040201d98>] C 
> x86_64/entry.S#handle_exception_saved+0x88/0xf4
> (XEN)    [<ffff82d07fffe044>] E ffff82d07fffe044
> (XEN)    [<ffff82d040412db0>] C stub_selftest+0xd0/0x168
> (XEN)    [<ffff82d0403508d6>] C setup.c#init_done+0x116/0x15a
>
> as opposed to this counterpart (earlier during the same boot, before CET
> is enabled):
>
> (XEN) Xen call trace:
> (XEN)    [<ffff82d04032d730>] R extable.c#search_one_extable+0x70/0x73
> (XEN)    [<ffff82d04032d802>] S search_exception_table+0xc2/0x177
> (XEN)    [<ffff82d040358378>] S traps.c#extable_fixup.isra.0+0x18/0x6c
> (XEN)    [<ffff82d040358e3b>] S do_invalid_op+0xab/0x106
> (XEN)    [<ffff82d040201d98>] S 
> x86_64/entry.S#handle_exception_saved+0x88/0xf4
> (XEN)    [<ffff82d040412db0>] S stub_selftest+0xd0/0x168
> (XEN)    [<ffff82d0403d0cc9>] S do_initcalls+0x29/0x38
> (XEN)    [<ffff82d04041adf2>] S __start_xen+0x1c72/0x2235
> (XEN)    [<ffff82d040288a57>] S __high_start+0xb7/0xc0
>
> (note the entirely missing entry for the stub itself [1]; sadly there are
> no stray entries there).
>
> [1] Arguably we could teach FRAME_POINTER=n traces to recognize stubs as
>     well. But not FRAME_POINTER=y ones. In fact, what's missing there
>     isn't the stub itself, but (of course) its immediate caller.
>
> Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>

Sorry, but this commit message is still deceptive to read.  Please
reformat it as:

---8<---
Shadow stacks contain little more than return addresses, and they in
particular allow precise call traces also with FRAME_POINTER=n:

Compare a WARN() in stub_selftest() which runs twice on boot, first
without Shadow Stacks:

    (XEN) Xen call trace:
    ...
    (XEN)    [<ffff82d040288a57>] S __high_start+0xb7/0xc0

and later with Shadow Stacks:

    (XEN) Xen call trace:
    ...
    (XEN)    [<ffff82d0403508d6>] C setup.c#init_done+0x116/0x15a


Note that ...
---8<---

This way you are presenting the information in chronological order, as
well as stating which has CET enabled and which does not.

We probably ought to teach the emulation stubs about
CONFIG_FRAME_POINTER.  It should be consistent 


> ---
> While the 'E' for exception frames is probably okay, I'm not overly
> happy with the 'C' (for CET). I would have preferred 'S' (for shadow),
> but we use that character already.
>
> As an alternative to suppressing output for the top level exception
> frame, adding the new code ahead of the 'R' output line (and then also
> ahead of the stack top read) could be considered.
>
> Quite likely a number of other uses of is_active_kernel_text() also want
> amending with in_stub().
> ---
> v3: Correct "link to other shadow stack" check. Don't log a line for the
>     (impossible) PV case. Add example stack trace to description.
> v2: IS_ENABLED() -> #ifdef. Re-base.
>
> --- a/xen/arch/x86/traps.c
> +++ b/xen/arch/x86/traps.c
> @@ -48,6 +48,7 @@
>  #include <asm/shared.h>
>  #include <asm/shstk.h>
>  #include <asm/smp.h>
> +#include <asm/stubs.h>
>  #include <asm/system.h>
>  #include <asm/traps.h>
>  #include <asm/uaccess.h>
> @@ -705,6 +706,13 @@ unsigned long get_stack_dump_bottom(unsi
>      }
>  }
>  
> +#ifdef CONFIG_XEN_SHSTK
> +static bool in_stub(unsigned long addr)
> +{
> +    return !((this_cpu(stubs.addr) ^ addr) >> STUB_BUF_SHIFT);
> +}
> +#endif

static inline, or __maybe_unused.  This stub needs using elsewhere, and
has nothing to do with XEN_SHSTK beyond the first user.

> +
>  #if !defined(CONFIG_FRAME_POINTER)
>  
>  /*
> @@ -797,6 +805,49 @@ static void show_trace(const struct cpu_
>           !is_active_kernel_text(tos) )
>          printk("   [<%p>] R %pS\n", _p(regs->rip), _p(regs->rip));
>  
> +#ifdef CONFIG_XEN_SHSTK
> +    if ( rdssp() != SSP_NO_SHSTK )

Thinking about it, the entry condition wants to be regs->entry_ssp.

If shadow stacks where enabled at the time of taking the exception/etc,
that's good enough.

> +    {
> +        const unsigned long *ptr = _p(regs->entry_ssp);

Something Linux is gaining might be worth using here.

+struct fred_shstk_entry_frame {
+    u64 ssef_ssp;
+    u64 ssef_lip;
+    u64 ssef_cs;
+};

(Names subject to improvement, especially as this is not actually FRED
related, although cs does get extended with SL and WFE when FRED is
enabled.)

The logic below will render incorrectly on the IST stacks, I think.

In those cases, we'll get an entry_ssp pointing at the IST shstk with {
ssp, lip, cs, (maybe) sup token }, where what we want to do for
rendering the dump is to follow ssp first to get to the outer context
and then walk up.  This is different from rendering regs->rsp which
starts from the outer context.


> +        unsigned int n;
> +
> +        for ( n = 0; (unsigned long)ptr & (PAGE_SIZE - sizeof(*ptr)); ++n )
> +        {
> +            unsigned long val = *ptr;
> +
> +            if ( is_active_kernel_text(val) || in_stub(val) )
> +            {
> +                /* Normal return address entry.  */
> +                printk("   [<%p>] C %pS\n", _p(val), _p(val));
> +                ++ptr;
> +            }
> +            else if ( !((val ^ (unsigned long)ptr) >>
> +                        (PAGE_SHIFT + STACK_ORDER)) )
> +            {
> +                if ( val & (sizeof(val) - 1) )
> +                {
> +                    /* Most likely a supervisor token. */
> +                    break;
> +                }

Supervisors tokens only exist in !opt_fred cases.  But I don't see how
this is a valid check for one.  You're looking for non-zero in the
bottom 3 bits, but that's potentially ambiguous with cs and fails to
spot a non-busy token.

The distinguishing characteristic of a supervisor token is that it is
it's own address, so check (val ^ ptr) >> 1.

> +
> +                /*
> +                 * Ought to be a hypervisor interruption frame.  But don't
> +                 * (re)log the current frame's %rip.
> +                 */
> +                if ( n || ptr[1] != regs->rip )
> +                    printk("   [<%p>] E %pS\n", _p(ptr[1]), _p(ptr[1]));

Ah - this n is the thing covering the fact that you didn't move to the
outer context originally.  Please do that and drop this.

If we're going to start using E here, we should make the FRAME_POINTER
side match.  When printing something we've identified as a stub, we
ought to say "in stub" rather than leaving it as a raw number.

Not for this patch, but it might be nice to identify EFI ranges too,
because that's the only other case where we commonly get raw numbers.

> +                ptr = _p(val);
> +            }
> +            else /* Bogus. */
> +                break;
> +        }
> +
> +        /* Fall back to legacy stack trace if nothing was logged at all. */
> +        if ( n )
> +            return;

This comment wants to be after the return, not before it.

~Andrew

> +    }
> +#endif /* CONFIG_XEN_SHSTK */
> +
>      if ( fault )
>      {
>          printk("   [Fault on access]\n");
>




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.