A mitigation reduces a possible consequence or makes a failure harder to use. It is different from correcting an invalid write. A destination must still be large enough, or the program must refuse the write before it happens.
NX, no-execute, restricts running instructions from marked memory pages. A stack canary is a guard value checked in covered functions. ASLR, address space layout randomization, varies placement of memory regions. PIE, position-independent executable, is a build property that lets an executable be loaded at different base addresses. These are four different roles, not four proofs that a program is safe.
Before you begin
- Recommended first
- Enough C and assembly to follow a compare and a jump, A beginner license check, Where practice is allowed, What a buffer overflow is
- Estimated time
- 20 min reading, 16 min activity. Setup and optional practice may take longer.
- Where you work
- Use the printed memory and source models in a desktop or mobile browser, with paper or a text editor. No compilation, debugger, pwntools installation, mitigation change or executable is needed. The layouts are conceptual, not measured process memory. Optional later tools need a separately checked owned environment and budget.
- Without a lab
- All evidence needed for the activity appears on this page. Copy it into notes if downloads are unavailable. Read the labeled model and predict each result; a paper trace practises the reasoning while tool operation waits for a lab. Reading needs no JavaScript. Answer checking needs JavaScript and a secure context; saving progress also needs usable browser storage.
After this lesson you can:
- Name the constraint each of the four worked rows is for.
- Match activity cards A through D without treating a match as a repair.
- Leave write Q wrong after the four matches.
01Example
Fresh paired mechanism model. It describes roles, not an inspected build.
Model event: a protected guard changes unexpectedly.
Mechanism: a canary check can detect that change at its check point.
Limit: the invalid write already occurred; coverage depends on the build.
Independent size fact: 12 bytes do not fit in 10 positions.
A mechanism’s presence does not repair that size mismatch.02What each one constrains
Memory is managed in regions called pages, with permissions such as read, write and execute. NX constrains execution on marked pages; a writable non-executable page can still be modified. A stack permission does not describe every page. Linker/loader metadata such as PT_GNU_STACK, the executable’s stack-requirement record, helps determine actual stack permissions; no such file inspection is supplied here.
A canary checks a guard in functions selected by the compiler’s protection options. A changed guard may trigger termination at the check; it does not intercept every write. GCC’s basic stack-protector option covers a defined subset of stack objects/functions, and broader variants differ. Do not infer a distribution’s default or a guard’s exact position from this role model.
ASLR is a runtime placement policy on supported systems. PIE supplies an executable form that can participate in varying its load address. Build flexibility and actual randomized placement are different facts. Neither compares write length with capacity.
A debugger or helper can affect execution conditions. GDB documents a default that disables native address randomization for programs it starts where supported, so a debugger’s addresses need not match ordinary launch conditions. Record the actual environment in later practice; changing mitigation settings is not required here.
Claims about a real binary need its exact identity, build/tool versions, flags and runtime conditions. The hardening record lists these gaps. No observed checksec/readelf report or matched runtime memory evidence is supplied, so the lesson makes role claims only.
Match hardening mechanisms to the consequences they constrain while preserving the need to fix the bounds error.Model. A teaching model drawn by hand. It shows the idea, not real tool output.
Read the full explanation below.
A protected guard changes unexpectedly after a modeled invalid write.
Modeled guard check
Independent bounds fact
A mitigation constrains consequences; it does not repair the bounds error or establish a real binary's protection.
A protected guard changes unexpectedly after a modeled invalid write.
Follow the explanation
- A protected guard changes unexpectedly after a modeled invalid write.
- A canary check can detect the change at its check point; the bounds mismatch remains.
Read every state and value
1. A protected guard changes unexpectedly after a modeled invalid write.
- Example
- Authored canary model
- Items
- Value: changed unexpectedly; Status: after invalid write · Value: not checked yet; Status: pending · Value: 12 bytes / 10 positions: does-not-fit; Status: unchanged · Value: unknown; Status: not supplied
- Active Item
- guard
- Event Label
- A protected guard changes unexpectedly after a modeled invalid write.
- Visible Limit
- A mitigation constrains consequences; it does not repair the bounds error or establish a real binary's protection.
2. A canary check can detect the change at its check point; the bounds mismatch remains.
- Example
- Authored canary model
- Items
- Value: changed unexpectedly; Status: unchanged · Value: can detect changed guard; Status: check point · Value: 12 bytes / 10 positions: does-not-fit; Status: unchanged · Value: unknown; Status: build-dependent
- Active Item
- check · size
- Event Label
- A canary check can detect the change at its check point; the bounds mismatch remains.
- Visible Limit
- A mitigation constrains consequences; it does not repair the bounds error or establish a real binary's protection.
03Match the four cards
Use the four authored cards to match mechanisms by their stated behavior. The leftover nine-byte request remains separate from all four roles.
Label: four role cards. Not a scan, not checksec, not readelf, and not a debugger session.
No binary was built. No host setting was read or changed.
Card A
Mark: the stack's model address differs across two ordinary runs of the same program.
Match it to the layout policy named in the reading.
Card B
Mark: a guard value is checked when the function is about to return.
If the check fails, the compiler's basic option is documented to print an error and exit.
That still does not make the original write correct.
Card C
Mark: the executable was built so it can be loaded at more than one address.
That is a build property. It is not, by itself, the kernel's choice to vary the layout on each run.
Card D
Mark: instructions will not run from this page. The card does not say the page is immune to writes.
Leftover bug, separate from the four marks:
pad has 8 positions. Write Q asks for 9 bytes. The write does not fit.
None of the four marks makes Write Q correct.
- Match A’s varying modeled addresses to ASLR and B’s checked guard to a stack canary. Explain placement versus detection.
- Match C’s flexible executable load address to PIE and D’s execute restriction to NX. Explain build property versus page permission.
- Compare Write Q’s nine bytes with eight positions. It still does not fit even if each mitigation is present.
- Stop after the role-and-limit table. No compiler, kernel, debugger or helper setting is changed.
04Optional challenge
For about ten optional minutes, use hardening-record.txt to draft the evidence needed before accepting a real protection claim: exact binary/hash, platform and architecture, compiler/link flags, inspecting tool/version and relevant runtime conditions. Leave missing observations explicit.
Label: missing hardening report. This is not checksec output, not readelf output, and not a compiler log.
No binary was built. No mitigation setting was changed. No tool was installed.
Before a later report can count, it needs all four of these:
1. The file name and a hash of that exact file.
2. The operating system, the machine family, and the compiler version.
3. The exact compile and link flags.
4. The name and version of the tool that printed the report.
Glossary for reading such a report later. These are manual roles, not results from this course.
NX, in this lesson: a page can be marked so the processor is not asked to run instructions from it.
The GNU linker manual's noexecstack keyword marks an object as not requiring an executable stack.
The same manual has a keyword that marks an object as requiring one. This course does not ask you to request an executable stack.
The GNU C Library uses PT_GNU_STACK, when that segment is present, in deciding whether to mark the stack non-executable.
A non-executable stack can still be writable. A mark on the stack is not a mark on every page.
Some machines do not separate a read permission from an execute permission. This course did not inspect one.
Stack canary, in this lesson: GCC's -fstack-protector option emits a guard variable for functions with the objects that option covers, including buffers of at least 8 bytes and functions that call alloca, which requests dynamic allocation in the current stack frame.
The guard is set on entry and checked on exit. The manual says a failed check prints an error and the program exits.
Only variables actually allocated on the stack are considered. A value kept in a register is outside that check.
The option does not check every function unless a broader variant is selected, and this course did not record which variant a distro uses by default.
ASLR, in this lesson: the proc_sys_kernel manual describes a system policy, present since Linux 2.6.12 on architectures that support it, that selects address space layout randomization.
This course does not ask you to read or write that policy. A layout that varies is not a length check.
PIE, in this lesson: GCC's -fPIE compiles position-independent code that can be linked into an executable, and -pie asks the link to produce a position-independent executable on targets that support it.
The GDB manual says such an executable is loaded at a randomly chosen address when randomization is in use.
PIE without that runtime policy is not evidence that every run moved. This course did not check a distro compiler's default.
Limit that applies to all four: none of them turns a write past a buffer into a correct write.
05Fresh cases for the checks
Use these fictional cases for the checks below. They have different inputs from the worked examples. Read them here or make notes; no installation or external service is needed.
Mitigation case A: fresh role observationsOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
A: a page permits data writes but does not permit instruction execution.
B: a protected function checks a guard value before returning.
C: a system layout policy varies placement between ordinary runs.
D: an executable is built so it can be loaded at different addresses.
These are authored role descriptions, not a hardening report for an inspected file.
Mitigation case B: independent evidence and boundsOwned fixture. An original file made for this course. Follow the activity's instructions in your own lab.
Build card: executable can be loaded at more than one address.
No runtime layout policy or address observations are supplied.
Buffer capacity: 9 positions. Requested write: 10 raw bytes; no extra terminator.
No compiler log, binary hash or inspection-tool/version record is supplied.
06Explain your evidence
Optional written self-check: explain one decision using the supplied case. No tool run is required.
Check your explanation: match each role and its coverage limit; separate PIE build capability from runtime ASLR evidence; keep the size violation independent; identify what makes a later artifact report traceable.
07Common mistakes
If NX is described as blocking writes, separate page write permission from execute permission.
If PIE alone is taken to prove varying addresses, distinguish a build’s capability from runtime policy and observed placement.
If a canary seems to check every memory access, identify its covered function and check point; the original bounds error still needs repair.
References
- GCC manual: Program Instrumentation Options
Compiler/linker roles and coverage; actual build flags and runtime evidence are separate.
- GCC manual: Options for Code Generation
Compiler/linker roles and coverage; actual build flags and runtime evidence are separate.
- GCC manual: Options for Linking
Compiler/linker roles and coverage; actual build flags and runtime evidence are separate.
- GNU ld: Options, including the stack keywords
Compiler/linker roles and coverage; actual build flags and runtime evidence are separate.
- GNU C Library: Dynamic Linking Tunables
Runtime policy and debugger launch conditions; no setting change is required.
- proc_sys_kernel(5): address space layout randomization
Runtime policy and debugger launch conditions; no setting change is required.
- Debugging with GDB: Starting your Program
Runtime policy and debugger launch conditions; no setting change is required.