Whether this is worthwhile, or even works without holes, is sort of an open question. I agree it's sounds a little heavy on the serpent fat, but the technical promise is definitely achievable.
It arranges things such that this sensitive data is only ever in the clear in registers (never memory),
and is saved in an encrypted memory region called the secure stack on context switchesFrom the article
For data confidentiality, in addition to the call stack management described previously, Ginseng must also intercept all exceptions to save sensitive registers to the callstack before the exception can be handled by the OS. Ginseng intercepts exceptions using dynamic trapping. A NOP instruction is inserted at the beginning of the exception vector code in the kernel, and replaced by a call to the secure monitor at runtime when sensitive data enters registers. (Once the registers are clear of sensitive data, the NOP is restored). Once the OS serves the exception, control is handed back to the app. GService manages the return address to ensure we resume at the correct point in the sensitive function.
Ouch, my performance!
.
Write to kernel code pages?!?
Ouch, my cache! Oh, and my security!
And yes, you have to trust Ginseng more than you trust the OS, it's the whole point and an explicit initial premise.
Basically the threat model seems to be “malicious kernel”, but if the kernel is actually malicious then it can do whatever it wants before the target has an opportunity to “protect” itself
From Intel x86 manual
To aid in handling exceptions and interrupts, each architecturally defined exception and each interrupt condition requiring special handling by the processor is assigned a unique identification number, called a vector number. The processor uses the vector number assigned to an exception or interrupt as an index into the interrupt descriptor table (IDT). The table provides the entry point to an exception or interrupt handler (see Section 6.10, “Interrupt Descriptor Table (IDT)”).
The allowable range for vector numbers is 0 to 255. Vector numbers in the range 0 through 31 are reserved by the Intel 64 and IA-32 architectures for architecture-defined exceptions and interrupts. Not all of the vector numbers in this range have a currently defined function. The unassigned vector numbers in this range are reserved. Do not use the reserved vector numbers.
Vector numbers in the range 32 to 255 are designated as user-defined interrupts and are not reserved by the Intel 64 and IA-32 architecture. These interrupts are generally assigned to external I/O devices to enable those devices to send interrupts to the processor through one of the external hardware interrupt mechanisms (see Section 6.3, “Sources of Interrupts”).
But it's still not relevant, as the kernel can arbitrarily halt execution of any process at any point, and therefore can read the content of all registers whenever it wants them.
Again, if your attack model is a malicious kernel nothing you're doing in user mode is going to protect you. If you're using kernel APIs to install protections against that, you're still dealing with a malicious kernel that can ignore or wrap whatever you do.
If you're trying to mitigate/protect against kernel bugs that's a different threat model, but the same general problems exist only by accident so are less likely to leak useful information.
Not if the kernel can't handle interruptions? The only way for the kernel to "steal control" from the process is through an interruption (software or hardware). If all interrupts are first handled by the secure monitor, which handles saving the registers securely, then it's OK.
In fact, if I was a malicious kernel, I would do precisely that
This whole idea will not work unless your hypervisor is a real complete hypervisor. This attempt at a halfway-hypervisor-lite is doomed to failure for this and many other reasons.
We now describe Ginseng’s runtime protection against such accesses. The runtime protection heavily relies on GService, a passive, app-independent piece of software in the Secure world. GService ensures the code integrity, data confidentiality and control-flow integrity (CFI). It does so only for sensitive functions to minimize overhead. It also modifies the kernel at three points, when booting, when modifying the kernel page table, and when handling an exception. Since we do not trust the OS, the kernel may overwrite the modifications. However, when any of these modifications is disabled, the kernel will infinitely trigger data aborts trying to modify readonly memory, thus ensuring sensitive data are always safe.
I would have look at the source code to find more but the github repository has been deleted.
[1] https://en.wikipedia.org/wiki/Trusted_execution_environment
https://en.wikipedia.org/wiki/ARM_architecture#Security_exte...
This sort of thing is exactly why TEE exists. It cannot be done half in userspace half in TEE
1. the os in charge of the hypervisor
2. the os in charge of the virtualised system.
We're saying that we don't trust 2. so we're going to get the kernel from 1. to intercept interrupts. But that means we already have a trustworthy kernel. The one running the hypervisor, so why aren't we just using that?