<feed xmlns='http://www.w3.org/2005/Atom'>
<title>threadx.git/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c, branch master</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/threadx.git/atom/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c?h=master</id>
<link rel='self' href='http://cgit.235523.xyz/threadx.git/atom/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c?h=master'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/threadx.git/'/>
<updated>2026-09-28T15:31:41Z</updated>
<entry>
<title>Merge commit from fork</title>
<updated>2026-09-28T15:31:41Z</updated>
<author>
<name>Frédéric Desbiens</name>
<email>frederic.desbiens@eclipse-foundation.org</email>
</author>
<published>2026-09-28T15:31:41Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/threadx.git/commit/?id=a9bd72de21ab5554dba77f3c61f9756af7b6070c'/>
<id>urn:sha1:a9bd72de21ab5554dba77f3c61f9756af7b6070c</id>
<content type='text'>
* Fixed the Cortex-A7 module data check to validate whole ranges

The Cortex-A7 module port received a module instance, a start address and a
byte size from the common Module Manager, then discarded the instance and the
size and asked the MMU to translate the start address alone, for reading only.
A range was therefore accepted whenever its first byte happened to be readable
by the module, so privileged dispatch code could read or write past the end of
the module's mapping, or use read-only module code as a write destination.

The check now takes the size and an access intent. The module's own data region
is answered from the manager's records, which name it exactly and cost no
translations; everything else is answered by translating every page the range
touches with the requested unprivileged access. Empty ranges, ranges whose last
byte would wrap, and ranges that leave the recorded data region partway through
are all refused, and the walk is bounded by TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES
so one module request cannot impose unbounded work on the kernel. Translation is
only believed while the requesting module's own context is loaded.

The outside direction gets its own check rather than negating the inside one:
a range that reaches into the module only partway is inside neither answer, and
negating a whole-range check would have let it pass as a kernel object.

Other ports keep their single data check through backward-compatible fallbacks
in the common header; their preprocessed dispatch output is byte-identical.

Regression coverage runs on the host by standing a simulated page map behind the
one architecture primitive, and reaches 100% line, branch and call coverage of
the new logic. Twenty-four of its expectations fail against the previous
implementation.

Assisted-by: Claude Code (Opus 5)

* Drove the Cortex-A7 range check through a live MMU

The host test for this check stands a simulated page map behind the port's CP15
primitive, so it never executes an address translation. That leaves the part
most easily got wrong unexercised: an encoding naming the wrong operation, or a
PAR fault bit read the wrong way round, passes it without complaint.

This adds a bare-metal image that builds a short-descriptor translation table,
enables the MMU, and drives the real check through the real translations on a
real Cortex-A7 translation regime, plus a script that builds and runs it under
an emulator. Sections are mapped for unprivileged read/write, unprivileged read
only, and privileged only, with an unmapped section behind the read-only one so
that a range can be made to leave its mapping partway through.

That last case is the one the replaced check accepted, and the test asserts
both answers against the same live MMU: the current check rejects the range,
and translating only its first address accepts it.

It also confirms what the simulated map could only assume, that ATS1CUW denies
a write to a read-only mapping while ATS1CUR allows the read. The write intent
is the reason the port asks for two translations rather than one.

The script skips with a notice when the cross toolchain or the emulator is
absent, so a machine without them does not fail the build.

Assisted-by: Claude Code (Opus 5)</content>
</entry>
<entry>
<title>Merge commit from fork</title>
<updated>2026-09-28T13:23:49Z</updated>
<author>
<name>Frédéric Desbiens</name>
<email>frederic.desbiens@eclipse-foundation.org</email>
</author>
<published>2026-09-28T13:23:49Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/threadx.git/commit/?id=3276b0efd57d5e74f2edb1cec15add6e24085611'/>
<id>urn:sha1:3276b0efd57d5e74f2edb1cec15add6e24085611</id>
<content type='text'>
A module reaches a kernel object's delete service through the Module Manager's
dispatch layer, which asked one question about the pointer: does the object lie
outside the module's own code and data. That is the policy for using an object,
and using an object a module does not own is supported and intended --
txm_module_object_pointer_get_extended searches the system's created objects by
name and hands back objects the application created and objects other modules
created, so that a module can send to a shared queue, take a shared semaphore or
get a shared mutex. Destroying one of those is not sharing it. A module could
name any object it had a pointer to and have the privileged dispatcher delete it:
a host service lost its queue, waiters were resumed with TX_DELETED, an owned
mutex's priority inheritance was unwound, and an active timer stopped.

