Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • How to Use This Course
  • Reading Paths
  • Concept Map
  • How to Read the Diagrams
  • Part 0: What Is a Protocol?10
  • Part 1: Timing and Clocking11
  • Part 2: Encoding and Framing12
  • Part 3: Handshakes13
  • Part 4: Flow Control17
  • Part 5: Transactions19
  • Part 6: Concurrency and Performance17
  • Part 7: Ordering13
  • Part 8: Sharing and Arbitration12
  • Part 9: Deadlock and Liveness13
  • Part 10: Reliability and Recovery20
  • Part 11: Bring-Up and Link Management10
  • Part 12: Power8
  • Part 13: Shared State and Coherence9
  • Part 14: Security and Isolation7
    • Access Attributes
    • Firewalls and Filtering
    • Address Translation for Devices
    • Link Integrity and Encryption
    • Fabric Denial of Service
    • Isolation and Multi-Tenancy
    • Drills: Security and Isolation
  • Part 15: Specifying and Verifying11
  • Part 16: Protocols Dissected17
  • Part 17: Capstones5
  • Part 18: Interview Preparation6
Loading lesson

‹Part 14: Security and Isolation
  1. Topics
  2. First Principles of Hardware Protocols
  3. Part 14: Security and Isolation
  4. Fabric Denial of Service

Part 14 · Security and Isolation · Principal (P)

Fabric Denial of Service

A requester does not need to break any access rule to hurt others: it only needs to send a lot. How one greedy agent fills shared queues and raises everyone's latency, modelled, and the fabric mechanisms that bound the damage.

