Files
hakmem/FALSE_POSITIVE_REPORT.md

146 lines
4.5 KiB
Markdown
Raw Normal View History

# False Positive Analysis Report: LIBC Pointer Misidentification
## Executive Summary
The `free(): invalid pointer` error is caused by **SS guessing logic** (lines 58-61 in `core/box/hak_free_api.inc.h`) which incorrectly identifies LIBC pointers as HAKMEM SuperSlab pointers, leading to wrong free path execution.
## Root Cause: SS Guessing Logic
### The Problematic Code
```c
// Lines 58-61 in core/box/hak_free_api.inc.h
for (int lg=21; lg>=20; lg--) {
uintptr_t mask=((uintptr_t)1<<lg)-1;
SuperSlab* guess=(SuperSlab*)((uintptr_t)ptr & ~mask);
if (guess && guess->magic==SUPERSLAB_MAGIC) {
int sidx=slab_index_for(guess,ptr);
int cap=ss_slabs_capacity(guess);
if (sidx>=0&&sidx<cap){
hak_free_route_log("ss_guess", ptr);
hak_tiny_free(ptr); // <-- WRONG! ptr might be from LIBC!
goto done;
}
}
}
```
### Why This Is Dangerous
1. **Reads Arbitrary Memory**: The code aligns any pointer to 2MB/1MB boundary and reads from that address
2. **No Ownership Validation**: Even if magic matches, there's no proof the pointer belongs to that SuperSlab
3. **False Positive Risk**: If aligned address happens to contain `SUPERSLAB_MAGIC`, LIBC pointers get misrouted
## False Positive Scenarios
### Scenario 1: Memory Reuse
- HAKMEM previously allocated a SuperSlab at address X
- SuperSlab was freed but memory wasn't cleared
- LIBC malloc reuses memory near X
- SS guessing finds old SUPERSLAB_MAGIC at aligned address
- LIBC pointer wrongly sent to `hak_tiny_free()`
### Scenario 2: Random Collision
- LIBC allocates memory
- 2MB-aligned base happens to contain the magic value
- Bounds check accidentally passes
- LIBC pointer wrongly freed through HAKMEM
### Scenario 3: Race Condition
- Thread A: Checks magic, it matches
- Thread B: Frees the SuperSlab
- Thread A: Proceeds to use freed SuperSlab -> CRASH
## Test Results
Our test program demonstrates:
```
LIBC pointer: 0x65329b0e42b0
2MB-aligned base: 0x65329b000000 (reading from here is UNSAFE!)
```
The SS guessing reads from `0x65329b000000` which is:
- 2,093,072 bytes away from the actual pointer
- Arbitrary memory that might contain anything
- Not validated as belonging to HAKMEM
## Other Lookup Functions
### ✅ `hak_super_lookup()` - SAFE
- Uses proper registry with O(1) lookup
- Validates magic BEFORE returning pointer
- Thread-safe with acquire/release semantics
- Returns NULL for LIBC pointers
### ✅ `hak_pool_mid_lookup()` - SAFE
- Uses page descriptor hash table
- Only returns true for registered Mid pages
- Returns 0 for LIBC pointers
### ✅ `hak_l25_lookup()` - SAFE
- Uses page descriptor lookup
- Only returns true for registered L2.5 pages
- Returns 0 for LIBC pointers
### ❌ SS Guessing (lines 58-61) - UNSAFE
- Reads from arbitrary aligned addresses
- No proper validation
- High false positive risk
## Recommended Fix
### Option 1: Remove SS Guessing (RECOMMENDED)
```c
// DELETE lines 58-61 entirely
// The registered lookup already handles valid SuperSlabs
```
### Option 2: Add Proper Validation
```c
// Only use registered SuperSlabs, no guessing
SuperSlab* ss = hak_super_lookup(ptr);
if (ss && ss->magic == SUPERSLAB_MAGIC) {
int sidx = slab_index_for(ss, ptr);
int cap = ss_slabs_capacity(ss);
if (sidx >= 0 && sidx < cap) {
hak_tiny_free(ptr);
goto done;
}
}
// No guessing loop!
```
### Option 3: Check Header First
```c
// Check header magic BEFORE any SS operations
AllocHeader* hdr = (AllocHeader*)((char*)ptr - HEADER_SIZE);
if (hdr->magic == HAKMEM_MAGIC) {
// Only then try SS operations
} else {
// Definitely LIBC, use __libc_free()
__libc_free(ptr);
goto done;
}
```
## Recommended Routing Order
The safest routing order for `hak_free_at()`:
1. **NULL check** - Return immediately if ptr is NULL
2. **Header check** - Check HAKMEM_MAGIC first (most reliable)
3. **Registered lookups only** - Use hak_super_lookup(), never guess
4. **Mid/L25 lookups** - These are safe with proper registry
5. **Fallback to LIBC** - If no match, assume LIBC and use __libc_free()
## Impact
- **Current**: LIBC pointers can be misidentified → crash
- **After fix**: Clean separation between HAKMEM and LIBC pointers
- **Performance**: Removing guessing loop actually improves performance
## Action Items
1. **IMMEDIATE**: Remove lines 58-61 (SS guessing loop)
2. **TEST**: Verify LIBC allocations work correctly
3. **AUDIT**: Check for similar guessing logic elsewhere
4. **DOCUMENT**: Add warnings about reading arbitrary aligned memory