Nothing in the manager intended that. The deallocation the delete path performs
afterwards has always required the memory to belong to the calling module, so a
delete of anything else could only ever end in TX_PTR_ERROR -- the ownership
requirement was already there and was enforced one step too late, after the
kernel object had been irreversibly destroyed and its waiters woken. An error
returned at that point describes a cleanup failure and restores nothing.

The eight delete dispatchers now ask the question before the delete rather than
after it. A memory-protected module may delete an object only if the object is
one it allocated from the manager's object pool, at the exact address the manager
returned to it, for an allocation made for that type's control block size -- the
same conditions the create dispatchers already apply, since deletion is the
inverse of creation. Anything else returns TXM_MODULE_INVALID_MEMORY, which is
what every other parameter rejection in the dispatch table returns, so no new
value enters the module ABI and a module cannot use a delete request to tell
"not yours" apart from "not a usable address". The object stays created, nothing
waiting on it is resumed, and no created count moves, because the kernel was
never asked.

The ownership question is deliberately separate from whether an object is there
at all and of the expected type. It establishes nothing about liveness or type,
and it is composed with the checks that do rather than replacing them.

_txm_module_manager_object_deallocate reached the manager's private header by
subtracting from whatever address the caller supplied, and read it before
deciding whether it was a header. All eight delete dispatchers call that function
directly, so an object the module does not own arrived there as a matter of
course rather than exceptionally: for an object the application allocated
statically, or for any address outside the object pool, the words in front of it
were unrelated memory read in privileged mode -- and acted on, since an address
whose preceding words happened to name the calling module was unlinked from that
module's allocation list and handed to the byte pool.

It now finds the allocation by searching the module's own allocation list. The
caller's address is compared and never dereferenced, so an address that names
none of this module's allocations is refused without a privileged read of
anything in front of it, and the search establishes what the header read could
not: that the address is the exact start of an allocation rather than somewhere
inside one. The search is bounded by the count the manager keeps beside the list
and runs with interrupts disabled, so a damaged list cannot make it run on and it
cannot observe the list being changed under it. The fix is in the deallocation
itself rather than at a call site, so it covers all eight delete dispatchers, the
deallocation request a module can make directly, protected and unprotected
modules alike.

txm_module_manager_stop needs no exemption and was not given one. It deletes the
objects a module created by calling the internal _tx_*_delete services directly,
and identifies them with _txm_module_manager_created_object_check rather than
through a module request, so no caller-facing check stands in its way.

The 209 expectations in the new test drive all eight object types through
allocation, an ownership check by the owning module and by another, a check
against a larger and a smaller expected size, and a deallocation that succeeds.
They cover an object the application owns, deliberately preceded by a header
naming the requesting module so that reading it can be seen to have happened;
another module's object, checked and refused from both sides; every aligned
interior offset of an allocation and the addresses of its header, its end, the
pool's ends and one past the pool; a null pointer and an address with no room for
a header in front of it; a request from no module at all; memory that has changed
hands, where a stale pointer names an address that now belongs to another module;
the bounds on the search, against a count smaller than the list and against a
count larger than a list whose links are broken; releasing the head, the middle
and the last of a list of three; and an object pool that was never created. Every
call asserts that the interrupt lock came back balanced, and that the search ran
with interrupts disabled exactly one deep.

Restoring the previous deallocation fails four of them and then segmentation
faults, on the null-pointer case, where the header in front of address zero is
read; removing the ownership check fails 74.

Line and branch coverage of the two new functions and of the changed
_txm_module_manager_object_deallocate is 100%, with two branch outcomes excepted
that are test scaffolding rather than product code: the host shim's stand-ins for
TX_DISABLE and TX_RESTORE carry a maximum-depth test and an underflow guard, and
the real ports' primitives are inline assembly with no branch at all. All 99
tests pass in each of the five configurations the tree builds with GCC 14.

Nothing in the tree compiles the dispatch header, so the eight changed
dispatchers were cross-compiled by hand: the two changed C files and a
translation unit that includes the header build at -Werror with arm-none-eabi-gcc
13.2.1 for every GNU 32-bit module port -- Cortex-A7, M0+, M23, M3, M33, M4 and
M7 -- and produce the same -Wall -Wextra warning counts as before the change, 117
on Cortex-A7 and 116 on each of the others. Cortex-R4 and RXv2 have no GNU module
port, and the two AArch64 module ports have no toolchain available here; the new
arithmetic is a comparison of two addresses of the same type, so a wider
ALIGN_TYPE changes nothing about it.

MISRA: all if bodies are braced, the address comparison goes through ALIGN_TYPE,
no pointer the caller supplied is dereferenced, and no goto appears. No deviation
is required.

Assisted-by: Claude Code (Opus 5) &lt;noreply@anthropic.com&gt;</content>
</entry>
</feed>
