TEE Is Not a Healer: Rollback-Resistant Reliable Storage
Abstract
Recent advances in secure hardware technologies, such as Intel SGX or ARM TrustZone, offer an opportunity to substantially reduce the costs of Byzantine fault-tolerance by placing the program code and state within a secure enclave known as a Trusted Execution Environment (TEE). However, the protection offered by a TEE only applies during program execution. Once power is switched off, the non-volatile portion of the program state becomes vulnerable to rollback attacks wherein it is undetectably reverted to an older version. In this paper we consider the problem of implementing reliable read/write registers out of failure-prone replicas subject to state rollbacks. To this end, we introduce a new unified model that captures multiple failure types that can affect a TEE-based system and establish tight bounds on the fault-tolerance of register constructions in this model. We consider both the static case, where failure thresholds hold throughout the entire execution, and the dynamic case, where any number of replicas can roll back, provided these failures do not occur too often. Our dynamic register emulation algorithm, TEE-Rex, provides the first correct implementation of a distributed state recovery procedure that requires neither durable storage nor specialized hardware, such as trusted monotonic counters.
Keywords and phrases:
Trusted execution environments, fault tolerance, crash recoveryCopyright and License:
2012 ACM Subject Classification:
Theory of computation Distributed computing modelsFunding:
This work was partially supported by the projects BYZANTIUM and DECO funded by MCIN/AEI, and REDONDA funded by the CHIST-ERA network.Editor:
Dariusz R. KowalskiSeries and Publisher:
Leibniz International Proceedings in Informatics, Schloss Dagstuhl – Leibniz-Zentrum für Informatik
1 Introduction
Tolerating Byzantine failures plays a major role in designing modern reliable distributed systems, and in particular blockchains. Unfortunately, tolerating Byzantine failures is expensive in terms of message complexity [15], latency [2, 43], and the number of replicas required [39, 32, 16]. Recent advances in secure hardware technologies, such as Intel SGX or ARM TrustZone, offer an opportunity to substantially reduce these costs by placing the program’s code and a portion of its state in a secure enclave known as a Trusted Execution Environments (TEE). Applications can furthermore detect any runtime alterations to the state stored outside the enclave using integrity metadata stored inside it. These mechanisms together can then be used to convert Byzantine failures to simple crash or omission failures, which are less expensive to tolerate [41, 31, 7, 42].
However, a TEE is a not a panacea, because the protection it offers only applies during program execution. Once power is switched off, the non-volatile portion of the program state becomes vulnerable to tampering by an attacker. To address this, hardware manufacturers provide a sealing mechanism [12] that allows programs to encrypt a portion of their state using a device-specific key before storing it in non-volatile storage. When the machine restarts, the TEE can check whether the sealed state was indeed written by it in the past. However, it cannot determine whether this state is the most recent version. This limitation makes TEE-based systems vulnerable to rollback attacks where the sealed state is undetectably replaced with an older version [35, 40, 8, 42, 13].
In this paper we study theoretical foundations for building reliable distributed systems in the presence of rollbacks. We start by introducing crash-restart-rollback (), a new unified failure model that formalizes rollbacks alongside other types of failures that can occur in a TEE-based system. In this model, processes can use local non-volatile storage to persist their states and are assumed to behave correctly while they are executing. A -faulty process can permanently crash, crash and restart infinitely often, or crash and restart with the content of its non-volatile storage being undetectably reverted to an older version.
We then study the problem of implementing reliable read/write registers in an asynchronous message-passing system where the register’s state is stored at a collection of failure-prone replicas accessed by arbitrarily many crash-prone clients. We consider two sub-classes of the above failure model for replicas – static and dynamic. Static failure models assume the existence of a priori fixed thresholds restricting the number of replicas that can experience failures of various types in every execution; this is similar to the classical crash or Byzantine failure models. The dynamic failure models generalize the static ones by permitting any number of replicas to roll back in exchange for requiring that these failures do not occur too often.
Static failure models.
For the static failure models, we give a fine-grained characterization of failure resilience, time complexity, and non-volatile storage requirements of a register implementation in terms of several thresholds: on the total number of any failures; on the number of rollback failures; and on the number of correct replicas that can crash at least once, but eventually stay up. Our main result establishes the following: a wait-free atomic multi-writer/multi-reader (MWMR) register can be implemented in the static model if and only if . Our algorithm matching this bound has the latency of message delays for both writes and reads, which is the same as for crash fault-tolerant register implementations [33, 11] and is thus optimal. This stands in a sharp contrast with the Byzantine-resilient register constructions, where write and read latencies of and message delays are inherent even for implementing a single-writer/multi-reader (SWMR) wait-free atomic register [14].
Our lower bound implies that, when , rollback failures are no different from Byzantine failures in terms of failure resilience: the number of replicas required in this case is the same as that needed to implement Byzantine-resilient registers when at most out of faulty replicas can behave arbitrarily [18]. On the other hand, the two models are separated when : in this case, a register can be implemented in with replicas, which is strictly fewer than required by Byzantine fault-tolerant implementations.
We also establish a lower bound on non-volatile storage consumption: when , at least replicas must store their states persistently on their local non-volatile storage. Since this storage is not protected by TEEs, in practice applications must rely on expensive mechanisms to monitor its integrity at runtime, e.g., using TEE-protected integrity metadata, such as Merkle trees. Our results show that for some resilience levels these overheads are unavoidable.
Dynamic failure models.
For the dynamic failure models, we first show that no implementation of a single-writer/single-reader (SWSR) safe register can use fewer than replicas, where bounds the number of replicas that either crash permanently or never stop crashing and recovering, and bounds the number of replicas that crash at least once. While it is well-known that no register implementation can exist without a majority of replicas being eventually up (), our result further refines this lower bound for the case when .
For a matching upper bound, we propose an algorithm, called TEE-Rex, that implements an MWMR atomic register using replicas. The algorithm is always safe and is wait-free in executions where: replicas eventually stop crashing; and either replicas do not crash too often or replicas never crash at all. Unlike in the static model, these conditions place no restrictions on the number of replicas that can roll back. The algorithm is parameterized by , but is unaware of . It achieves the optimal time complexity of message delays for both writes and reads in failure-free executions.
The most interesting ingredient of TEE-Rex is a novel distributed recovery protocol that enables crashed replicas to rebuild their state and become fully operational upon restart. As we explain in §4.1, implementing recovery correctly is nontrivial: a naive approach of simply querying a read quorum and adopting the state of the most up-to-date replica is fundamentally unsafe [36]. In fact, as we discovered in this work, some of the prior recoverable register implementations suffer from nontrivial safety bugs [17, 13] (§5). Others do not guarantee liveness [35, 42], or require trusted hardware-based primitives such as persistent monotonic counters [25, 36]. In contrast, our TEE-Rex algorithm provides what we believe is the first self-contained and rollback-resilient construction of a read/write register under dynamic failure models.
2 System Model
We consider an asynchronous message-passing system consisting of a collection of failure-prone processes implementing a high-level object abstraction. The set of processes is partitioned into a set of replicas, and a set of possibly infinitely many clients. Similarly to the classical faulty shared memory model of [24, 3], the replicas are responsible for storing the object state, and the clients interact with the replicas to handle the requests supported by the implemented object type. To study tradeoffs between failure resilience and non-volatile storage consumption, we assume that replicas have access to non-volatile storage.
Formally, an object implementation is a composition of client and replica automata. An execution of an implementation is a sequence of states interleaved with atomic send and receive actions starting from an initial state. The action atomicity ensures that all non-volatile storage modifications performed as part of the received message handling are failure atomic [9], i.e., their effects are either persisted in their entirety or not at all.
Failure models.
A failure model is a predicate over executions. An execution is valid under a failure model (or simply -valid) if holds. For failure models and , iff .
Client failures.
Clients can experience permanent crash failures, but otherwise do not deviate from their prescribed protocols. A client is correct in an execution if it never crashes, and is faulty, otherwise.
Replica failures.
Our baseline failure model for replicas, to which we refer as crash-restart-rollback (), formalizes the types of failures that can occur in a TEE-based system (see §1). Under , a replica can experience a crash that interrupts its execution either permanently or temporarily. In the former case, the replica stops taking steps; in the latter case, it restarts, by executing a specified on restart block and then continues its execution from the state reached thereupon. For simplicity, we assume that when a replica is restarted, the restart and its immediately preceding crash occur simultaneously as a single atomic event. We refer to the restarted replicas that have completed their on restart blocks as active. Upon restart, a replica with non-volatile storage may additionally experience a rollback failure, which causes the content of its non-volatile storage to revert to a version older than it had before the latest crash.
A replica is up in an interval if it does not crash without restart before , and does not crash (either with or without restart) at all times in ; a replica is up after , if it is up in ; a replica is eventually up if there exists such that is up after . Replicas in fall into four disjoint classes:
-
a replica is perfect if it never crashes;
-
a replica is benign if it crashes and restarts at least once, but is eventually up, and furthermore, it never rolls back;
-
a replica is crash-faulty if it crashes without restarting or crashes and restarts infinitely often, and furthermore, it never rolls back; and
-
a replica is rollback-faulty if it rolls back at least once.
Perfect and benign replicas are together called correct; crash-faulty and rollback-faulty replicas are together called faulty.
Note that generalizes the classical crash-recovery failure model [22] by extending its set of faulty behaviors with rollback failures. It is also strictly weaker than the Byzantine failure model since faulty replicas are required to follow their prescribed protocols. The relationship of our model to other prior failure models is further discussed in §5.
Channel reliability assumptions.
We assume that every pair of processes and are connected via point-to-point authenticated links such that if both and are up after some , then every message sent by to after is eventually delivered by . Note that this assumption is strictly weaker than the standard notion of a reliable channel. Specifically, it does not require reliability for messages sent by a correct process to another correct process before both processes stop crashing.
Specifications.
We use standard safety and liveness notions to specify correctness conditions for register implementations: atomicity [28] (linearizability [21]), safeness [28], wait-freedom [19], and obstruction-freedom [20]. We consider both single-writer/single-reader (SWSR) and multiple-writer/multiple-reader (MWMR) register implementations.
Fault-tolerant implementations.
An object implementation is safe under a failure model (or simply -safe) if it satisfies safety in all -valid executions. We define -liveness similarly. An implementation is -tolerant if it is both safe and live under .
We study the implementability of registers under failure models where, in every execution, arbitrarily many clients can crash and replicas are subject to a restricted variant of the baseline model above. Specifically, in §3 we consider static failure models for replicas where the number of faulty, rollback-faulty, and benign replicas in every execution is bounded by , , and , respectively. Note that . In §4 we consider more flexible dynamic failure models.
3 Register Implementations in Static Failure Models
In this section we give a full characterization of the costs of implementing a register under static failure models in terms of its failure resilience, time complexity, and non-volatile storage requirements. For resilience, the following theorem establishes a tight bound on the total number of replicas as a function of thresholds on the number of replicas of different types.
Theorem 1.
Assume that all replicas have access to non-volatile storage. Then for all , , , there exists a -tolerant implementation of a wait-free atomic MWMR register if and only if .
When , the bound in the theorem specializes to , the same as for crash fault-tolerant register implementations. When , the bound specializes to . This matches the known generalization [18] of the Byzantine implementability bound for registers [34, 14] to the case when at most out of faulty replicas can behave arbitrarily and the rest can crash but not deviate from the prescribed protocol. On the other hand, if , then the theorem shows that we can implement a -tolerant wait-free atomic register with replicas – strictly fewer than in the Byzantine case. We next refine Theorem 1 to additionally give a tight bound on the number of replicas that must be equipped with non-volatile storage.
Theorem 2.
Let be the number of replicas with non-volatile storage. Then for all , , , there exists a -tolerant implementation of a wait-free atomic MWMR register if and only if
Note that the constraint on in Theorem 2 implies that in Theorem 1; we illustrate the constraint in Figure 1. The above theorem shows that, when , atomic registers can be implemented without any stable storage at all. In practice, avoiding non-volatile storage offers substantial performance gains when the register state fits within the TEE-protected RAM, as it avoids the overheads of monitoring storage integrity at runtime.
Finally, the result in Theorem 2 can be strengthened along several dimensions:
-
The time complexity of the upper bound we present in §3.1 is always message delays for both writes and reads. This matches the time complexity of crash fault-tolerant implementations, which is optimal. This property stands in a sharp contrast with Byzantine-resilient register constructions, where write and read latencies of and message delays, respectively, are inherent even for single-writer/multi-reader (SWMR) wait-free atomic registers [14].
-
The lower bound in Theorem 2 is established for obstruction-free safe implementations of SWSR registers.
-
When either of or is unknown, a tight bound is obtained from Theorem 2 by letting or .
-
If the number of replicas is less than the resilience bound (), the upper bound does not sacrifice safety, but only liveness (the top-left box in Figure 1). In §4 we leverage this property to develop a register construction under dynamic failure models that do not require all failure thresholds to hold for the entire execution.
Our findings collectively demonstrate that, while existing Byzantine fault-tolerant register constructions [34, 1, 18, 14] can be used for implementing -tolerant atomic registers, they are not well-suited for this purpose due to their sub-optimal failure resilience and latency. We next present an algorithm that gives a witness for our upper bound; we defer the proofs of the other results to [26, §A].
3.1 Upper Bound
An algorithm that shows our upper bound for is presented in Figure 2. It is based on the MWMR variant of ABD [5, 33, 11], where read and write quorums are sets of replicas of size and , respectively. The algorithm uses the truth value of the predicate to determine the values of and (line 2), the restart handler logic (lines 6–7), and whether non-volatile storage should be used to store the replica’s state (line 5). In the following we refer to the variant of executed if holds as algorithm , and as algorithm otherwise. The former corresponds to the bottom-left box in Figure 1, while the latter corresponds to the remaining portions of the figure.
Replica states.
Each replica stores a copy of the register state in a variable (line 3), which is a tuple consisting of the register value and timestamp . Timestamps are pairs of a counter and the client identifier, ordered lexicographically: iff . Each replica also maintains a Boolean flag : if this flag is true, the replica is not allowed to respond to read requests.
Quorum-access functions.
The clients use auxiliary functions (lines 14–19) and (lines 29–34) to respectively query and update various components of replica states at a quorum. For , the argument specifies the state elements to query: e.g., corresponds to . For , specifies which state elements to modify and their new values: e.g., to set to .
The (respectively, ) function starts by generating a globally unique request identifier (lines 15 and 30); in practice this can be implemented using cryptographic nonces. The function then broadcasts a (respectively, ) message to all replicas, and awaits (respectively, ) messages from (respectively, ) replicas. Since channels may fail to deliver messages sent before their respective destinations stop crashing (§2), to ensure liveness, both and continue retransmitting requests until receiving the desired quorums of responses. The function then returns the set of payloads received in messages from a read quorum of replicas.
Whenever a replica with receives a message (line 35), it responds with a message, carrying and the current value of the state variable associated with . Whenever a replica receives a message (line 20), it applies the update encoded in to its state and acknowledges this fact with a message. In particular, if and , then is set to ; otherwise, the state is left unchanged.
Read and write protocols.
To write a value , a process first calls to retrieve a set of timestamps from a read quorum (line 9). It then selects the highest timestamp counter and calls to store with timestamp at a write quorum. To read a value, a process first calls to retrieve a set of timestamp-value pairs from a read quorum (line 25). It then selects the pair with the highest timestamp, calls to store it at a write quorum, and returns .
Algorithm .
Algorithm handles the case when the predicate holds, i.e., (the bottom-left box in Figure 1 and the first disjunct in Theorem 2). The algorithm uses read and write quorums of size (line 2) and keeps the state of replicas in non-volatile storage (line 5), which is the minimum required by the lower bound of Theorem 2. Its restart handler returns immediately (lines 6–7), so the flag is always false at all replicas, which can thus respond to messages.
Since in every execution at least replicas are eventually up, both write and read quorums are always available, and, as a result, algorithm is wait-free. We defer the full proof of its linearizability to [26, §A] and only prove the following key property the proof relies on:
Property 3 (Real-Time Order).
Let be a completed call to , and be a completed call to or . If is invoked after the completion of , then it returns a set containing a value for .
Proof of Property 3 for ..
Since returns, it has executed the request at replicas. Since returns, it has retrieved the value of from a replicas. The intersection of the two quorums has a cardinality . Furthermore, the number of replicas in this intersection that store their states on non-volatile storage is . Since at most replicas experience rollback failures, there exists at least one replica in the intersection of the quorums used by and that stores its state on non-volatile storage and never rolls back. Our protocol only allows replicas to increase the value of during normal execution (line 22), so replica must have after it responds to . Since starts after completes, must respond to after completes. Thus, must have when it responds to , so receives a response containing .
Algorithm .
Algorithm handles the case when the predicate does not hold (the right and top-left boxes in Figure 1 and the second disjunct in Theorem 2). The algorithm does not use non-volatile storage, and has write quorums of size and read quorums of size . Additionally, only replicas that have not crashed before being queried can participate in read quorums. To ensure this, each replica sets its flag to true before returning from the recovery procedure (line 7). Recall that a replica contacted by checks this flag and responds only if it is false (line 36).
When , in every execution at least replicas are eventually up, so some write quorum is eventually available. Also, at least replicas never crash, so some read quorum is always available. Hence, in this case is wait-free. Furthermore, is always safe, even when . Similarly to , the safety of this follows from the Real-Time Order Property, proved below; the rest of the proof is given in [26, §A].
Proof of Property 3 for ..
The intersection of any read and write quorums has a cardinality . Hence there exists a replica that both executes and responds to . Since is a member of a read quorum, it could not have crashed. Since the value of is non-decreasing in the absence of crashes (line 22), must have after it responds to . Since starts after completes, must respond to after completes. Thus, must have when it responds to , so receives a response containing .
4 Register Implementations in Dynamic Failure Models
In this section we study the implementability of registers under failure models where any number of replicas can experience failures throughout an execution. By Theorem 1, no register implementation can be simultaneously safe and live in all executions if . To circumvent this impossibility, we introduce a family of dynamic failure models , defined as follows.
We assume a constant such that, in any execution, every message sent by a process to a process at time is guaranteed to be received by by , provided and are up in . Note that this assumption does not make the model stronger than asynchronous: since we do not assume a lower bound on message delays or processing times, processes do not have a means to measure time passage and thus take advantage of the existence of [6].
Definition 4.
Given , , and , an execution is -valid if there exists such that , all replicas in are eventually up in and either:
-
1.
and for any two crash events occurring at times and , we have ; or
-
2.
no replica crashes in some set such that .
Intuitively, the parameter captures the upper bound on the number of replicas that are not eventually up in , and must be known to any register implementation. Conditions 1 and 2 further restrict failure scenarios: either failures must be separated by at least message delays, or at most replicas can crash in . The former is similar to the churn-limiting assumptions used to model process participation in dynamic and reconfigurable systems [6]. The parameter is implementation-specific and captures the minimum time required for a replica to recover its state before the next crash occurs.
We show that every static failure model from §3 is a special case of a dynamic model . Thus, any -tolerant register implementation is also -tolerant.
Proposition 5.
.
Proof.
Consider any -valid execution . Then in this execution up to replicas are faulty and up to replicas are benign. Let be the set of correct replicas, and be the set of perfect replicas. Then and . Furthermore, the replicas in are eventually up and those in never crash, so the condition in case 2 of Definition 4 is satisfied. Thus, is -valid, as needed.
We prove the following upper bound in the dynamic failure model:
Theorem 6.
For all , , if and , then there exists an implementation of an atomic wait-free MWMR register that is -live and always safe.
For the lower bound, we prove a stronger result that holds for -tolerant implementations, and not just those that are -live and always safe:
Theorem 7.
For all , , , if there exists a -tolerant implementation of a safe obstruction-free SWSR register, then .
The two theorems imply a tight resilience bound:
Theorem 8.
For all , , -live implementation of an atomic wait-free MWMR register exists for some if and only if .
The lower bound in Theorem 7 reveals an inherent trade-off: while by Proposition 5 any -tolerant register implementation is also -tolerant, any such implementation requires , thus sacrificing the optimal resilience under (cf. Theorem 2).
In the rest of this section we present an algorithm that validates Theorem 6, which we call TEE-Rex; we defer the proofs of the other results to [26, §B]. The algorithm’s latency in crash-free executions is message delays for both reads and writes, which is optimal. Since under , any number of replicas can suffer rollbacks in some executions, the algorithm does not rely on non-volatile storage. It also does not require the knowledge of .
4.1 Crash-Consistency Basics
The pseudocode of TEE-Rex appears in Figure 3. It reuses the and procedures of algorithm (Figure 2, lines 8–28). The algorithm relies on read quorums of size , and so-called crash-consistent write quorums of size , introduced in the following. Given the mapping between static and dynamic models established by Proposition 5, these quorum sizes mirror those in : for read quorums and for write quorums.
Since replicas lose their memory contents during a restart, TEE-Rex follows the approach of : a replica relies on its flag to determine whether its current state can be used to respond to requests. However, in contrast to , simply setting upon restart so that any replica that has not crashed permanently is active will not be live. To see why, fix arbitrary and , and consider an execution where all replicas are eventually up, every replica crashes and restarts at least once, and every two consecutive crashes are separated by . Clearly, is -valid. However, will not be live in as it eventually runs out of non-stale replicas to form read quorums. To deal with such scenarios, a key ingredient of TEE-Rex is a novel recovery protocol executed by a replica upon restart (lines 4–18). This protocol reconstructs the state of a restarted replica, and thus enables it to clear its flag.
The protocol achieves this by synchronizing with other replicas. However, doing this naively by simply querying a read quorum and adopting the state of the most up-to-date replica would lead to a safety violation. For example, consider the algorithm instantiated with and . Suppose we modify the on restart procedure at line 6 as follows. After setting , a restarting replica would invoke to retrieve the register states from a read quorum of replicas, assign the one with the highest timestamp to its own copy of , set , and then return. The following scenario demonstrates that this modification results in a linearizability violation.
Example 9.
Let , and , so that . Consider the execution of the modified algorithm in Figure 4, which is valid under . First, a client invokes (line 8 in Figure 2), which calls to query timestamps at a read quorum and then to store the new register state at a write quorum. In the latter call, sends to all replicas and waits until it gathers responses from a write quorum. Replica gets first, sets , and responds with a . It then crashes, loses its state, and reconstructs the state by contacting a read quorum of size . Since has not yet reached either replica, sets back to its initial value . Replica then receives , sets , and replies to with a . Process now gathers responses from processes, which constitutes a write quorum. Thus, and at terminate. Later, a client calls , which obtains responses from a read quorum . Since neither replica is aware of ’s write, the returns , violating linearizability.
To address the above problem, the set of responses collected by from a write quorum of replicas must be crash-consistent [36]. Let be a partial order on the set of replica events such that if there exists a replica such that precedes at .
Definition 10.
A set of events is crash-consistent if for for each event occurring at replica , if after this event crashes, restarts, and invokes , then the set of events triggered by the read satisfies .
For example, consider Figure 4. Let be the set of events triggered by the call to at (orange dots), and let be the set of events triggered by the call to during the recovery at (blue dots). Let be the event at sending , the event at sending , and the event at sending . Since , the set is not crash-consistent.
4.2 Crash-Consistency with Incarnation Numbers and Crash Vectors
To implement crash-consistency, similarly to [36, 25], each replica in TEE-Rex maintains two pieces of metadata – an incarnation number and a crash vector. The former is an integer, initialized to , and the latter tracks the highest known incarnation numbers of all other replicas. The crash vector of replica is stored in , and for convenience, ’s own incarnation number is stored in . This incarnation number is updated by the recovery procedure at line 9. To explain the basic principles underlying crash-consistency checks in TEE-Rex, we first assume that incarnation numbers written by each replica into monotonically increase:
Property 11 (Incarnation Number Monotonicity).
This property can be easily ensured assuming that each replica has a built-in monotonic counter that is never rolled back, as done in previous work [36]. In §4.3 we show how to ensure this property without such an assumption. The code needed for this is shown in blue and should be ignored when reading this section.
implementation.
In TEE-Rex, a process receiving a message piggybacks its incarnation number and a copy of its crash vector on the response (line 55). These are used by the implementation of to check whether the set of responses it receives is crash-consistent.
This check is integrated within the loop executed by (line 21). At each iteration of this loop, a message is (periodically) broadcast to all replicas excluding those in the set , which accumulates the replicas whose responses have been already validated as crash-consistent. Once acknowledgments are received from a set of replicas such that is a write quorum (line 25), the function checks their crash-consistency. The replicas that sent responses that are not crash-consistent are then purged from , and the set of remaining replicas is reassigned to (line 32).
To identify which replicas in the set sent crash-consistent responses, the crash vectors received in the messages are combined into a vector by taking their entry-wise maximum (line 31). Then the incarnation numbers in the messages are compared against . If , then restarted while our invocation of was gathering responses from other replicas, which indicates that ’s prior response is no longer crash-consistent. As a special case, a replica always treats its own responses as crash consistent (the disjunct at line 32). If after excluding all replicas whose responses are not crash-consistent, contains a write quorum, then returns (line 33); otherwise, it proceeds to the next iteration of the loop.
Recovery implementation.
The recovery procedure executed by replica starts by setting its flag to true (line 5), thus preventing from replying to and messages (lines 41 and 52). The replica then selects a new incarnation number (line 9), which is required to satisfy Property 11, and stores it at a write quorum (line 10; we explain the implementation of later). The replica further invokes to retrieve the states from a read quorum, including both register states and crash vectors (line 11). Finally, the replica reconstructs its state by merging the crash vectors it received and picking the register value with the highest timestamp, and clears the flag (lines 12–18).
The messages sent by the to retrieve the states (line 11) carry the new incarnation number, and a replica receiving such a message incorporates the new incarnation number into its crash vector (line 43). Thus, the new incarnation number is written to a read quorum of replicas, which intersects any write quorum that may be used by a concurrent invocation of . This ensures that this invocation only accepts a crash-consistent set of responses (Definition 10) and rules out the execution in Example 9, as we now explain.
Example 12.
Assume that in Figure 4, ’s incarnation number before the crash is and its new incarnation number after the recovery is . Then will send in its response to (line 55), and in its request to (line 11). When responds to , it will record in its crash-vector entry for (line 43). Then will piggyback this crash vector on its response to ’s request (line 55). This will cause to discard ’s response, since it carries a smaller incarnation number () than ’s entry in the crash vector received from () (line 32).
Note that in the above example, replica might crash and recover after responding to but before responding to . When recovering, will reconstruct its crash vector to a value no lower than what it was before the crash (line 14), thus giving a correct response to . This is ensured by the fact that each recovering replica, such as , writes its incarnation number crash-consistently to a write quorum (line 10), which intersects with a read quorum that a replica such as uses to reconstruct its state during recovery (line 11).
Thus, a replica writes its incarnation number in the restart handler in two places – first crash-consistently to some write quorum (line 10), and then to the particular read quorum used to reconstruct its state (line 11). These serve complementary purposes: the former ensures that other replicas can reconstruct the incarnation number of when they restart; the latter ensures that other replicas can detect when the crash-consistency of their writes can be compromised by a concurrent restart of .
implementation.
We now describe the implementation for incarnation numbers (line 10), which happens to be subtle. We could implement in the exact same way as , by using crash vectors piggybacked on messages to check for crash-consistency of a write; this was the approach taken in [36]. Unfortunately, the resulting algorithm would not be live cases where liveness must be ensured to match the lower bound of Theorem 7.
Example 13.
Let , and , so that . Consider an execution of the above version of the algorithm where crashes permanently, and crash and restart once at the beginning of the execution, and the remaining replicas never crash. This execution is valid under (case 2 in Definition 4). But and would not be able to complete the recovery in it, because their calls to would wait for responses (line 25). However, only active replicas would respond to messages (line 52), and there are only such replicas.
To rule out such executions, in our algorithm a replica accepts a message for a write to even if its flag is set (line 56). In this case the replica cannot piggyback its crash vector on the response (as in line 55): the replica may have lost the crash vector upon a restart and has not yet reconstructed it (line 14). Hence, the replica puts a dummy value in place of a crash vector (line 63). The function then checks crash-consistency for writes of incarnation numbers differently from register writes: after it receives enough responses, it calls to read the crash vectors explicitly (line 29). Replicas only reply to requests sent by this function when their flag is cleared (line 41), and thus they have valid crash vectors in their state.
In the absence of further crashes in Example 13, and can complete the recovery as follows: they first collect s from replicas that are up () and then read crash vectors from replicas that are active (). We can show that TEE-Rex is correct assuming Incarnation Number Monotonicity.
Theorem 14.
4.3 Implementing Incarnation Numbers without Non-Volatile Storage
Since in the presence of rollbacks the replicas cannot rely on non-volatile storage to implement monotonically growing incarnation numbers, TEE-Rex relies on a distributed mechanism for this purpose, shown in blue in Figure 3. This mechanism extends the one used to store incarnation numbers in (line 10) described in the previous section. Note that a replica cannot restore its previous incarnation number by just reading it using : if the replica crashed before completing the at line 10, the is not guaranteed to restore its latest pre-crash incarnation number, thus violating Property 11.
To address this problem, we adopt a two-phase approach. In addition to , the state of each replica includes a vector . Before assigning its new incarnation number to (line 9), a replica first writes it to at a crash-consistent write quorum of replicas (line 8). Upon a restart, a replica restores its previous incarnation number as a maximum of responses returned by and computes the new incarnation number by incrementing the result (lines 6–7). Then the intersection between the quorums used by (line 8) and (line 6) helps ensure Property 11.
There is, however, a subtlety. Recall that in our algorithm a replica has to reply to a message for a write to even while it restarting (line 56), and the same should hold for writes to : this is necessary to avoid deadlocks like the one in Example 13. When incarnation numbers are computed as described above, a restarting replica thus needs to reply to a even before it computed its incarnation number at line 9. This poses a dilemma: which incarnation number should the replica include into its message (line 55) to allow other replicas to validate the crash-consistency of their writes to and ? To address this challenge, when a replica executing or sends a message to another replica , it piggybacks the maximum incarnation number of known to it on this message (line 24). A replica receiving a request for or then adopts this incarnation number (line 58) and uses it in its reply.
In [26, §B] we prove that, even though the above mechanism may require processes to temporarily adopt stale incarnation numbers, it does ensure Incarnation Number Monotonicity (Property 11). Thus, given Theorem 14, the overall TEE-Rex algorithm is correct. Here we illustrate the operation of the incarnation number implementation on an example.
Example 15.
Let , and , so that . Consider an execution where crashes permanently, and are eventually up, and the remaining replicas never crash. This execution is valid under (case 2 in Definition 4). At the beginning of the execution, and crash, restart, and become active again, so that and have . Suppose now that and crash and restart again.
To recover, each of and determines its previous incarnation number (line 6) and computes a new incarnation number , which it tries to store crash-consistently in at a write quorum (line 8). Since does not have any information about the incarnation number of , its first message to carries as the incarnation number (line 24), which includes into its reply. The that invokes after this (line 29) fetches for from a read quorum , so the from is discarded (line 32). The same happens at with the it receives from .
Each of and then includes into the next message it sends to the other replica (line 24), which the latter adopts as its incarnation number and includes into its reply (line 58). Once receives the from , it calls once more (line 29) and validates the as crash-consistent, finishing the execution of . Replica then sets to its new incarnation number (line 9) and completes the rest of the recovery, including .
Assume that now receives the from and calls to validate it (line 29). This read yields for , so discards the and sends another to with incarnation number (line 24). Now responds with a carrying its incarnation number , which it also stores locally. When receives this , it performs another , validates the as crash-consistent and completes . It then sets to its new incarnation number (line 9) and completes the rest of the recovery. In the end, both and successfully recover with a higher incarnation number, satisfying Property 11.
5 Related Work
The classical crash-recovery failure model assumed that every process is equipped with durable storage that cannot be rolled back [22]. This assumption was lifted by Aguilera et al. [4], who analyzed consensus solvability as a function of the number of processes with and without durable storage. The same model was also used by Guerraoui et al. [17] to derive tight bounds for a reliable register construction. In these models the processes are assumed to know whether they are equipped with durable storage, which they can trust to be incorruptible. In contrast, in our model a recovering process cannot trust its non-volatile storage to be up-to-date, leading to different resilience bounds. Furthermore, in the register implementation of Guerraoui et al. a restarted replica is considered up-to-date once it accepts a single request. As we show in [26, §C], this leads to a safety violation.
Dinis et al. [13] proposed a rollback-recovery (RR) model to capture rollback attacks in TEE-based systems. This model is weaker than as it disallows permanent process crashes. Furthermore, the register implementation of Dinis et al. uses ordinary quorums for writes, without crash-consistency checks. As we show in [26, §D], this leads to a safety violation similar to the one in Example 9. The implementation also does not guarantee wait-freedom for reads. In contrast, our framework captures the full spectrum of failures in TEE-based systems and enables developing correct solutions for these environments.
The crash-recovery failure model where the processes do not have access to incorruptible durable storage was assumed by Chandra et al. [10], Kończak et al. [27], and Liskov et al. [30] in the context of their efforts to develop practical variants of Paxos [29] and viewstamped replication [38]. The proposed solutions, however, did not use crash-consistent quorums for storing their state, and as a result, were shown in [36] to violate safety.
Jehl et al. [25] proposed a versioning scheme similar to crash vectors that allows quickly replacing a failed replica in Paxos. This technique was subsequently generalized to the notion of crash-consistent quorums by Michael et al. [36], who also demonstrated how it can be used to implement a recoverable atomic register. However, the safety of these versioning schemes critically depends on the replicas’ ability to track their incarnations across restarts. In turn, supporting this capability in TEE-based systems requires hardware-based persistent counters. These counters are implemented using flash memory, resulting in poor write performance and quick wear-out [35]. Furthermore, they are not universally supported by TEE manufacturers, and have been recently deprecated by Intel SGX [23].
Although the technical report version [37] of the work by Michael et al. suggests that such incarnation numbers can be supported by means of a distributed mechanism, it does not provide a full implementation. In addition, while the recoverable register implementation of [36, 37] is always safe, its liveness under the static failure models requires , which is strictly worse than our bound of .
Persistent monotonic hardware counters have been demonstrated in [40] to be a powerful mechanism to guard against rollback attacks. In TEE-based systems they can be used together with sealing (§1) to validate state freshness upon restart [12]. Unfortunately, these solutions inherit the drawbacks of persistent counters we explained above.
ROTE [35] guards against rollbacks using a rollback-resistant distributed counter. Its implementation relies on a two-round protocol where a new value of the counter is first written to a write quorum, and then read back from the same write quorum to validate that it was stored reliably. Although this solution is safe, it does not ensure liveness, as the same write quorum cannot be guaranteed to be available two times in a row under failures and asynchrony. Engraft [42] uses a similar protocol, so is subject to the same liveness issue.
References
- [1] Ittai Abraham, Gregory V. Chockler, Idit Keidar, and Dahlia Malkhi. Byzantine disk Paxos: optimal resilience with Byzantine shared memory. Distributed Comput., 18(5), 2006. doi:10.1007/S00446-005-0151-6.
- [2] Ittai Abraham, Kartik Nayak, Ling Ren, and Zhuolun Xiang. Good-case latency of Byzantine broadcast: A complete categorization. In Symposium on Principles of Distributed Computing (PODC), 2021.
- [3] Yehuda Afek, David S. Greenberg, Michael Merritt, and Gadi Taubenfeld. Computing with faulty shared objects. J. ACM, 42(6), 1995. doi:10.1145/227683.227688.
- [4] Marcos K Aguilera, Wei Chen, and Sam Toueg. Failure detection and consensus in the crash-recovery model. Distributed Comput., 13(2), 2000. doi:10.1007/S004460050070.
- [5] Hagit Attiya, Amotz Bar-Noy, and Danny Dolev. Sharing memory robustly in message-passing systems. In Symposium Principles of Distributed Computing (PODC), 1990.
- [6] Hagit Attiya, Hyun Chul Chung, Faith Ellen, Saptaparni Kumar, and Jennifer L. Welch. Simulating a shared register in an asynchronous system that never stops changing. In Symposium on Distributed Computing (DISC), 2015.
- [7] Johannes Behl, Tobias Distler, and Rüdiger Kapitza. Hybrids on steroids: SGX-based high performance BFT. In European Conference on Computer Systems (EuroSys), 2017.
- [8] Marcus Brandenburger, Christian Cachin, Matthias Lorenz, and Rüdiger Kapitza. Rollback and forking detection for trusted execution environments using lightweight collective memory. In Conference on Dependable Systems and Networks (DSN), 2017.
- [9] Dhruva R. Chakrabarti, Hans-Juergen Boehm, and Kumud Bhandari. Atlas: leveraging locks for non-volatile memory consistency. In Conference on Object Oriented Programming, Systems, Languages, and Applications (OOPSLA), 2014.
- [10] Tushar D Chandra, Robert Griesemer, and Joshua Redstone. Paxos made live: An engineering perspective. In Symposium on Principles of Distributed Computing (PODC), 2007.
- [11] Gregory Chockler, Nancy Lynch, Sayan Mitra, and Joshua Tauber. Proving atomicity: An assertional approach. In Symposium on Distributed Computing (DISC), 2005.
- [12] Victor Costan and Srinivas Devadas. Intel SGX explained. IACR Cryptology ePrint Archive, 2016:86, 2016. URL: http://eprint.iacr.org/2016/086.
- [13] Baltasar Dinis, Peter Druschel, and Rodrigo Rodrigues. RR: A fault model for efficient TEE replication. In Network and Distributed System Security Symposium (NDSS), 2023.
- [14] Dan Dobre, Rachid Guerraoui, Matthias Majuntke, Neeraj Suri, and Marko Vukolic. The complexity of robust atomic storage. In Symposium on Principles of Distributed Computing (PODC), 2011.
- [15] Danny Dolev and Rüdiger Reischuk. Bounds on information exchange for Byzantine agreement. J. ACM, 32(1), 1985. doi:10.1145/2455.214112.
- [16] Cynthia Dwork, Nancy Lynch, and Larry Stockmeyer. Consensus in the presence of partial synchrony. J. ACM, 35(2), 1988. doi:10.1145/42282.42283.
- [17] Rachid Guerraoui, Ron R. Levy, Bastian Pochon, and Jim Pugh. The collective memory of amnesic processes. ACM Trans. Algorithms, 4(1), 2008. doi:10.1145/1328911.1328923.
- [18] Rachid Guerraoui and Marko Vukolic. How fast can a very robust read be? In Symposium on Principles of Distributed Computing (PODC), 2006.
- [19] Maurice Herlihy. Wait-free synchronization. ACM Trans. Program. Lang. Syst., 13(1), 1991. doi:10.1145/114005.102808.
- [20] Maurice Herlihy, Victor Luchangco, and Mark Moir. Obstruction-free synchronization: Double-ended queues as an example. In International Conference on Distributed Computing Systems (ICDCS), 2003.
- [21] Maurice Herlihy and Jeannette M. Wing. Linearizability: A correctness condition for concurrent objects. ACM Trans. Program. Lang. Syst., 12(3), 1990. doi:10.1145/78969.78972.
- [22] Michel Hurfin, Achour Mostéfaoui, and Michel Raynal. Consensus in asynchronous systems where processes can crash and recover. In Symposium on Reliable Distributed Systems (SRDS), 1998.
- [23] Intel Corporation. End of life for Intel SGX monotonic counter service. https://www.intel.com/content/www/us/en/support/articles/000057968/software/intel-security-products.html, 2023. Accessed: 2025-02-09.
- [24] Prasad Jayanti, Tushar Deepak Chandra, and Sam Toueg. Fault-tolerant wait-free shared objects. J. ACM, 45(3), 1998. doi:10.1145/278298.278305.
- [25] Leander Jehl, Tormod Erevik Lea, and Hein Meling. Replacement: Decentralized failure handling for replicated state machines. In Symposium on Reliable Distributed Systems (SRDS), 2015.
- [26] Sadegh Keshavarzi, Gregory Chockler, and Alexey Gotsman. TEE is not a healer: Rollback-resistant reliable storage (extended version). arXiv, 2505.18648, 2025. URL: https://arxiv.org/abs/2505.18648.
- [27] Jan Kończak, Nuno Santos, Tomasz Żurkowski, Paweł T. Wojciechowski, and André Schiper. JPaxos: State machine replication based on the Paxos protocol. Technical Report EPFL-REPORT-167765, EPFL, 2011.
- [28] Leslie Lamport. On interprocess communication. Distributed Comput., 1(2), 1986.
- [29] Leslie Lamport. The part-time parliament. ACM Trans. Comput. Syst., 16(2), 1998. doi:10.1145/279227.279229.
- [30] Barbara Liskov and James Cowling. Viewstamped replication revisited. Technical Report MIT-CSAIL-TR-2012-021, MIT CSAIL, 2012.
- [31] Jian Liu, Wenting Li, Ghassan O. Karame, and N. Asokan. Scalable Byzantine consensus via hardware-assisted secret sharing. IEEE Trans. Computers, 68(1), 2019. doi:10.1109/TC.2018.2860009.
- [32] Nancy A Lynch and Michael Merritt. Easy impossibility proofs for distributed consensus problems. Distributed Comput., 1(1), 1986. doi:10.1007/BF01843568.
- [33] Nancy A. Lynch and Alexander A. Shvartsman. RAMBO: A reconfigurable atomic memory service for dynamic networks. In Symposium on Distributed Computing (DISC), 2002.
- [34] Jean-Philippe Martin, Lorenzo Alvisi, and Michael Dahlin. Minimal Byzantine storage. In Symposium on Distributed Computing (DISC), 2002.
- [35] Sinisa Matetic, Mansoor Ahmed, Kari Kostiainen, Aritra Dhar, David Sommer, Arthur Gervais, Ari Juels, and Srdjan Capkun. ROTE: Rollback protection for trusted execution. In USENIX Security Symposium (USENIX Security), 2017.
- [36] Ellis Michael, Dan R. K. Ports, Naveen Kr. Sharma, and Adriana Szekeres. Recovering Shared Objects Without Stable Storage. In Symposium on Distributed Computing (DISC), 2017.
- [37] Ellis Michael, Dan R. K. Ports, Naveen Kr. Sharma, and Adriana Szekeres. Recovering shared objects without stable storage (extended version). Technical Report UW-CSE-TR-17-10-01, University of Washington, 2017. URL: https://www.microsoft.com/en-us/research/publication/recovering-shared-objects-without-stable-storage-extended-version/.
- [38] Brian M. Oki and Barbara H. Liskov. Viewstamped replication: A new primary copy method to support highly-available distributed systems. Symposium on Principles of Distributed Computing (PODC), 1988.
- [39] Marshall Pease, Robert Shostak, and Leslie Lamport. Reaching agreement in the presence of faults. J. ACM, 27(2), 1980. doi:10.1145/322186.322188.
- [40] Raoul Strackx and Frank Piessens. Ariadne: A minimal approach to state continuity. In USENIX Security Symposium (USENIX Security), 2016.
- [41] Giuliana Santos Veronese, Miguel Correia, Alysson Neves Bessani, Lau Cheuk Lung, and Paulo Veríssimo. Efficient Byzantine fault-tolerance. IEEE Trans. Computers, 62(1), 2013. doi:10.1109/TC.2011.221.
- [42] Weili Wang, Sen Deng, Jianyu Niu, Michael K. Reiter, and Yinqian Zhang. ENGRAFT: Enclave-guarded Raft on Byzantine faulty nodes. In Conference on Computer and Communications Security (CCS), 2022.
- [43] Qianyu Yu, Giuliano Losa, and Xuechao Wang. TetraBFT: Reducing latency of unauthenticated, responsive BFT consensus. In Symposium on Principles of Distributed Computing (PODC), 2024.
