
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>tminusplus</title><description>Thoughts from tminusplus</description><link>https://tminusplus.dev/</link><language>en-us</language><item><title>Fault Tolerant Systems with Temporal</title><link>https://tminusplus.dev/posts/fault-tolerant-systems-with-temporal/</link><guid isPermaLink="true">https://tminusplus.dev/posts/fault-tolerant-systems-with-temporal/</guid><description>Building fault tolerant systems with Temporal</description><pubDate>Thu, 11 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This article will explain why I like to build distributed fault tolerant systems with Temporal. We will start by discussing choreography vs orchestration, and then get into Temporal.&lt;/p&gt;
&lt;h1&gt;Choreography&lt;/h1&gt;
&lt;p&gt;In a choreographed systems, perhaps consisting of micro-services and event queues, processes are spread out across services which communicate with events:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Payment Service
async def process_payment(order_id: str, amount: float):
    try:
        result = await payment_provider.charge(amount)
        await message_queue.publish(&quot;payment_completed&quot;, {
            &quot;order_id&quot;: order_id,
            &quot;transaction_id&quot;: result.id
        })
    except PaymentError as e:
        await message_queue.publish(&quot;payment_failed&quot;, {
            &quot;order_id&quot;: order_id,
            &quot;error&quot;: str(e)
        })

# Inventory Service
async def handle_payment_completed(event):
    order_id = event[&quot;order_id&quot;]
    await database.update_inventory(order_id)
    await message_queue.publish(&quot;inventory_updated&quot;, {
        &quot;order_id&quot;: order_id
    })

# Notification Service
async def handle_inventory_updated(event):
    order_id = event[&quot;order_id&quot;]
    await email_service.send_confirmation(order_id)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach promises compelling benefits. Services can be developed and deployed independently. Teams work autonomously. Each service owns its logic and data. New services can be added without changing existing ones. The system scales naturally as each component handles its own load.&lt;/p&gt;
&lt;p&gt;Choreography works well when processes are simple, state is minimal, timing is not critical, and failures are tolerable. Otherwise, reality gets messy.&lt;/p&gt;
&lt;h2&gt;When Choreography Breaks&lt;/h2&gt;
&lt;p&gt;Questions like &quot;what&apos;s the status of order X?&quot; become difficult to answer. The payment service knows about the charge but not the inventory. The inventory service hasn&apos;t seen the payment. The notification service is completely in the dark.&lt;/p&gt;
&lt;p&gt;The problems compound when we add real-world requirements like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Long-running processes with multi-step approvals, human intervention, and complex retry policies.&lt;/li&gt;
&lt;li&gt;Failure handling across internal services and third-party APIs.&lt;/li&gt;
&lt;li&gt;Visibility into the process for debugging, SLA monitoring, and state inspection.&lt;/li&gt;
&lt;li&gt;Complex flows with conditional paths, dynamic routing, parallel processing, and complex error recovery.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We need a model that centralizes process flow and state management. We need orchestration.&lt;/p&gt;
&lt;h2&gt;Orchestration&lt;/h2&gt;
&lt;p&gt;Instead of a process being defined across services coordinating through events, orchestration defines a central workflow which executes the process. This is exactly the approach that Temporal enables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
async def process_order(order: Order) -&amp;gt; str:
    inventory = await workflow.execute_activity(
        check_inventory,
        order.items,
        start_to_close_timeout=timedelta(minutes=5)
    )
    
    payment = await workflow.execute_activity(
        process_payment,
        order.payment,
        start_to_close_timeout=timedelta(minutes=5)
    )
    
    await workflow.execute_activity(
        update_inventory,
        order.items,
        start_to_close_timeout=timedelta(minutes=5)
    )
    
    return &quot;Order completed&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This now means that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;State becomes explicit - the workflow maintains a clear record of what happened.&lt;/li&gt;
