Skip to content
Back to skills

430 Asan Validation E97e1aeb

ASecurity

Reference for detecting memory bugs in C stub code using AddressSanitizer.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
testingpythonshellbashdebugging

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 430-asan-validation_e97e1aeb --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 430 Asan Validation E97e1aeb?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 430 Asan Validation E97e1aeb
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-430-asan-validation-e97e1aeb/badge)](https://www.skillsdirectory.com/skills/tools-only-430-asan-validation-e97e1aeb)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# AddressSanitizer (ASan) Validation for MoonBit C Bindings

Reference for detecting memory bugs in C stub code using AddressSanitizer.

## Why ASan Matters

C bindings introduce manual memory management invisible to MoonBit's GC. These bugs
silently corrupt memory or leak resources. ASan catches them at runtime:

| Bug Class | Typical Cause in Bindings |
|---|---|
| Use-after-free | Accessing a C object after its finalizer ran |
| Double-free | Calling `moonbit_decref` on an already-released object |
| Memory leaks | Missing finalizer via `moonbit_make_external_object` |
| Buffer overflow | Wrong size passed to `moonbit_make_bytes` |
| Use-after-return | Returning a pointer to a local C variable |

## Quick Start

The skill includes `scripts/run-asan.py` which automates the full workflow.
It also works in CI environments.

Single package:

```bash
python3 scripts/run-asan.py --repo-root <project-root> --pkg moon.pkg
```

Multiple packages:

```bash
python3 scripts/run-asan.py \
  --repo-root <project-root> \
  --pkg moon.pkg \
  --pkg main/moon.pkg
```

The `--pkg` argument accepts both `moon.pkg` (DSL format) and `moon.pkg.json`
(JSON format). If the specified file doesn't exist, the script tries the other
format automatically.

Note for multiple packages, you need to include all packages with `native-stub`
and all entry packages. A package is an entry package if:

1. It is configured to be a main package, that is `is-main` set to true in
   `moon.pkg`/`moon.pkg.json`.
2. It contains tests.

---

## How It Works

The script combines two mechanisms. Understanding them is also useful for
manual setup or debugging.

### 1. Disable mimalloc

MoonBit bundles mimalloc as its allocator via `libmoonbitrun.o`. mimalloc
intercepts `malloc`/`free`, preventing ASan from tracking allocations. The
script replaces `libmoonbitrun.o` with an empty compiled object and restores
it afterward. Pass `--no-disable-mimalloc` to skip this step.

### 2. Package config patching

ASan flags must be injected into package config files. The script snapshots,
patches, and restores them in a `try/finally` block:

| Field | How it's patched | Why |
|---|---|---|
| `cc-flags` | Set to `-g -fsanitize=address -fno-omit-frame-pointer` | Instruments MoonBit-generated C code |
| `stub-cc-flags` | **Append** the same flags to existing value | Instruments C stub files (preserves `-I`, `-D` flags) |

Patch `stub-cc-flags` on all packages with `native-stub` (unconditionally safe to
patch all packages). Patch `cc-flags` on all entry packages (`is-main` or test).

### Environment variables

The script sets:

- `ASAN_OPTIONS="detect_leaks=<0|1>:fast_unwind_on_malloc=0"` — enables ASan
  and (where supported) LSan leak detection. `fast_unwind_on_malloc=0`
  produces more accurate stack traces.
- `LSAN_OPTIONS="suppressions=<path>"` — if a `.lsan-suppressions` file exists
  at the project root, it is passed to LSan (see Leak Suppressions below).

---

## Platform Setup

### macOS

**Homebrew LLVM** (preferred) — supports both ASan and LSan (leak detection).
The script probes `llvm`, `llvm@18`, `llvm@19`, `llvm@15`, `llvm@13`
automatically. Install with `brew install llvm`.

**System clang (Xcode 15+)** (fallback) — supports ASan but **not** LSan.
Leak detection is disabled (`detect_leaks=0`).

**Compiler override via `MOON_CC` + `MOON_AR`:** On macOS, the script sets
`MOON_CC` and `MOON_AR` to use Homebrew LLVM explicitly, because Apple Clang
does not support LeakSanitizer. This override is only needed on macOS — on
Linux, the system compiler supports ASan natively and when `cc-flags` is
present, tcc is disabled automatically.

Key constraints:

- `MOON_CC` accepts a compiler **path only** (e.g., `/opt/homebrew/opt/llvm/bin/clang`).
  Flags like `-fsanitize=address` cannot be included — moon treats the value
  as a single executable path.
- `MOON_AR` is **ignored** unless `MOON_CC` is also set.
- On macOS, moon derives `ar` from the compiler path. Homebrew LLVM has
  `llvm-ar` but not `ar`, so `MOON_AR=/usr/bin/ar` is needed.

### Linux

System `gcc` or `clang` on most distributions includes ASan out of the box.
On minimal images, install `libasan` (e.g., `apt-get install libasan6`).

### Windows

Use `cl.exe` with `/Z7 /fsanitize=address` for compilation. To disable
mimalloc manually:

```powershell
echo "" >dummy_libmoonbitrun.c
$moon_home = if ($env:MOON_HOME) { $env:MOON_HOME } else { "$env:USERPROFILE\.moon" }
$out_path = Convert-Path "$moon_home\lib\libmoonbitrun.o"
cl.exe dummy_libmoonbitrun.c /c /Fo: $out_path
```

---

## Leak Suppressions

macOS system libraries (libobjc, libdispatch, dyld) have known leaks that
trigger false positives. Place `.lsan-suppressions` at the project root:

```plaintext
leak:_libSystem_initializer
leak:_objc_init
leak:libdispatch
```

Each `leak:<pattern>` is matched against stack traces. If any frame matches,
the leak is suppressed. The script passes the absolute path to
`LSAN_OPTIONS` automatically.

Only suppress leaks from system/third-party code you do not control. Never
suppress leaks in your own C stub functions.

---

## Interpreting Results

### heap-use-after-free

Object was freed but still accessed. Check finalizer order and that `moonbit_decref`
is not called too early. Verify raw C pointers do not outlive the MoonBit wrapper.

### double-free

Same pointer freed twice. Ensure each C resource has exactly one owner. Check that
`moonbit_decref` is not called on already-released objects.

### heap-buffer-overflow

Writing past allocated buffer. Check `moonbit_make_bytes` and `moonbit_make_int32_array`
size calculations, especially byte-count vs element-count conversions.

### detected memory leaks

C allocations not freed. Verify every C allocation is wrapped with
`moonbit_make_external_object` so a finalizer is registered for cleanup.

### Fix Workflow

1. Read the ASan stack trace to find the first frame in your C stub code.
2. Identify which external object or buffer is involved.
3. Trace its lifetime: creation, `incref`/`decref` calls, finalizer registration.
4. Fix the root cause and re-run under ASan to confirm.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.