GSoC'22: Shadow Stack
While the previous blog post described the first steps took into the direction of familiarizing myself with Unikraft and an initial attempt to using
clang's ShadowCallStack, in this post, we will take a look into some implementations that were tried in the meantime.
Following the findings of previous weeks of investigation work, the issue of
clang not providing a runtime for the ShadowCallStak was resolved by coming up with a primitive constructor which would take care of initializing the Shadow Stack.
This constructor had to be moved in the
ukboot library, more specifically, in
boot.c, in order to be called at the same time (at bootstrapping) with the other Unikraft specific constructors and init functions.
As a result, the constructor cannot maintain its current form, as it brings critical overhead to the boot process, which, following Unikraft’s vision, should be highly performant.
Documenting each and every step took by this stage was a must, and you’ll be able to find my investigation work here; understanding the layout of both
traps_arm64.c facilitated debugging work.
The biggest milestone achieved until now was bringing Shadow Stack support for a simple app (
helloworld, to be more precise).
This constitutes the key part of the draft PR and the whole documentation of the process can be found here.
As a Proof of Concept, I used both
gdb to investigate the content of the Shadow Stack and
assembly code to prove the way the prologue and epilogue of the function is modified by this security mechanism.
What’s more, improving the performance of the aforementioned constructor was anticipated and a first step taken into this direction was giving the Shadow Stack the same size as the regular, visible stack.
The biggest problem faced during this stage was debugging my Unikraft application; catching traps isn’t fun, but the community helped and a straightforward solution to debugging an
AArch64 app was documented.
Nevertheless, not realizing that the
x18 register wasn’t fixed (using the
-ffixed-x18 flag) for all Unikraft functions was another issue which was finally resolved with help from my mentors.
At the request of my mentors, I embarked on a mission to document the reason as to why the
clang's version of the ShadowCallStack was dropped.
The reason seems to be quite obscure, but after putting the pieces together, I was left with the conclusion that a Time-Of-Check-To-Time-Of-Use (TOCTOU) vulnerability was the main actor in this complicated play.
But more on that in my documentation.
Testing the Shadow Stack support (which proved to work fine on the
helloworld app) on other, more complex applications (such as
nginx) is an important first step forward.
I’ve already started working on these apps, here it is a sneak peak:
|App\Compiler||gcc - x86||gcc - aarch64||clang - x86||clang - aarch64||clang with scs||gcc-12 with scs|
The second step consists of providing a simple ROP attack and observing how the execution behaves under these circumstances.
Thirdly, it should also be taken into consideration how my implementation would influence future security mechanisms (CET or CFI).
I aim to provide a proposal which will facilitate fitting both Shadow Stack and other related means of securing against ROP attacks together, on Unikraft.