[PATCH v2 00/14] Support RISC-V Control Flow Integrifty (CFI)
Jesse Huang
jesse.huang@sifive.com
Tue Jul 29 06:15:54 GMT 2025
On Sat, Jul 26, 2025 at 8:54 PM Vivian Wang <wangruikang@iscas.ac.cn> wrote:
> On 7/23/25 17:12, Jesse Huang wrote:
> > Hi Henrich,
> >
> > Thanks for your question.
> > For dynamic binaries, the loader performs a check on all dependencies
> > listed in link_map, and CFI features (SS, LP) will only be enabled
> > respectively only if property notes present in all of the dependencies.
> > I have summarized the possible combinations and their behavior here.
> >
> > 1) Binary w/o CFI
> > No compatibility problem
> >
> > 2) Binary w/ CFI + dependency w/ CFI
> > CFI will be enabled and should be no problem
> >
> > 3) Binary w/ CFI + dynamic loading library w/o CFI
> > a. If the mode is always-on (default), dlopen will fail.
> > b. If the mode is permissive (can be set by glibc tunables),
> > CFI will be disabled
> >
> > 4) Binary w/ CFI + dependency w/o CFI
> > CFI will not be enabled because of the dependency check won't pass
> >
> > The only case that causes problem is attempting to execute shadow stack
> > instructions on a RVA20 machine, in this case they will be illegal
> > instructions due to lacking the support of Zimop/Zcmop, but this is
> > unlikely to happen.
>
> The problems is that, AFAICT, now the size of jmp_buf depends on
> __riscv_shadow_stack. You have mentioned this in passing in the cover
> letter, but this is a huge problem for the software ecosystem because
> there will always be binary code that depends on the size of jmp_buf by
> e.g. using it in a struct.
>
> We did that to s390 [1] back in 2014 once. It was bad. Let's not do that
> again.
>
> Can we learn from what x86 and aarch64 did that did not cause this
> problem? My understanding is that they used some otherwise unused
> storage in the full jmp_buf, but I'm not sure about the details.
>
>
Thanks Vivian, this is definitely something I didn't consider.
x86 implemented this by making sigset_t a union that contains the original
sigset_t, or a
shrinked sigset_t and a shadow stack pointer. To leverage the unused space
in sigmask
without affecting the sigset_t's size
/* Number of bits per long. */
#define _JUMP_BUF_SIGSET_BITS_PER_WORD (8 * sizeof (unsigned long int))
/* The biggest signal number. As of kernel 4.14, x86 _NSIG is 64. The
common maximum sigset for i386, x32 and x86-64 is 12 bytes (96 bits).
Define it to 96 to leave some rooms for future use. */
#define _JUMP_BUF_SIGSET_NSIG 96
/* Number of longs to hold all signals. */
#define _JUMP_BUF_SIGSET_NWORDS \
(ALIGN_UP (_JUMP_BUF_SIGSET_NSIG, _JUMP_BUF_SIGSET_BITS_PER_WORD) \
/ _JUMP_BUF_SIGSET_BITS_PER_WORD)
typedef struct
{
unsigned long int __val[_JUMP_BUF_SIGSET_NWORDS];
} __jmp_buf_sigset_t;
typedef union
{
__sigset_t __saved_mask_compat;
struct
{
__jmp_buf_sigset_t __saved_mask;
/* Used for shadow stack pointer. NB: Shadow stack pointer
must have the same alignment as __saved_mask. Otherwise
offset of __saved_mask will be changed. */
unsigned long int __shadow_stack_pointer;
} __saved;
} __jmpbuf_arch_t;
#undef __sigset_t
#define __sigset_t __jmpbuf_arch_t
This is similar to our implementation for ucontext in that we shrunk the
sigset_t for extra space,
and x86's way is apparently better because our approach also affects the
size of sigset_t (thus jmp_buf)
which I didn't notice. I will change to x86's approach in the next version.
Deepak, do you have any concern with this approach?
Thanks,
Jesse Huang
> Thanks,
> Vivian "dramforever" Wang
>
> [1]: https://lwn.net/Articles/606377/
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20250729/6c404693/attachment.htm>
More information about the Libc-alpha
mailing list