Re: [PATCH v4 19/36] arm64/gcs: Allocate a new GCS for threads with GCS enabled

Mark Brown
2023-08-18T20:15:48Z

[-- Attachment #1.1: Type: text/plain, Size: 2897 bytes --]

On Fri, Aug 11, 2023 at 05:26:03PM +0100, Catalin Marinas wrote:
> On Mon, Aug 07, 2023 at 11:00:24PM +0100, Mark Brown wrote:

> > + mmap_write_lock(mm);
> > + mapped_addr = do_mmap(NULL, addr, size, PROT_READ, flags,
> > + VM_SHADOW_STACK | VM_WRITE, 0, &unused, NULL);

> Why not PROT_WRITE as well? I guess I need to check the x86 patches
> since the do_mmap() called here has a different prototype than what's in
> mainline.

> This gets confusing since currently the VM_* flags are derived from the
> PROT_* flags passed to mmap(). But you skip the PROT_WRITE in favour of
> adding VM_WRITE directly.

I have to confess that I inherited this from the x86 code and never
thought too hard about it. I've got a horrible feeling the reasoning is
simply the way in which x86 fits shadow stack into the page tables
without having a mechanism like permission indirection, these don't
apply for us.

> I haven't followed the x86 discussion but did we run out of PROT_* bits
> for a PROT_SHADOW_STACK?

It's more that there are security concerns with having PROT_, especially
in conjunction with needing to provide a token for stack pivot - we not
only need to map pages for the GCS, we also need to write a cap token
into it so that we can pivot to the new stack. If the GCS can ever be
written to by userspace via normal means then that's an issue for the
basic protection model that the feature is trying to implement. If we
have the PROT_ but try to check for bad uses of it that makes everything
messy and complicated which is especially non-ideal for a feature with a
security focus. Having a more packaged system call is easier for
everyone.

More detail in the x86 patch that's currently in -next:

https://lore.kernel.org/all/20230319001535.23210-34-rick.p.edgecombe@intel.com/

> > + /* Allocate RLIMIT_STACK with limits of PAGE_SIZE..4G */
> > + size = PAGE_ALIGN(min_t(unsigned long long,
> > + rlimit(RLIMIT_STACK), SZ_4G));
> > + return max(PAGE_SIZE, size);
> > +}

> I saw Szabolcs commenting on the default size as well. Maybe we should
> go for RLIMIT_STACK/2 but let's see how the other sub-thread is going.

I've updated it.

> > + if ((clone_flags & (CLONE_VFORK | CLONE_VM)) != CLONE_VM)
> > + return 0;

> Is it safe for CLONE_VFORK not to get a new shadow stack? A syscall for
> exec could push something to the stack. I guess the GCS pointer in the
> parent stays the same, so it wouldn't matter.

Yes, pushing should be fine just as for the regular stack.

> That said, I think this check should be somewhere higher up in the
> caller of gcs_alloc_thread_stack(). The copy_thread_gcs() function
> already does most of the above checks. Is the GCS allocation called from
> elsewhere as well?

That's the only place. I've moved the above check into copy_thread_gcs(),
you're right that the other checks are redundant as they're done in the
caller already.

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

[-- Attachment #2: Type: text/plain, Size: 161 bytes --]