We continue discussing the paper "Shielding applications from an untrusted cloud with Haven,” written by Andrew Baumann, Marcus Peinado, and Galen Hunt. We discuss the various issues encountered. We now review SGX optimizations that were recommended by this Haven study. One was Exception Handling and the other was Demand Loading.
When an exception is reported to the user, SGX provides the register context and information to shield module. The shield then performs sanity-checks to ensure that the exception is valid, it is in the shield module and that it should be reported to the LibOS. When the shield prepares to deliver the exception to the LibOS, it copies the context and cause in a format defined by the drawbridge ABI. The context is modified to run the LibOS exception handler. Haven resumes the modified context. But the ERESUME instruction is only available outside the enclave. In fact both ERESUME and IRET are illegal in an enclave. The shield, therefore, EEXITs to a small untrusted stub that immediately resumes the enclave, restoring the context. The LibOS then runs and the application exception handlers get a chance which typically finish by executing the IRET to restore the original context. But the IRET is an illegal instruction and so another exception is triggered. A single application exception results in two exceptions and eight enclave crossings. If these instructions were permitted or their replacements provided, then it would be a significant performance win.
Demand loading is the other scenario where optimization has been made possible. Haven's shield loads application and LibOS binaries. Initially however it did not load lazily. A lazy load is so called because the virtual address space is reserved but the pages are allocated and filled only on first access. As loading occurs, other threads may also access the same pages. Demand paging is done using a private memory mapping before remapping pages with appropriate permissions in the final location. But SGX does not support moving an existing page. Haven loads all the binaries even before a page fault occurs. This adds overhead to startup because there may be many DLLs that are to be loaded costing time and also space. An optimization would be to include an instruction which allows a new page to be both allocated and initialized with a copy of the data from the private memory mapping. Then this new page can be made available to the application. Thus demand loading can be another win.
#codingexercise
In a complete undirected graph, there are n nodes numbered 1 to n and there are k forbidden edges. Find the number of Hamiltonian cycles in this graph that don't contain the forbidden edges. Assume that order of traversal does not matter.
A Hamiltonian cycle is one which visits all the vertices exactly once. A forbidden edge that connects two vertices must not be in this cycle. The graph is complete and undirected so every pair has an edge. Therefore a node can reach every other node. When the nodes are listed in the order of the Hamiltonian cycle, we see that those nodes can be jumbled in any way because each node can reach every other node. Consequently the problem degenerates to finding the permutations of the sequence 1 to n. such that the forbidden edges don't appear.
Permutations of a string:
Void Permute (String a, StringBuilder b, bool[] used, List<string> edges)
{
If ( b.Length == a.Length) { if ( edges.Any(x => b.contains(x)) == false) print b.ToString(); return;}
For (int I = 0; I < a.Length ; i++)
{
If (used[i]) continue;
used[i] = true;
B += A[i];
Permute(a, b, used, edges);
B [B.Length – 1] = ‘/0’;
used[i] = false;
}
}
When an exception is reported to the user, SGX provides the register context and information to shield module. The shield then performs sanity-checks to ensure that the exception is valid, it is in the shield module and that it should be reported to the LibOS. When the shield prepares to deliver the exception to the LibOS, it copies the context and cause in a format defined by the drawbridge ABI. The context is modified to run the LibOS exception handler. Haven resumes the modified context. But the ERESUME instruction is only available outside the enclave. In fact both ERESUME and IRET are illegal in an enclave. The shield, therefore, EEXITs to a small untrusted stub that immediately resumes the enclave, restoring the context. The LibOS then runs and the application exception handlers get a chance which typically finish by executing the IRET to restore the original context. But the IRET is an illegal instruction and so another exception is triggered. A single application exception results in two exceptions and eight enclave crossings. If these instructions were permitted or their replacements provided, then it would be a significant performance win.
Demand loading is the other scenario where optimization has been made possible. Haven's shield loads application and LibOS binaries. Initially however it did not load lazily. A lazy load is so called because the virtual address space is reserved but the pages are allocated and filled only on first access. As loading occurs, other threads may also access the same pages. Demand paging is done using a private memory mapping before remapping pages with appropriate permissions in the final location. But SGX does not support moving an existing page. Haven loads all the binaries even before a page fault occurs. This adds overhead to startup because there may be many DLLs that are to be loaded costing time and also space. An optimization would be to include an instruction which allows a new page to be both allocated and initialized with a copy of the data from the private memory mapping. Then this new page can be made available to the application. Thus demand loading can be another win.
#codingexercise
In a complete undirected graph, there are n nodes numbered 1 to n and there are k forbidden edges. Find the number of Hamiltonian cycles in this graph that don't contain the forbidden edges. Assume that order of traversal does not matter.
A Hamiltonian cycle is one which visits all the vertices exactly once. A forbidden edge that connects two vertices must not be in this cycle. The graph is complete and undirected so every pair has an edge. Therefore a node can reach every other node. When the nodes are listed in the order of the Hamiltonian cycle, we see that those nodes can be jumbled in any way because each node can reach every other node. Consequently the problem degenerates to finding the permutations of the sequence 1 to n. such that the forbidden edges don't appear.
Permutations of a string:
Void Permute (String a, StringBuilder b, bool[] used, List<string> edges)
{
If ( b.Length == a.Length) { if ( edges.Any(x => b.contains(x)) == false) print b.ToString(); return;}
For (int I = 0; I < a.Length ; i++)
{
If (used[i]) continue;
used[i] = true;
B += A[i];
Permute(a, b, used, edges);
B [B.Length – 1] = ‘/0’;
used[i] = false;
}
}