Skip to content

exec: fix uneven private pools and concurrent pool update - #1962

Open
warpdesign wants to merge 2 commits into
aros-development-team:masterfrom
warpdesign:fix-exec-private-pool-tails
Open

warpdesign wants to merge 2 commits into
aros-development-team:masterfrom
warpdesign:fix-exec-private-pool-tails

Conversation

@warpdesign

Copy link
Copy Markdown
Contributor

Support uneven private-pool capacities without losing tail bytes or violating allocation alignment. Serialize non-SMP Allocate/Deallocate updates to prevent shared-pool corruption. Fixes UFO’s allocator alerts, with native and 64-bit sanitizer regressions.

@warpdesign
warpdesign force-pushed the fix-exec-private-pool-tails branch from c49568f to d8677b2 Compare October 29, 2026 23:49
Allow terminal remainders in Allocate/Deallocate regions without writing a free-chunk header into an unusable tail. Preserve allocation alignment, restore the original free count on coalescing, and reject corrupt aligned pools and overflowing ranges using pointer-sized arithmetic.

Serialize plain-header Allocate/Deallocate transactions with the existing memory lock on non-SMP targets. Longer validation exposes task switches while UFO shares a private pool between its intro and filesystem processes; a concurrent allocation can otherwise overwrite a node retained by Deallocate. Keep existing SMP header locks and managed callback paths. Advance exec.library to 51.12.

Add Exec component normal/quick regressions for UFO-sized and uneven pools, all free orders, guards, fragmentation, shared-pool processes and Forbid nesting. A host harness compiles the production core with 64-bit types under ASan/UBSan at 16/32-byte alignment, including SMP concurrency and managed requests above 4 GiB. Native checks pass 508/508; host configurations pass 999/999, 1959/1959, 1011/1011 and 1971/1971. Original code rejects the uneven pools, and the four-process test reproduces unsynchronized list corruption before the locking change.
@warpdesign
warpdesign force-pushed the fix-exec-private-pool-tails branch from d8677b2 to 13263d3 Compare October 29, 2026 23:51
@Kalamatee

Copy link
Copy Markdown
Member

I've reviewed the diff and the PR-head files, but haven't built or run them. These are the points I'd like looked at.

Possible bugs

  1. rom/exec/memory.c:463: uneven private pools now raise a fatal alert.
    The usual setup is an empty MemHeader (mh_First = NULL, mh_Free = 0) followed by Deallocate(mh, base, size) for the whole region. When size isn't a multiple of MEMCHUNK_TOTAL, the new MM_FREE bounds check in validateHeader rounds the size up past mh_Upper and raises AN_MemCorrupt|AT_DeadEnd.
    For example, mh_Lower = base, mh_Upper = base + 0x1008, MEMCHUNK_TOTAL = 16, then Deallocate(mh, base, 0x1008): the rounded size is 0x1010, which is past mh_Upper, so it alerts.
    This is the case the PR says it supports. The empty-list branch of stdDealloc should clamp to mh_Upper and keep the remainder.

  2. rom/exec/memory.c:463: an unaligned mh_Lower is now fatal on free.
    The check requires (addr & ~mask) >= mh_Lower. A region whose mh_Lower isn't chunk-aligned, such as an AllocMem() result plus 4, now alerts when it is filled with Deallocate(mh, mh_Lower, size). Before this PR that free went ahead.

  3. rom/exec/memory_nommu.c:172 and :401: the allocators disagree on what's valid.
    stdAlloc and validateChunk now accept an uneven last chunk. nommu_AllocAbs() and nommu_AvailMem(MEMF_LARGEST) still raise AN_MemoryInsane|AT_DeadEnd ("Misaligned chunk") on any mc_Bytes that isn't aligned.
    AddMemList() and krnCreateMemHeader() with an uneven size can create such a chunk. AllocMem()/FreeMem() would work on it, but AvailMem(MEMF_LARGEST) or AllocAbs() would alert.

  4. rom/exec/memory.c:515: new validateChunk rule.
    A chunk below mh_Lower is now fatal on every allocation. Until now mh_Lower was only informational, so code that set it loosely, or above its first MemChunk, worked and now alerts.

  5. rom/exec/memory.c:798: the largest-chunk size is overstated.
    The last chunk's mc_Bytes includes the unusable tail. If the last free chunk is 24 bytes with 16-byte chunks, the scan reports 24 but Allocate(mh, 24) rounds up to 32 and returns NULL. That breaks the common pattern of allocating what AvailMem(MEMF_LARGEST) reports.

Locking

  1. rom/exec/allocate.c:145: Allocate()/Deallocate() now always call Forbid()/Permit().

    • The Permit() can reschedule partway through a call that never switched tasks before (it doesn't on AmigaOS either). A task calling it outside Forbid can now be preempted inside the call.
    • The lock only serialises tasks against each other. A region shared between an interrupt handler and a task can still be corrupted, because Forbid doesn't mask interrupts. The autodoc should say so.
  2. rom/exec/allocate.c:141: the comment says the lock may be the hosted target's memory semaphore.
    With __AROSEXEC_BROKENMEMLOCK__ defined, that would mean calling ObtainSemaphore() inside Allocate()/Deallocate(). That is unsafe in interrupts and in early boot: PrepareAROSSupportBase calls Allocate() at prepareexecbase.c:73 and :370 with no task. Either use Forbid()/Permit() directly or correct the comment.

  3. rom/exec/allocate.c:144: the lock is split across two layers.
    The same #if !defined(__AROSEXEC_SMP__) MEM_LOCK/MEM_UNLOCK block is pasted around the stdAlloc() and stdDealloc() calls, while the SMP lock is inside those functions. Any other direct user of stdAlloc()/stdDealloc() has to remember the uniprocessor lock itself.
    One header-lock helper inside stdAlloc()/stdDealloc() would cover both cases and remove the four #if blocks: the spinlock on SMP, Forbid otherwise.

Smaller issues

  1. rom/exec/deallocate.c:79: the old precheck is off by one and now redundant.
    The memoryBlock + byteSize > mh_Upper + 1 check stays in place next to the stricter validateHeader check, so a bad free gets different alerts depending on how far out it is:

    • A free ending one byte past mh_Upper passes the precheck, then gets the fatal AN_MemCorrupt.
    • A free further out gets only the recoverable AN_BadFreeAddr.

    Either drop the precheck or make it use the same rounded bound.

  2. developer/debug/test/exec/privatepool/privatepool.c: the test doesn't cover m68k.

    • On the target it reports only through bug(), so running it from a Shell prints nothing. Failures show up only as RC 20 or on serial.
    • The 64-bit host harness only runs 16- and 32-byte alignments. It never tests the 8-byte chunk layout used on m68k, which is where the UFO alerts were seen.

Points 1 and 3 matter most, because they can produce the same kind of fatal alert this PR is meant to fix.

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.

2 participants