On this page

    Note

    Level: Principal (P) · Answers: Who is allowed to ask?

    Before this: Fairness and Starvation, Head-of-Line Blocking

    A system's firewall and IOMMU are configured perfectly: every requester can reach only the memory it owns. One of them, a misbehaving accelerator, simply issues reads as fast as it can. Every one of them is allowed. Within milliseconds, the processor's memory latency has risen tenfold and a real-time audio stream starts to drop samples. No access rule was broken. The attack was volume.

    How does a fabric stop one requester's volume from denying service to the others?

    The obvious answer, and why it breaks

    The obvious defence is access control: block requests the requester is not allowed to make. A flood consists entirely of allowed requests. What it exhausts is shared capacity: queue slots, buffer entries, credits, and the target's bandwidth. In a single shared first-come-first-served queue, whoever keeps the most requests in it decides everyone's waiting time.

    Build it: one greedy requester, two policies

    A target serves one request per cycle. Three well-behaved requesters together use 30% of it. A greedy one keeps a fixed number of requests outstanding at all times. The model compares one shared FIFO queue with per-requester queues served round-robin.

    example.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    """Fabric denial of service: one greedy requester against three well-behaved ones.
    
    A shared target serves one request per cycle. Three victims each issue a request with
    probability 0.1 per cycle (30% of the target in total). The aggressor keeps LIMIT
    requests outstanding at all times, re-issuing as soon as one completes.
      shared FIFO:  one queue in arrival order; the aggressor's requests fill it
      round-robin:  one queue per requester; the target serves non-empty queues in turn
    Measured: the victims' mean and 99th-percentile latency (arrival to completion, cycles)
    and the share of the target the aggressor gets.
    """
    import random
    from collections import deque
    
    CYCLES, VICTIMS, P = 50000, 3, 0.1
    
    
    def run(policy, limit, seed=1):
        rng = random.Random(seed)
        queues = [deque() for _ in range(VICTIMS + 1)]   # index VICTIMS = aggressor
        fifo = deque()
        outstanding = 0
        lat, aggr_done, rr = [], 0, 0
        for t in range(CYCLES):
            for v in range(VICTIMS):
                if rng.random() < P:
                    (fifo.append((v, t)) if policy == "fifo" else queues[v].append(t))
            while outstanding < limit:                   # aggressor tops up
                (fifo.append((VICTIMS, t)) if policy == "fifo" else queues[VICTIMS].append(t))
                outstanding += 1
            # serve one request
            if policy == "fifo":
                who, t0 = fifo.popleft()
            else:
                for k in range(VICTIMS + 1):
                    q = (rr + k) % (VICTIMS + 1)
                    if queues[q]:
                        who, t0 = q, queues[q].popleft(); rr = q + 1; break
            if who == VICTIMS:
                outstanding -= 1; aggr_done += 1
            else:
                lat.append(t - t0 + 1)
        lat.sort()
        return sum(lat) / len(lat), lat[int(0.99 * len(lat))], aggr_done / CYCLES
    
    
    limits = [1, 2, 4, 8, 16, 32]
    fifo = [run("fifo", l) for l in limits]
    rr = [run("rr", l) for l in limits]
    print("aggressor outstanding:   " + " ".join(f"{l:6d}" for l in limits))
    print("FIFO victim mean (cyc):  " + " ".join(f"{m:6.1f}" for m, _, _ in fifo))
    print("FIFO victim p99 (cyc):   " + " ".join(f"{p:6d}" for _, p, _ in fifo))
    print("RR victim mean (cyc):    " + " ".join(f"{m:6.1f}" for m, _, _ in rr))
    print("RR victim p99 (cyc):     " + " ".join(f"{p:6d}" for _, p, _ in rr))
    print("aggressor share, FIFO:   " + " ".join(f"{s:6.2f}" for _, _, s in fifo))
    print("aggressor share, RR:     " + " ".join(f"{s:6.2f}" for _, _, s in rr))
    assert fifo[-1][0] > 10 * rr[-1][0] and max(p for _, p, _ in rr) <= 2 * (VICTIMS + 1)
    print("PASS: in one shared queue the greedy requester sets everyone's latency; per-requester queues with round-robin keep the victims' latency bounded however greedy it is")

    It prints:

    Diagram
    aggressor outstanding:        1      2      4      8     16     32
    FIFO victim mean (cyc):     1.6    3.0    5.9   11.5   22.9   45.7
    FIFO victim p99 (cyc):        4      6     10     17     30     55
    RR victim mean (cyc):       1.5    1.5    1.5    1.5    1.5    1.5
    RR victim p99 (cyc):          5      5      5      5      5      5
    aggressor share, FIFO:     0.70   0.70   0.70   0.70   0.70   0.70
    aggressor share, RR:       0.70   0.70   0.70   0.70   0.70   0.70
    PASS: in one shared queue the greedy requester sets everyone's latency; per-requester queues with round-robin keep the victims' latency bounded however greedy it is
    The model's numbers: with one shared queue, victims' latency grows in step with the greedy requester's queue depth, to 45.7 cycles; with per-requester round-robin it stays at 1.5 cycles.

    Two points stand out. In the shared queue, the victims' latency is set almost entirely by how many requests the greedy agent keeps queued: 32 outstanding requests mean about 46 cycles of waiting. And fairness costs nothing in throughput: under round-robin the greedy requester still gets the 70% the others leave unused. It simply cannot take their share or make them wait behind it.

    Mechanisms that bound the damage

    Mechanism

    What it limits

    Per-requester queues and fair arbitration

    Waiting time behind other requesters (lessons 8.3 and 4.14)

    Outstanding-request limits per requester

    How many shared slots one agent can hold

    Credits or buffers partitioned per requester or class

    Buffer exhaustion by one agent (lesson 4.6)

    Rate limiting or bandwidth regulation

    Sustained bandwidth per requester (lesson 4.10)

    QoS priorities with guarantees

    Latency for real-time traffic, with limits so priority cannot itself become a weapon (lesson 8.7)

    Timeouts and isolation of a faulty agent

    A requester that hangs or floods can be cut off (lesson 9.9)

    How real protocols chose

    System

    Defence against volume

    AXI interconnects

    QoS signals (AxQOS) and arbitration, with regulators limiting outstanding transactions and bandwidth per port

    Arm MPAM, Intel RDT

    Memory bandwidth and cache capacity partitioned between software groups (fact A28)

    PCI Express

    Per-virtual-channel credits and arbitration in switches

    What breaks

    The opening system (illustrative) had a single shared request queue in front of its memory controller. Giving each major requester its own queue, a limit on outstanding reads, and a bandwidth regulator for the accelerator brought the processor's memory latency back to normal under the same flood, at a cost of under 1% in the accelerator's throughput when the system was otherwise idle.

    Why this matters

    Principal lens. Treat shared capacity as a security resource, not only a performance one. For every shared queue, buffer and target, ask what one requester can do to the others if it sends as fast as it can, and bound it: separate queues, fair arbitration, per-requester limits and regulators. Mixed-criticality systems need these guarantees as much as they need access control.

    Interview

    Asked: "How can a requester cause denial of service without violating any access rule?"

    Weak answer: It can't if the firewall is right.

    Strong answer: By volume: it sends only allowed requests, but enough to fill shared queues, buffers and credits, so everyone else waits behind it. In one shared FIFO, the victims' latency grows with the attacker's queue depth; in a model with 32 outstanding requests it reached about 46 cycles against 1.5 with round-robin. Defences bound what one agent can take: per-requester queues with fair arbitration, outstanding-request limits, partitioned credits, bandwidth regulators, and QoS with guarantees, plus timeouts to isolate a faulty agent. Fairness doesn't cost throughput: the greedy agent still gets the spare bandwidth.

    Follow-ups: (1) Why can strict priority be a denial-of-service risk? (2) What does an outstanding-request limit protect?

    Check yourself In the model, why does round-robin keep the victims' latency at 1.5 cycles whatever the greedy requester does?

    Each requester has its own queue and gets a turn every few cycles, so a victim never waits behind the greedy requester's backlog.

    Check yourself Why can strict priority itself enable denial of service?

    A high-priority requester that floods can starve every lower-priority one; priority needs limits or guarantees.

    Key takeaways

    • Denial of service needs only volume: allowed requests that exhaust shared capacity.

    • In one shared queue, the greediest requester sets everyone's latency.

    • Bound each requester with separate queues, fair arbitration, limits and regulators; fairness costs no throughput.

    Keep going

    Next: Lesson 14.6, Isolation and Multi-Tenancy. Many tenants on one chip: everything in this part, combined.

    Related topics

    • Part 14 overview: Security and Isolation

    Previous

    193 / 234

    Next: Isolation and Multi-Tenancy

    Up next

    Up nextIsolation and Multi-Tenancy

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Access Attributes
    • Firewalls and Filtering
    • Address Translation for Devices