On Thu, 2023-11-16 at 15:35 +0000, Mark Brown wrote:
On Thu, Nov 16, 2023 at 01:55:07PM +0000, Szabolcs.Nagy@arm.com wrote:
The 11/16/2023 12:33, Mark Brown wrote:
On Thu, Nov 16, 2023 at 10:32:06AM +0000, Szabolcs.Nagy@arm.com wrote:
i guess the tricky case is stack!=0 && shadow_stack_size==0: the user may want a new shadow stack with default size logic, or (with !CLONE_VM || CLONE_VFORK) wants to use the existing shadow stack from the parent.
If shadow_stack_size is 0 then we're into clone() behaviour and doing the default/implicit handling which is to do exactly what the above describes.
to be clear does clone with flags==CLONE_VM|CLONE_VFORK always use the parent shadow stack independently of the stack argument?
!CLONE_VM rather than CLONE_VM but yes, that's what the clone() and hence current clone3() behaviour is here.
"flags & CLONE_VM" gets a new shadow stack, unless also "flags & CLONE_VFORK". Other flags in there are not consulted for the logic of whether to create a new shadow stack.
So CLONE_VM|CLONE_VFORK will use the parent shadow stack.
!CLONE_VM will also sort of use the same shadow stack, but it's a COW one.
Now that I've thought about it more, removing the CLONE_VFORK part of the logic has several downsides. It is a little extra work to create and unmap a shadow stack for an operation that is supposed to be this limited fast thing.
It also will change the SSP(let me know if anyone has a general term we can use) for the child. So if you have like: ssp = _get_ssp() if (!vfork()) { foo = *ssp; ... }
...it's awkward edge. In the vfork man page it points to fork which has the text: "The child process is an exact duplicate of the parent process except for the following points", which obviously doesn't include SSP.
Lastly, there are already cases where the x86 glibc implementation stays on the shadow stack when it switches regular stacks (i.e. sigaltstack()). vfork children are not supposed to return, so it should normally work to be on the same shadow stack. So it's not a special situation unless we can resolve those other situations, which are limited by the stack lifetime issues.
What about a CLONE_NEW_SHSTK for clone3 that forces a new shadow stack? So keep the existing logic, but the new flag can override the logic for !CLONE_VM and CLONE_VFORK if the caller wants. The behavior of shadow_stack_size is then simple. 0 means use default size, !0 means use the passed size. No need to overload and tie up args->stack.
In the other direction though... CLONE_VFORK can be used to stay on the existing shadow stack and possibly corrupt it. This connects with earlier discussions around signals dropping a token before being handled and the overflow use case, and trying to guarantee one thread per shadow stack at a time, etc. So if there is any inclination towards trying to get that, it might actually be useful for another reason. It will close one method for getting two threads on the same shadow stack at the same time (one is sleeping yes, but it's the same problem in effect).