abi-aa icon indicating copy to clipboard operation
abi-aa copied to clipboard

[Morello] Proposal: New ABI for functions with more than 8 arguments

Open dpgao opened this issue 2 years ago • 2 comments

Summary

The Morello ABI specifies that at most 8 arguments of a C function can be passed explicitly in registers. Any additional arguments must be ‘spilled’ onto the stack and passed implicitly to the callee. This ABI assumes that the caller and the callee share the same stack.

I propose a new ABI that removes this assumption, thereby enabling the callee to execute on a different stack from the caller's if desired.

Background

Before the introduction of the new varargs ABI (#112), variadic arguments were also passed via the stack. At present, they are passed via a buffer pointed to by c9. Thus, variadic functions no longer need to scan through the stack to retrieve its variadic arguments, which used to be a source of spatial safety violations.

Rationale

The current ABI necessarily exposes the caller's stack pointer to the callee. Under situations where the callee is untrusted code, this poses a security risk as the callee has the power to corrupt the caller's stack frames. The proposed ABI does not mandate that the argument buffer be allocated on the stack, allowing flexibility in its location depending on the level of security needed. For example, if the callee is trusted by the caller, the buffer may well be allocated on the stack. Otherwise, the buffer can be allocated on a single-use page that is deallocated by the caller as soon as the callee returns.

Design

I propose that the spilled arguments be passed via the same buffer used for passing variadic arguments. Here's a sketch of the implementation:

  1. The spilled arguments are inserted at the front of the buffer.
  2. Padding space is inserted.
  3. Variadic arguments (if there're any) are inserted after the padding.
  4. Since the types of the spilled arguments are known statically, the offset at which the variadic arguments begin is also known statically.
  5. The va_start macro can thus be implemented to correctly set the va_list variable.

ABI Transition Plan

We aim to split the transition into two phases to increase compatibility. Two clang flags (-morello-bounded-memargs and -morello-bounded-memargs=caller) are added.

  1. Caller pushes to CSP, callee reads from CSP Current default
  2. Caller pushes to CSP and sets C9, callee reads from CSP. Compatible with 1 but breaks varargs with >8 fixed arguments (very rare) Enabled by -morello-bounded-memargs=caller
  3. Caller pushes to CSP and sets C9, callee reads from C9. Compatible with 2 Enabled by -morello-bounded-memargs

Technicalities

  1. When the c9 buffer is sufficiently large, its bounds become imprecise and may range over data that the caller does not intend to pass to the callee. The compiler should detect such situations and allocate the buffer at a representable alignment.
  2. When the va_start macro sets the va_list capability, it would be desirable to have that capability's lower bound increased past the region containing the spilled arguments. A carefully designed padding scheme for step 2 above will be needed to make sure that this is the case despite any address rounding.

Alternatives

An alternative to this approach is to use ‘directed capabilities’ proposed by Georges et al.. This requires hardware and ISA changes though.

A less disruptive approach is to maintain the current ABI but use a certain intermediary to copy the spilled arguments to the other stack when the callee needs to be executed on another stack. The challenges with this approach are that a) it is slower and b) it is unclear who this intermediary should be and how such an intermediary can be implemented for all possible function signatures and the resulting sets of arguments that need to be copied over.

dpgao avatar Jul 05 '22 20:07 dpgao

Just trying to gather more information about what we need this for (not saying this shouldn't be possible but this would be an ABI break). For compartment calls we would have more registers besides SP that shouldn't be exposed (any register not used for argument passing, some system registers etc). For clearing argument registers we need a different calling convention. which could be made to pass arguments via c9 instead of the stack. So we only need to change the way we're passing arguments for non-compartment calls if there is a requirement that compartment calls pass arguments the same way as non-compartment calls?

sbaranga-arm avatar Jul 06 '22 08:07 sbaranga-arm

Hello,

Thanks for your questions.

Just trying to gather more information about what we need this for (not saying this shouldn't be possible but this would be an ABI break).

Yes, this would indeed be an ABI break.

For compartment calls we would have more registers besides SP that shouldn't be exposed (any register not used for argument passing, some system registers etc). For clearing argument registers we need a different calling convention. which could be made to pass arguments via c9 instead of the stack. So we only need to change the way we're passing arguments for non-compartment calls if there is a requirement that compartment calls pass arguments the same way as non-compartment calls?

Yes, it would be nice if both compartment calls and non-compartment calls pass arguments the same way, even though compartment calls can perform some extra register clearing.

Please don’t hesitate to ask if you have more questions.

dpgao avatar Jul 06 '22 10:07 dpgao