&lt;li&gt;Failure handling becomes explicit - as defined in the workflow.&lt;/li&gt;
&lt;li&gt;Process tracking becomes trivial - we can see exactly where each workflow stands.&lt;/li&gt;
&lt;li&gt;Recovery becomes automatic - if the system goes down, it will resume from the last workflow state after recovering.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The complexity moves from individual services that each team builds, into reliable infrastructure that has proven itself across many companies over many years.&lt;/p&gt;
&lt;p&gt;But how does it work?&lt;/p&gt;
&lt;h1&gt;Temporal Overview&lt;/h1&gt;
&lt;p&gt;We will take a high-level explanation of Temporal, and avoid introducing too many concepts like namespaces, versioning, signals, timers, child workflows, cluster replication etc.&lt;/p&gt;
&lt;h2&gt;Service Architecture&lt;/h2&gt;
&lt;p&gt;The Temporal Server maintains the source of truth about what should happen. It stores workflow histories, dispatches tasks, tracks timeouts, and schedules retries. It contains multiple services and a database. It provides a crucial guarantee: to maintain a durable record of workflow histories.&lt;/p&gt;
&lt;p&gt;Workers handle your code execution, and are hosted by you. They pick up tasks, run your workflow and activity code, and then provides an updated workflow history to the server. They are stateless, and can encrypt payload data before sending it to the server.&lt;/p&gt;
&lt;p&gt;This separation between the server and workers creates a crucial property: program state exists independently from execution. Even if all your workers crash, the server maintains a durable record of what needs to happen next.&lt;/p&gt;
&lt;h2&gt;Workflows&lt;/h2&gt;
&lt;p&gt;Workflow code must run deterministically. This means it produces identical event logs across runs while potentially appending new events which trigger server tasks.&lt;/p&gt;
&lt;p&gt;This might seem counterintuitive. Most code contains side effects and runs from top-to-bottom once. Workflow code, however, eliminates side effects and executes many times within a single workflow execution.&lt;/p&gt;
&lt;p&gt;Consider this example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
async def process_order(order: Order) -&amp;gt; str:
    inventory = await workflow.execute_activity(
        check_inventory,
        order.items,
        start_to_close_timeout=timedelta(minutes=5)
    )
    
    payment = await workflow.execute_activity(
        process_payment,
        order.payment,
        start_to_close_timeout=timedelta(days=1)
    )
    
    await workflow.execute_activity(
        update_inventory,
        order.items,
        start_to_close_timeout=timedelta(minutes=5)
    )
    
    return &quot;Order completed&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A single workflow execution spans multiple workflow tasks, and builds a workflow history stored in the server:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WorkflowExecutionStarted
  - workflow_type: &quot;process_order&quot;
  - input: [serialized order data]

WorkflowTaskScheduled
WorkflowTaskStarted
WorkflowTaskCompleted
  - Commands: [ActivityTaskScheduled(check_inventory)]
  
ActivityTaskScheduled(check_inventory)
  - activity_type: &quot;check_inventory&quot;
  - input: [serialized items]
  - schedule_to_close_timeout: 5 minutes
ActivityTaskStarted(check_inventory)
ActivityTaskCompleted(check_inventory)
  - result: {&quot;available&quot;: true}
  
WorkflowTaskScheduled
WorkflowTaskStarted
WorkflowTaskCompleted
  - Commands: [ActivityTaskScheduled(process_payment)]
  
ActivityTaskScheduled(process_payment)
  - activity_type: &quot;process_payment&quot;
  - input: [payment details]
  - schedule_to_close_timeout: 5 minutes
ActivityTaskStarted(process_payment)
ActivityTaskCompleted(process_payment)
  - result: {&quot;success&quot;: true}
  
WorkflowTaskScheduled
WorkflowTaskStarted
WorkflowTaskCompleted
  - Commands: [ActivityTaskScheduled(update_inventory)]

ActivityTaskScheduled(update_inventory)
  - activity_type: &quot;update_inventory&quot; 
  - input: [items to update]
  - schedule_to_close_timeout: 5 minutes
ActivityTaskStarted(update_inventory)
ActivityTaskCompleted(update_inventory)
  
WorkflowTaskScheduled
WorkflowTaskStarted
WorkflowTaskCompleted
  - Commands: [CompleteWorkflowExecution]
  
WorkflowExecutionCompleted
  - result: &quot;Order completed&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If our system crashes after processing payment but before updating inventory, Temporal knows exactly where we were and what needs to happen next. When the system comes back online, the server dispatches the task to continue executing on the worker.&lt;/p&gt;
