Skip to content

Add ASan shadow poisoning and fill-pattern instrumentation#20

Merged
jserv merged 1 commit intomasterfrom
poison
Feb 9, 2026
Merged

Add ASan shadow poisoning and fill-pattern instrumentation#20
jserv merged 1 commit intomasterfrom
poison

Conversation

@jserv
Copy link
Copy Markdown
Collaborator

@jserv jserv commented Feb 9, 2026

Teach AddressSanitizer about TLSF's internal pool layout so it can detect use-after-free and buffer overflow within custom memory pools. Free block payloads are poisoned (excluding embedded TLSF metadata: free-list pointers and next block's prev pointer), and unpoisoned on allocation, realloc expansion, and pool growth/append.

A separate compile-time option (-DTLSF_ENABLE_POISON) fills payloads with 0xAA on alloc and 0xFF on free, catching stale-pointer bugs on bare-metal targets where sanitizers are unavailable.

Extend CI matrix with ASan+UBSan builds for gcc and clang on both x86-64 and ARM64, plus a combined ASan+POISON configuration.


Summary by cubic

Integrates ASan shadow poisoning into TLSF to catch use-after-free and overflows, plus an optional fill-pattern mode for bare‑metal. CI adds ASan+UBSan on x86‑64 (gcc/clang) and ARM64 (gcc), and an ASan+POISON job on x86‑64 (clang).

  • New Features

    • ASan: poison only the safe region of free block payloads (skip TLSF metadata); unpoison on alloc/realloc and on pool init/grow/append.
    • Fill mode (-DTLSF_ENABLE_POISON): 0xAA on alloc, 0xFF on free to expose stale-pointer and uninitialized reads.
  • Migration

    • Works automatically with -fsanitize=address; no code changes.
    • Enable fill-pattern with -DTLSF_ENABLE_POISON.

Written for commit d2ee54b. Summary will update on new commits.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/tlsf.c">

<violation number="1" location="src/tlsf.c:564">
P2: ASan coverage gap: the left-trim free block in `block_ltrim_free` is never poisoned. After `block_split` poisons only the right portion (`rest`), the left portion (`block`) is inserted into the free list with its payload fully unpoisoned. Stale accesses to that region won't be detected by ASan.

Add `block_poison_free(block)` after `block_insert(t, block)` in `block_ltrim_free`, matching the pattern used in `arena_grow`, `arena_append_pool`, and `tlsf_free`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread src/tlsf.c
Teach AddressSanitizer about TLSF's internal pool layout so it can
detect use-after-free and buffer overflow within custom memory pools.
Free block payloads are poisoned (excluding embedded TLSF metadata:
free-list pointers and next block's prev pointer), and unpoisoned on
allocation, realloc expansion, and pool growth/append.

A separate compile-time option (-DTLSF_ENABLE_POISON) fills payloads
with 0xAA on alloc and 0xFF on free, catching stale-pointer bugs on
bare-metal targets where sanitizers are unavailable.

Extend CI matrix with ASan+UBSan builds for gcc and clang on both
x86-64 and ARM64, plus a combined ASan+POISON configuration.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 9, 2026

WCET Results (x86-64)

TLSF WCET Analysis
==================
Timer:      cycles
Cache:      hot
Pool:       4194304 bytes (4.0 MB)
Iterations: 5000 (warmup: 500)
Sizes:      16 64 256 1024 4096 bytes

--- malloc_worst (small alloc from single huge block) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         73         98         98        123        221        245       99.0        8.5
      64         73         98         98        123        196      30723      104.9      433.1
     256         73         98         98        122        220        245       92.0       12.1
    1024         73         98         98        123        196        245       94.6       11.4
    4096         73         98         98        123        123        270       96.5       10.2

--- malloc_best (exact bin hit, no split) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         49         73         74         74        123        367       70.4        9.9
      64         49         73         74         98         98        147       72.7        8.3
     256         49         73         74         98         98        147       67.8       13.8
    1024         49         73         74         98         98        147       71.3       11.0
    4096         49         73         74         74         74        147       69.6        9.3

--- free_worst (sandwiched between two free blocks) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         49         74         98         98        147        221       75.2       10.7
      64         49         74         98         98        147        245       77.8       11.7
     256         49         74         98        122        172        441       75.7       12.8
    1024         49         74         98         98         98        147       75.4       10.3
    4096         73         98         98         98        123        245       89.5       12.1

--- free_best (no merge (used neighbors)) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         49         73         74         74        122        123       63.4       12.3
      64         49         73         74         74        122        196       63.4       12.5
     256         49         73         74         74        123      16439       65.5      231.9
    1024         49         73         74         74        123        221       61.8       12.8
    4096         49         73         74         74         74        123       63.0       12.2

--- worst/best ratio (p99) ---
    size     malloc       free
      16      1.26x      1.32x
      64      1.26x      1.32x
     256      1.66x      1.32x
    1024      1.66x      1.32x
    4096      1.00x      1.00x

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 9, 2026

WCET Results (arm64)

TLSF WCET Analysis
==================
Timer:      ticks
Cache:      hot
Pool:       4194304 bytes (4.0 MB)
Iterations: 5000 (warmup: 500)
Sizes:      16 64 256 1024 4096 bytes

--- malloc_worst (small alloc from single huge block) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         24         32         32         32         40         40       30.4        3.3
      64         24         32         32         32         40         40       30.3        3.3
     256         16         32         32         32         40         48       30.3        3.4
    1024         24         32         32         32         40         40       30.0        3.5
    4096         24         32         32         32         40         40       30.1        3.4

--- malloc_best (exact bin hit, no split) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         16         24         32         32         32         40       25.0        2.7
      64          8         24         32         32         32         32       25.0        2.7
     256         16         24         24         32         32         32       24.7        2.4
    1024         16         24         32         32         32         40       24.9        2.6
    4096         16         24         32         32         32         32       24.8        2.4

--- free_worst (sandwiched between two free blocks) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16         16         24         32         32         32         40       25.7        3.4
      64          8         24         32         32         32         32       25.8        3.4
     256         16         24         32         32         32         32       25.6        3.2
    1024         16         24         32         32         32         40       25.7        3.3
    4096         16         24         32         32         32         32       25.9        3.4

--- free_best (no merge (used neighbors)) ---
    size        min        p50        p90        p99      p99.9        max       mean     stddev
      16          8         24         24         24         24         32       20.6        4.0
      64          8         24         24         24         24         32       20.6        4.0
     256          8         24         24         24         24       3984       21.3       56.2
    1024         16         24         24         24         24         32       20.6        4.0
    4096          8         24         24         24         24       2424       21.1       34.2

--- worst/best ratio (p99) ---
    size     malloc       free
      16      1.00x      1.33x
      64      1.00x      1.33x
     256      1.25x      1.33x
    1024      1.00x      1.33x
    4096      1.00x      1.33x

@jserv jserv merged commit d8aebbe into master Feb 9, 2026
13 checks passed
@jserv jserv deleted the poison branch February 9, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant