Thread (107 messages) 107 messages, 7 authors, 2020-04-08

Re: [RFC PATCH v9 07/27] Add guard pages around a Shadow Stack.

From: Dave Hansen <hidden>
Date: 2020-02-26 18:17:34
Also in: linux-arch, linux-doc, linux-mm, lkml

On 2/5/20 10:19 AM, Yu-cheng Yu wrote:
INCSSPD/INCSSPQ instruction is used to unwind a Shadow Stack (SHSTK).  It
performs 'pop and discard' of the first and last element from SHSTK in the
range specified in the operand.
This implies, but does not directly hit on an important detail: these
instructions *touch* memory.  They don't just mess with the shadow stack
pointer, they actually dereference memory.  This makes them very
different from just manipulating %rsp and are what actually make this
guard page thing work in the first place.
The maximum value of the operand is 255,
and the maximum moving distance of the SHSTK pointer is 255 * 4 for
INCSSPD, 255 * 8 for INCSSPQ.
You could also be kind and do the math for us, reminding us that ~1k and
~2k are both very far away from the 4k guard page size.
Since SHSTK has a fixed size, creating a guard page above prevents
INCSSP/RET from moving beyond.
What does this have to do with being a fixed size?  Also, this seems
incongruous with an API that takes a size as an argument.  It sounds
like shadow stacks are fixed in size *after* allocation, which is really
different from being truly fixed in size.
Likewise, creating a guard page below
prevents CALL from underflowing the SHSTK.
The language here is goofy.  I think of any "stack overflow" as the
condition where a stack grows too large.  I don't call too-large
grows-down stacks underflows, even though they are going down in their
addressing.
quoted hunk ↗ jump to hunk
Signed-off-by: Yu-cheng Yu <redacted>
---
 include/linux/mm.h | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index b5145fbe102e..75de07674649 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2464,9 +2464,15 @@ static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * m
 static inline unsigned long vm_start_gap(struct vm_area_struct *vma)
 {
 	unsigned long vm_start = vma->vm_start;
+	unsigned long gap = 0;
 
-	if (vma->vm_flags & VM_GROWSDOWN) {
-		vm_start -= stack_guard_gap;
+	if (vma->vm_flags & VM_GROWSDOWN)
+		gap = stack_guard_gap;
+	else if (vma->vm_flags & VM_SHSTK)
+		gap = PAGE_SIZE;
Comments, please.  There is also a *lot* of stuff that has to go right
to make PAGE_SIZE OK here, including the rather funky architecture of a
single instruction.

It seems cruel and unusual punishment to future generations to make them
chase git logs for the logic rather than look at a nice code comment.

I think it's probably also best to have this be

	gap = ARCH_SHADOW_STACK_GUARD_GAP;

and then you can give the full rundown about the sizing logic inside the
arch/x86/include definition.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help