&lt;h2&gt;Activities&lt;/h2&gt;
&lt;p&gt;Pure deterministic execution within workflows has limits. Real systems must interact with non-deterministic systems like calling an API. Activities provide this bridge in Temporal.&lt;/p&gt;
&lt;p&gt;Consider an example activity to call the inventory service API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@activity.defn
async def check_inventory(items):
    return await inventory_service.check(items)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it completes successfully, then it will store the output into the workflow history in the corresponding ActivityTaskCompleted event.&lt;/p&gt;
&lt;p&gt;If it fails to complete, it will retry according to the retry policy the workflow executed the activity with. If it times out, then you will receive an explicit error you can handle in the workflow.&lt;/p&gt;
&lt;p&gt;Note that activities should be idempotent to ensure safe retries, and good workflow design means the workflow never fails.&lt;/p&gt;
&lt;h1&gt;Workflow Design Patterns&lt;/h1&gt;
&lt;p&gt;Now that you have a basic understanding of what Temporal is, we will go into design patterns.&lt;/p&gt;
&lt;p&gt;Treat this as pseudo-code. I fed an LLM some of the Temporal documentation and Python SDK to generate these because blogs can take a long time to write and I do not have a lot of time.&lt;/p&gt;
&lt;p&gt;There are no silver bullets in software, and that applies to workflow design as well. Do not cargo cult design patterns without understanding their trade-offs. Set retries and timeouts appropriate for your specific use-case.&lt;/p&gt;
&lt;h2&gt;Saga Pattern: Coordinated Transactions&lt;/h2&gt;
&lt;p&gt;You may require multiple things to happen together, or not at all, but you are subject to failures such as network partitions. The saga pattern can help you ensure that operations are atomic:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
async def transfer_funds(transfer: TransferRequest) -&amp;gt; str:
    try:
        # Debit source account
        debit = await workflow.execute_activity(
            debit_account,
            DebitRequest(
                account_id=transfer.source_id,
                amount=transfer.amount
            )
        )
        
        try:
            # Credit destination account
            credit = await workflow.execute_activity(
                credit_account,
                CreditRequest(
                    account_id=transfer.dest_id,
                    amount=transfer.amount
                )
            )
            return &quot;Transfer complete&quot;
            
        except Exception:
            # Compensation: Reverse debit
            await workflow.execute_activity(
                reverse_debit,
                debit.transaction_id
            )
            return &quot;Transfer failed&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admittedly, this is a classic example to give but also the least used pattern in my experience. I believe a major reason for this, is because most of the workflows I have written are designed to eventually succeed even if a fault occurs.&lt;/p&gt;
&lt;h2&gt;Polling Pattern: External Process Integration&lt;/h2&gt;
&lt;p&gt;If you ever call an API that is asynchronous, meaning it ends up starting a long-running progress in the background, you can use the polling pattern to elegantly await for the completion of it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
async def track_payment(payment: Payment) -&amp;gt; str:
    # Submit payment to external system
    tracking_id = await workflow.execute_activity(
        submit_payment,
        payment
    )
    
    # Checks every 60s, fails after four hours.
    status = await workflow.execute_activity(
		    check_payment_status,
        CheckPaymentStatusInput(tracking_id=tracking_id),
        start_to_close_timeout=timedelta(seconds=5),
        schedule_to_close_timeout=timedelta(hours=4),
        retry_policy=RetryPolicy(
            backoff_coefficient=1.0,
            initial_interval=timedelta(seconds=60),
        ),
    )
    
    return status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We did not go into workflow histories in this post, but the advantage to this method is that it is simple and keeps the workflow history size small.&lt;/p&gt;
&lt;h2&gt;Batch Processing Pattern: High-Volume Operations&lt;/h2&gt;
&lt;p&gt;If you have a large amount of work to complete, such as with batch processing, you can fan-out the work to child workflows.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
async def process_batch(batch: BatchConfig) -&amp;gt; BatchResult:
    futures = []
    
    for chunk in create_chunks(batch.items, size=100):
        handle = workflow.start_child_workflow(
            process_chunk,
            chunk,
        )
        futures.append(handle)
        
    return await asyncio.gather(*futures)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;More advanced use-cases will add a concurrency limiter on the calls, using a wait condition, so that you don’t overwhelm your system when handling batches.&lt;/p&gt;
