Introduction

Every program that runs on your computer lives in memory, and memory has addresses — numbers that say where each byte of code or data sits. For decades, attackers exploited a simple fact: on a given operating system, your program would always load in the same place.

A classic attack like a buffer overflow works by overwriting a return address and jumping to attacker-controlled code. To pull this off, the attacker needs to know where that code lands. If the address never changes, they can learn it once and use it forever.

ASLR — Address Space Layout Randomization — breaks that assumption. Every time a program starts, the operating system places the code segment, the stack, the heap, and shared libraries at randomly chosen locations. The attacker who memorized the right address now faces an empty guess. First deployed by Linux's PaX patch team around 2001 and later adopted by every major OS, ASLR is today a baseline defense on every device you own.

Try It: Shuffle the Memory Map

Each row below represents one memory region: code (the executable), stack (local variables and return addresses), and heap (dynamic allocations). Press New run to simulate a fresh program launch — ASLR picks a new random base for each region, just as a real OS would.

<!-- {{c_html_intro}} -->
<div class="controls">
  <button id="btn-run" type="button">{{btn_run}}</button>
  <label class="entropy-label">
    {{lbl_entropy}}
    <input id="entropy-slider" type="range" min="8" max="28" value="16" step="4">
    <span id="entropy-val">16</span> {{lbl_bits}}
  </label>
</div>
<div class="map-wrap">
  <div class="map" id="memory-map">
    <!-- {{c_rows_injected}} -->
  </div>
  <div class="axis" id="addr-axis">
    <!-- {{c_axis_injected}} -->
  </div>
</div>
<div class="history" id="history">
  <!-- {{c_history_injected}} -->
</div>
/* {{c_css_intro}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; margin-bottom: .8rem; }
button { font: 600 14px system-ui; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
.entropy-label { display: flex; align-items: center; gap: .4rem; font-size: .9rem; color: #444; }
#entropy-slider { width: 120px; accent-color: #1d3557; }
/* {{c_css_map}} */
.map-wrap { display: flex; gap: 0; align-items: stretch; }
.map { flex: 1; display: flex; flex-direction: column; gap: 4px; padding: .4rem 0; }
.region { display: flex; align-items: center; border-radius: 6px; padding: 0 .8rem;
          font-size: .85rem; font-weight: 600; color: #fff; transition: background .2s; }
.region span.addr { font: 600 12px ui-monospace, monospace; margin-right: .5rem; opacity: .85; min-width: 90px; }
.region span.name { flex: 1; }
.region span.size { font-size: .75rem; opacity: .7; margin-left: .4rem; }
.region.code    { background: #1d6fa4; min-height: 48px; }
.region.heap    { background: #2a9d8f; min-height: 36px; }
.region.stack   { background: #e76f51; min-height: 44px; }
.axis { display: flex; flex-direction: column; justify-content: space-between;
        padding: .4rem 0 .4rem .5rem; font: 11px ui-monospace, monospace; color: #888; width: 70px; }
/* {{c_css_history}} */
.history { margin-top: .8rem; border-top: 1px solid #dde; padding-top: .5rem; }
.history h4 { margin: 0 0 .4rem; font-size: .82rem; color: #555; font-weight: 600; }
.history table { border-collapse: collapse; font: 12px ui-monospace, monospace; width: 100%; }
.history th { font-weight: 700; color: #333; border-bottom: 1px solid #ccc; padding: 2px 6px; text-align: left; }
.history td { padding: 2px 6px; color: #444; }
.history tr:nth-child(even) td { background: #f4f6f8; }
.history .new-row td { animation: flash .4s ease; }
@keyframes flash { from { background: #d0eaff; } to { background: inherit; } }
// Code not found

Notice that the relative order of the regions stays the same (code below stack, heap between them), but the absolute addresses are different every time. An attacker who hardcodes 0x400000 as the code base will almost certainly jump to garbage — or crash — instead of reaching their payload. The larger the entropy bits, the bigger the random space and the harder the blind guess.

How It Really Works

ASLR is elegant in its simplicity: the OS adds a random offset to each segment's base address at load time. The security strength comes entirely from how many random bits are used.

  • Entropy bits are the key measure. With bb bits of entropy, there are 2b2^{b} possible base addresses. On 64-bit Linux the stack gets about 28 random bits — over 268 million positions. A blind brute-force attempt has a 1/2b1/2^{b} success probability per try.
  • KASLR (Kernel ASLR) applies the same idea to the kernel itself, so even a compromised userspace process doesn't automatically know where kernel code lives.
  • Weaknesses exist. 32-bit systems have far fewer entropy bits — sometimes only 8–16 — making repeated attempts feasible. Information leaks (bugs that print an address to the attacker) let them recover the base and bypass ASLR entirely. Return-oriented programming (ROP) can sometimes be chained without needing precise addresses.
  • Layered defenses. ASLR is never deployed alone. It combines with stack canaries, non-executable memory (NX/DEP), and Control-Flow Integrity (CFI) to raise the cost of exploitation collectively — no single layer is enough, but together they make reliable exploits extremely difficult.

The deeper point: ASLR converts a deterministic attack (jump to the known address) into a probabilistic one (guess correctly from 2b2^{b} possibilities). Probability is not a proof of security, but it raises the attacker's cost dramatically.

Where It Matters

ASLR is not a research curiosity — it is active on every device you use today:

  • Operating systems: Linux (since 2005 in mainline), Windows Vista+, macOS 10.5+, Android, and iOS all enable ASLR by default. Disabling it is an explicit opt-out.
  • Browsers: Chrome, Firefox, and Safari run renderer processes under ASLR and add their own JIT-code randomization so that even script-generated machine code lands at unpredictable addresses.
  • Servers and cloud: container runtimes and hypervisors rely on KASLR to isolate tenant workloads — a guest OS compromise should not immediately reveal host kernel addresses.
  • Embedded and IoT: modern microcontrollers (Cortex-M33 with TrustZone, RISC-V with PMP) are adding hardware-assisted address randomization even in resource-constrained settings.
  • Security research: every CTF (capture-the-flag) challenge involving memory corruption must account for ASLR, making entropy measurement and leak-hunting core skills for exploit developers.

Understanding ASLR is understanding the probabilistic layer of the modern security stack — the randomness that sits between a memory bug and a working exploit.

Conclusion

Address Space Layout Randomization is a beautiful application of a simple idea: if your enemy needs to know where something is, move it somewhere new every time. By randomizing the base addresses of code, stack, and heap on each program launch, ASLR transforms a reliable exploit into a blind lottery.

It is not magic — information leaks and low-entropy 32-bit systems can break it. But deployed alongside stack canaries, NX memory, and CFI, ASLR is one of the reasons that writing a reliable memory-corruption exploit in 2024 requires far more sophistication than it did in 2000. The attacker's determinism became our randomness, and that trade has paid off at every scale from smartphones to cloud infrastructure.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/address-space-layout-randomization/Content licensed under CC BY-NC 4.0.