&lt;h2&gt;Actor Pattern: Entity Management&lt;/h2&gt;
&lt;p&gt;This is, in my opinion, the holy grail of workflow use-cases because it represents the idea of orchestration systems mentioned at the beginning of this post:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@workflow
class PlayerWorkflow:
    def __init__(self):
        self._state = &quot;INITIALIZING&quot;
        self._inventory = {}
        self._health = 100
        self._position = Position(0, 0)
        self._pending_actions = []
        
    @workflow.run
    async def run(self, player_id: str) -&amp;gt; None:
        self._state = &quot;ACTIVE&quot;
        
        while True:
            # Process any pending actions
            while self._pending_actions:
                action = self._pending_actions.pop(0)
                await self._process_action(action)
                
            # Check if we need to continue-as-new
            if self._should_continue_as_new():
                workflow.continue_as_new(
                    player_id,
                    state=self._get_continuation_state()
                )
                
            # Wait for next action
            await workflow.wait_condition(
                lambda: bool(self._pending_actions)
            )
    
    @workflow.signal
    async def submit_action(self, action: PlayerAction):
        self._pending_actions.append(action)
        
    @workflow.query
    def get_player_state(self) -&amp;gt; PlayerState:
        return PlayerState(
            health=self._health,
            position=self._position,
            inventory=self._inventory
        )
        
    async def _process_action(self, action: PlayerAction):
        if action.type == &quot;MOVE&quot;:
            await self._handle_move(action.data)
        elif action.type == &quot;INVENTORY&quot;:
            await self._handle_inventory(action.data)
        elif action.type == &quot;COMBAT&quot;:
            await self._handle_combat(action.data)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My caution is that this is an advanced use-case, and if I was to write an actor workflow I would do it in a very different way than this. For example, &lt;a href=&quot;https://github.com/temporalio/samples-python/tree/main/message_passing/safe_message_handlers&quot;&gt;safe_message_handlers in temporalio/samples-python&lt;/a&gt; show one nice way of handling messages in a workflow.&lt;/p&gt;
&lt;p&gt;But the concept of having a single workflow that processes all messages for an entity is accurately represented.&lt;/p&gt;
&lt;h2&gt;And So Forth&lt;/h2&gt;
&lt;p&gt;We have barely even started talking about workflow design or patterns. The one cost of adopting Temporal is that you must learn how to design workflows. This means the adoption cost is slightly higher, although I believe often overstated, but this is also the strength of the tool because it forces you to make things like retry policies and timeouts explicit.&lt;/p&gt;
&lt;h1&gt;Focus on Solving Problems&lt;/h1&gt;
&lt;p&gt;It should be evident why I like Temporal for building distributed systems. Really, it lets me focus on solving problems rather than building reliable, usable, event infrastructure.&lt;/p&gt;
&lt;p&gt;If you would like to learn more, you can find real workflow examples at &lt;a href=&quot;https://github.com/temporalio/samples-python&quot;&gt;github.com/temporalio/samples-python&lt;/a&gt;. There are sample repos for every language they support.&lt;/p&gt;
&lt;p&gt;There are also several great talks available about Temporal on YouTube:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GEXllEH2XiQ&quot;&gt;Build an AI Agent with Temporal from Steve Androulakis&lt;/a&gt;: short example of building agentic tooling using Workflows.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LHkeXk_8Cq4&quot;&gt;Inception or deja vu all over again by Sergey Bykov - J On The Beach 2023&lt;/a&gt;: goes into how Temporal Cloud is built using Temporal (I helped build some of this).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=fqKDWZDj-c0&quot;&gt;Temporal @ Hashicorp | Replay 2023&lt;/a&gt;: talks about Temporal adoption at Hashicorp, including their authorization system.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=ybm86vpkpyo&quot;&gt;Actor Workflows: Reliably orchestrating thousands of Flink clusters at Netflix | Replay 2023&lt;/a&gt;: great deep dive into an implementation actor workflows.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=yeoawVIn060&quot;&gt;Keeping Workflow Developers Afloat | Drew Hoskins, Stripe&lt;/a&gt;: lessons from Stripe adopting Temporal from my old team lead (great advice on timeouts in here).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hope you found this interesting.&lt;/p&gt;
</content:encoded></item><item><title>Bring on the Rowhammer</title><link>https://tminusplus.dev/posts/bring-on-the-rowhammer/</link><guid isPermaLink="true">https://tminusplus.dev/posts/bring-on-the-rowhammer/</guid><description>Rowhammer explanation from the hardware perspective</description><pubDate>Sat, 02 Jun 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As dynamic random-access memory (DRAM) modules have become smaller and more packed with memory, an attack vector appeared which bypasses the underlying assumption we carry in software development: that the only way to change a piece of memory is to directly write to it.&lt;/p&gt;
&lt;p&gt;By quickly toggling pieces of memory in the right location, you can cause a piece of memory in another location to change value. This can allow you to attack other processes on the machine and gain privilege escalation with the kernel. It works because writing to memory causes electrical charge to dump into a cell, which causes a small amount of leakage to dump to other cells. The insulation found in between the cells is not enough to prevent this disturbance and even error-correcting code memory (ECC memory) can only guarantee that a bit or two won&apos;t flip at a time.&lt;/p&gt;
&lt;p&gt;Traditionally this attack was proposed by writing to memory, but today we&apos;ll take a deep dive into how you can reproduce this by reading memory and why that works.&lt;/p&gt;
&lt;h1&gt;Readhammer&lt;/h1&gt;
&lt;p&gt;We&apos;re going to take a peek at a &lt;a href=&quot;https://github.com/google/rowhammer-test&quot;&gt;practical DRAM Rowhammer exploit&lt;/a&gt; found by &lt;a href=&quot;https://github.com/mseaborn&quot;&gt;Mark Seaborn&lt;/a&gt; and &lt;a href=&quot;https://github.com/thomasdullien&quot;&gt;Thomas Dullien&lt;/a&gt;. Take a quick read of the warning on the repository before running the Rowhammer tests. It may cause your computer to halt and catch fire if it manages to flip the right bit.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;rowhammer_test&lt;/code&gt; will allocate 1 GB of memory, pick eight random memory addresses, read those addresses and then flush the cache to DRAM. There is magic here because we aren&apos;t actually attempting to flip bits by writing to them, like in a traditional Rowhammer attack, but instead we read them.&lt;/p&gt;
&lt;p&gt;The core of the program is as follows with my added comments. The trick in this code is to clear the CPU cache of the values we are reading by using the x86 instruction &lt;code&gt;CLFLUSH{:.macro}&lt;/code&gt; in &lt;code&gt;toggle(..){:.function}&lt;/code&gt;. This ensures we read the memory from DRAM instead of the CPU cache.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const size_t mem_size = 1 &amp;lt;&amp;lt; 30;
const int toggles = 540000;

void main_prog() {
  /* Allocate 1 GB (mem_size) of memory */
  g_mem = (char *) mmap(NULL, mem_size, PROT_READ | PROT_WRITE,
                        MAP_ANON | MAP_PRIVATE, -1, 0);
  assert(g_mem != MAP_FAILED);

  /* Set all bits to 1 in our alloc&apos;d memory, which allows us to detect if a
     bit flipped by looking for a bit not set to 1.

     Perhaps this also allows for greater voltage fluctuation, since every bit
     must be refreshed. */
  printf(&quot;clear\n&quot;);
  memset(g_mem, 0xff, mem_size);

  Timer t;
  int iter = 0;
  for (;;) {
    printf(&quot;Iteration %i (after %.2fs)\n&quot;, iter++, t.get_diff());

    /* Function below, read 8 random addresses 540000 (toggles) times
       and repeat this 10 times */
    toggle(10, 8);

    Timer check_timer;
    uint64_t *end = (uint64_t *) (g_mem + mem_size);
    uint64_t *ptr;
    int errors = 0;
    for (ptr = (uint64_t *) g_mem; ptr &amp;lt; end; ptr++) {
      uint64_t got = *ptr;
      /* Check if there was a bit flip by looking for a bit
         not set to 1 in our alloc&apos;d memory */
      if (got != ~(uint64_t) 0) {
        printf(&quot;error at %p: got 0x%&quot; PRIx64 &quot;\n&quot;, ptr, got);
        errors++;
      }
    }
    printf(&quot;  Checking for bit flips took %f sec\n&quot;, check_timer.get_diff());
    if (errors)
      exit(1);
  }
}

static void toggle(int iterations, int addr_count) {
  Timer timer;
  for (int j = 0; j &amp;lt; iterations; j++) {
    /* Pick the 8 random addresses */
    uint32_t *addrs[addr_count];
    for (int a = 0; a &amp;lt; addr_count; a++)
      addrs[a] = (uint32_t *) pick_addr();

    /* Read the 8 random addresses 540000 (toggles) times */
    uint32_t sum = 0;
    for (int i = 0; i &amp;lt; toggles; i++) {
      for (int a = 0; a &amp;lt; addr_count; a++)
        sum += *addrs[a] + 1;
      for (int a = 0; a &amp;lt; addr_count; a++)
        /* Flush the read memory from our cache using an x86 specific instruction */
        asm volatile(&quot;clflush (%0)&quot; : : &quot;r&quot; (addrs[a]) : &quot;memory&quot;);
    }

    // Sanity check. We don&apos;t expect this to fail, because reading
    // these rows refreshes them.
    if (sum != 0) {
      printf(&quot;error: sum=%x\n&quot;, sum);
      exit(1);
    }
  }
}

/* Pick a random memory address */
char *pick_addr() {
  size_t offset = (rand() &amp;lt;&amp;lt; 12) % mem_size;
  return g_mem + offset;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we&apos;ve seen the proof-of-concept code, we can start to break down how reading from DRAM can cause a bit flip in an adjacent memory cell.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   On destructive read                        After destructive read                                                                  
   ──────────────●───────────── Word line     ──────────────●───────────── Word line                                                  
   │             │                            │             │                                                                         
   │           1 ▼                            │           1 ▼                                                                         
   │           ──┴── Transistor               │           ──┴── Transistor                                                            
   │           ─┬─┬─                          │           ─┬─┬─                                                                       
   │            │ │                           │            │ │                                                                        
   ●──────◀─────┘ └──◀───┐                    ●──────▶─────┘ └──▶───┐                                                                 
   │                   ──┴── Capacitor        │                   ──┴── Capacitor                                                     
   ▼   Charge dumped   ──┬──                  ▲    Capacitor      ──┬──                                                               
   │   onto bit line     ▽                    │    recharged        ▽                                                                 
   │                                          │                                                                                       
Bit line                                   Bit line                                                                                   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two separate buses, the bit line and the word line. The bit line is for sending the bit value and receiving a value to write. The use of the word line will become clear in the next picture. Note the word line and bit line cross each other but are not connected, as signified by a lack of a circle at their intersection.&lt;/p&gt;
&lt;p&gt;There are two components here, the transistor and the capacitor. The transistor will connect the capacitor to the bit line when the word line has a value of 1. A value of 1 means a high voltage (1.8V) whereas a value of 0 means a low voltage (~0V). The capacitor stores the bit value and must discharge on a read to the bit line, making it a destructive read.&lt;/p&gt;
&lt;p&gt;This means that we must refresh the capacitor after reads to ensure it holds the same value as before the read. On an unrelated note, capacitors will leak charge meaning we must periodically refresh every cell in the DRAM module to prevent it from losing its value.&lt;/p&gt;
&lt;p&gt;We can zoom out of a single cell to the bigger picture of how a DRAM bank works below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;On destructive read                                                  After destructive read                                           
                                                                                                                                      
           Row                                                                  Row                                                   
           Decoder                                                              Decoder                                               
           ┌────┐                                                               ┌────┐                                                
           │    │ Word Lines ┌────────────┐                                     │    │ Word Lines ┌────────────┐                      
           │    ├────────────▶ ┌──────────┴─┐                                   │    ├────────────▶ ┌──────────┴─┐                    
Row  ──────▶    ├────────────▶ │            │                        Row  ──────▶    ├────────────▶ │            │                    
Addr ──────▶    ├────────────▶ │   Memory   │                        Addr ──────▶    ├────────────▶ │   Memory   │                    
           │    ├────────────▶ │   Array    │                                   │    ├────────────▶ │   Array    │                    
           │    │            └─┤            │                                   │    │            └─┤            │                    
           └────┘              └─┬──┬──┬──┬─┘                                   └────┘              └─▲──▲──▲──▲─┘                    
                                 │  │  │  │                                                           │  │  │  │                      
                            ┌────▼──▼──▼──▼────┐                                                 ┌────┴──┴──┴──┴────┐                 
                            │ Sense Amplifier  │ Sense bit line                                  │ Sense Amplifier  │ Refresh DRAM    
                            │                  │ and buffer it                                   │                  │ cells           
                            └────┬──┬──┬──┬────┘                                                 └────┬──┬──┬──┬────┘                 
                            ┌────▼──▼──▼──▼────┐                                                 ┌────┴──┴──┴──┴────┐                 
                            │    Row Buffer    │ Cache result of                                 │    Row Buffer    │                 
                            │                  │ entire row                                      │                  │                 
                            └────┬──┬──┬──┬────┘                                                 └────┬──┬──┬──┬────┘                 
                            ┌────▼──▼──▼──▼────┐                                                 ┌────┴──┴──┴──┴────┐                 
           Column ──────────▶  Column Decoder  │ Select part of                 Column ──────────┤  Column Decoder  │                 
           Addr   ──────────▶                  │ buffered row                   Addr   ──────────┤                  │                 
                            └────────┬─────────┘                                                 └────────┬─────────┘                 
                                     │ Output                                                             │                           
                                       (1 bit in this example)                                                                        
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The word lines connect to a row decoder which the CPU can use to select the row of DRAM it would like to read. This connects each cell in that row to the bit lines which cause the capacitors to discharge into the sense amplifier.&lt;/p&gt;
&lt;p&gt;The sense amplifier will detect if the bit line is low or high and output that into the row buffer. It will also  refresh the read value back into the capacitors by dumping charge back into them. This is why we can use reads for a Rowhammer attack.&lt;/p&gt;
&lt;p&gt;The row buffer will cache the bits read in an entire row and enhance read speeds for a row because we don&apos;t need to wait for the capacitors to discharge and then recharge. This is why we must pick memory locations in different rows, because if we didn&apos;t then the refresh would never occur after the first read.&lt;/p&gt;
&lt;p&gt;The final question is why does dumping charge into the DRAM cells sometimes cause other bits to flip. It is because DRAM modules are scaling to smaller physical dimensions to fit more memory onto a single chip. It becomes more difficult to prevent DRAM cells from electrically interacting with each other.&lt;/p&gt;
&lt;p&gt;You can imagine a scenario where we could target a specific machine, understand how the physical addresses relate to the DRAM rows, allocate a physically-contiguous page of memory and then Rowhammer both sides of a targetted row to make this more effective. This is dubbed Double-Sided Rowhammering.&lt;/p&gt;
&lt;h1&gt;Summary&lt;/h1&gt;
&lt;p&gt;We&apos;ve been so focused on building fast and small computers we&apos;ve allowed holes to slip in the design of reliable hardware and ultimately software. One of the great challenges of this era is how to build reliable software and that starts with reliable hardware. While Rowhammer is only one attack against one component in a computer, it is a signal that we should consider the security of a hardware design when trading off performance and size.&lt;/p&gt;
&lt;p&gt;Two steps to prevent Rowhammer attacks are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ship ECC memory in consumer devices to protect against single/double bit-errors&lt;/li&gt;
&lt;li&gt;Refresh rows located around hot rows with high numbers of reads/writes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hope you learned something!&lt;/p&gt;
&lt;h2&gt;Real-World Attacks&lt;/h2&gt;
&lt;p&gt;Here are some interesting Rowhammer attacks I found while researching for this blog:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.vusec.net/projects/glitch/&quot;&gt;GLitch&lt;/a&gt; - Rowhammer the memory in a mobile GPU by using Javascript on a website&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.cs.vu.nl/~herbertb/download/papers/throwhammer_atc18.pdf&quot;&gt;Throwhammer&lt;/a&gt; - Rowhammer with network packets by using remote direct memory access (RDMA)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vusec/drammer&quot;&gt;DRAMMER&lt;/a&gt; - Rowhammer used to attack an Android device&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item></channel></rss>