diff --git a/common_modules/module_manager/inc/txm_module_manager_util.h b/common_modules/module_manager/inc/txm_module_manager_util.h
index 92bb55dd..7feb0f3e 100644
--- a/common_modules/module_manager/inc/txm_module_manager_util.h
+++ b/common_modules/module_manager/inc/txm_module_manager_util.h
@@ -45,8 +45,32 @@
/* Define check macros for modules. */
+/* A port supplies TXM_MODULE_MANAGER_CHECK_INSIDE_DATA to decide whether a caller-supplied
+ range lies inside the module's writable data or shared memory. Ports whose check can tell
+ a read-only region from a writable one also supply the two intent-specific variants below.
+ Ports that cannot make the distinction inherit their single check for both intents, so
+ their behaviour is unchanged. */
+
+#ifndef TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size) \
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size)
+#endif
+
+#ifndef TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size) \
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size)
+#endif
+
+/* A range is outside the module's data only when no part of it is reachable by the module.
+ The read intent is used here because it describes the widest set of reachable addresses.
+ A port whose inside check cannot prove the whole range at once must supply its own
+ definition, because negating a partial-range check would report a partly reachable range
+ as being outside the module. */
+
+#ifndef TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA
#define TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA(module_instance, obj_ptr, obj_size) \
- (!(TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size)))
+ (!(TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size)))
+#endif
#define TXM_MODULE_MANAGER_CHECK_INSIDE_CODE(module_instance, obj_ptr, obj_size) \
(((obj_ptr) < ((obj_ptr) + (obj_size))) && \
@@ -65,12 +89,14 @@
/* Define macros for module. */
+/* The module reads from these ranges, so a read-only region is acceptable. */
#define TXM_MODULE_MANAGER_ENSURE_INSIDE_MODULE(module_instance, obj_ptr, obj_size) \
- (TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) || \
+ (TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size) || \
TXM_MODULE_MANAGER_CHECK_INSIDE_CODE(module_instance, obj_ptr, obj_size))
+/* The kernel writes to these ranges, so the module must be able to write them too. */
#define TXM_MODULE_MANAGER_ENSURE_INSIDE_MODULE_DATA(module_instance, obj_ptr, obj_size) \
- TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size)
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size)
#define TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, obj_ptr, obj_size) \
(TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA(module_instance, obj_ptr, obj_size) && \
diff --git a/ports_module/cortex_a7/ac5/example_build/build_threadx.bat b/ports_module/cortex_a7/ac5/example_build/build_threadx.bat
index ea349e37..764992f0 100644
--- a/ports_module/cortex_a7/ac5/example_build/build_threadx.bat
+++ b/ports_module/cortex_a7/ac5/example_build/build_threadx.bat
@@ -223,6 +223,7 @@ armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../com
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_properties_get.c
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c
+armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../module_manager/src/txm_module_manager_inside_data_check.c
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../module_manager/src/txm_module_manager_mm_register_setup.c
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_start.c
armcc -g -O0 --cpu=cortex-a7.no_neon --fpu=softvfp -c -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_stop.c
@@ -278,7 +279,7 @@ armar -r tx.a txm_module_manager_in_place_load.o txm_module_manager_initialize.o
armar -r tx.a txm_module_manager_kernel_dispatch.o txm_module_manager_maximum_module_priority_set.o txm_module_manager_memory_fault_handler.o
armar -r tx.a txm_module_manager_memory_fault_notify.o txm_module_manager_memory_load.o txm_module_manager_object_pointer_get.o
armar -r tx.a txm_module_manager_object_pool_create.o txm_module_manager_queue_notify_trampoline.o txm_module_manager_semaphore_notify_trampoline.o
-armar -r tx.a txm_module_manager_mm_register_setup.o txm_module_manager_start.o txm_module_manager_stop.o
+armar -r tx.a txm_module_manager_inside_data_check.o txm_module_manager_mm_register_setup.o txm_module_manager_start.o txm_module_manager_stop.o
armar -r tx.a txm_module_manager_thread_create.o txm_module_manager_thread_notify_trampoline.o txm_module_manager_thread_reset.o
armar -r tx.a txm_module_manager_timer_notify_trampoline.o txm_module_manager_unload.o txm_module_manager_thread_stack_build.o
armar -r tx.a txm_module_manager_user_mode_entry.o
diff --git a/ports_module/cortex_a7/ac5/inc/txm_module_port.h b/ports_module/cortex_a7/ac5/inc/txm_module_port.h
index 1102a377..126be1e8 100644
--- a/ports_module/cortex_a7/ac5/inc/txm_module_port.h
+++ b/ports_module/cortex_a7/ac5/inc/txm_module_port.h
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -240,6 +242,22 @@ The following extensions must also be defined in tx_port.h:
#define TXM_ADDRESS_TRANSLATION_FAULT_BIT 1
+/* Mask selecting the ASID field of CONTEXTIDR in the short-descriptor format. */
+#define TXM_CONTEXTIDR_ASID_MASK 0x000000FF
+
+/* Access intents understood by the module data range check. */
+#define TXM_MODULE_MANAGER_ACCESS_READ 0
+#define TXM_MODULE_MANAGER_ACCESS_WRITE 1
+
+/* Define the largest number of MMU pages a single caller-supplied range may span before
+ the data range check rejects it. This bounds the worst-case, module-controlled cost of
+ validating one kernel request, which keeps the check deterministic. The default permits
+ a 4 MB range. Applications that hand larger shared-memory ranges to kernel services can
+ define a larger value. */
+#ifndef TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES
+#define TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES 1024
+#endif
+
#define TXM_ASID_RESERVED 0xFFFFFFFF
#define TXM_MODULE_ASID_ERROR 0xF6
@@ -375,9 +393,26 @@ typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT
/* Define the macros to perform port-specific checks when passing pointers to the kernel. */
-/* Define macro to make sure object is inside the module's data or shared memory. */
+/* Define macros to make sure a whole object is inside the module's data or shared memory.
+ The MMU is the authority for shared and external memory, so the check translates every
+ page the range touches with the requested unprivileged access. Passing the size and the
+ access intent through is what keeps the check honest: translating only the first address,
+ and only for reading, would accept a range that leaves the module's mapping partway
+ through, or a read-only range used as a kernel write destination. */
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_READ)
+
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_WRITE)
+
+/* The unqualified check keeps its historical name and takes the stricter write intent. */
#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \
- _txm_module_manager_inside_data_check((ULONG) obj_ptr)
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size)
+
+/* Negating the inside check would report a range that only partly reaches into the module
+ as being outside it, so the outside direction gets its own check. */
+#define TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size))
/* Define some internal prototypes to this module port. */
@@ -397,7 +432,10 @@ UINT _txm_module_manager_mm_initialize(VOID);
VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_level2_page_clear(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_module_manager_remove_asid(TXM_MODULE_INSTANCE *module_instance); \
-UINT _txm_module_manager_inside_data_check(ULONG pointer);
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request); \
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size); \
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request); \
+ULONG _txm_module_manager_current_asid_get(VOID);
#define TXM_MODULE_MANAGER_VERSION_ID \
CHAR _txm_module_manager_version_id[] = \
diff --git a/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_inside_data_check.c b/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_inside_data_check.c
new file mode 100644
index 00000000..e7ebf262
--- /dev/null
+++ b/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_inside_data_check.c
@@ -0,0 +1,538 @@
+/***************************************************************************
+ * Copyright (c) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Module Manager */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#include "tx_api.h"
+#include "txm_module.h"
+
+
+/* Define the ways a caller-supplied range can sit relative to the module's data region. */
+
+#define TXM_MODULE_DATA_WINDOW_OUTSIDE 0 /* No byte touches the data mapping. */
+#define TXM_MODULE_DATA_WINDOW_CONTAINED 1 /* Every byte is recorded module data. */
+#define TXM_MODULE_DATA_WINDOW_PARTIAL 2 /* Some, but not all, bytes are. */
+
+
+/* Define the page mask derived from the smallest MMU page this port maps. */
+
+#define TXM_MODULE_PAGE_OFFSET_MASK ((ALIGN_TYPE) (TXM_MODULE_MEMORY_ALIGNMENT - 1))
+#define TXM_MODULE_PAGE_BASE_MASK (~TXM_MODULE_PAGE_OFFSET_MASK)
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_range_end_get Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function derives the inclusive address of the last byte of a */
+/* caller-supplied range. An empty range is rejected, matching the */
+/* portable check macros, and so is a range whose last byte is not */
+/* representable, so that no caller ever works from a wrapped address. */
+/* */
+/* INPUT */
+/* */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* last_byte Destination for the last address */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the range is non-empty and does not wrap */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_range_end_get(ALIGN_TYPE obj_ptr, ULONG obj_size, ALIGN_TYPE *last_byte)
+{
+
+ALIGN_TYPE span;
+ALIGN_TYPE highest;
+UINT status;
+
+
+ /* Assume the range cannot be used until proven otherwise. */
+ status = TX_FALSE;
+
+ /* An empty range has no last byte and is rejected, just as the portable macros do. */
+ if (obj_size != ((ULONG) 0))
+ {
+
+ /* Work with the inclusive span so that a range reaching the top of the address
+ space stays representable. */
+ span = ((ALIGN_TYPE) obj_size) - ((ALIGN_TYPE) 1);
+
+ /* Determine the highest first byte this span can start from. */
+ highest = (~((ALIGN_TYPE) 0)) - span;
+
+ /* Determine if the range would run past the end of the address space. */
+ if (obj_ptr <= highest)
+ {
+
+ /* Record the last byte of the range. */
+ *last_byte = obj_ptr + span;
+
+ /* The range is usable. */
+ status = TX_TRUE;
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_data_window_classify Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function reports how a range sits relative to the module's own */
+/* data region. The module manager records that region exactly, while */
+/* the MMU can only map it a page at a time, so the mapping may reach */
+/* past the recorded end. The recorded region therefore stays the */
+/* authority for the whole mapped window: a range that leaves the */
+/* recorded region but still lands in the window is reported as */
+/* partial, and its caller rejects it rather than asking the MMU, */
+/* which would accept the rounding slack. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* */
+/* OUTPUT */
+/* */
+/* One of the TXM_MODULE_DATA_WINDOW_* classifications */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_data_window_classify(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE first_byte, ALIGN_TYPE last_byte)
+{
+
+ALIGN_TYPE data_start;
+ALIGN_TYPE data_end;
+ALIGN_TYPE window_start;
+ALIGN_TYPE window_end;
+UINT window;
+
+
+ /* Pick up the recorded data region. The end address is inclusive. */
+ data_start = (ALIGN_TYPE) module_instance -> txm_module_instance_data_start;
+ data_end = (ALIGN_TYPE) module_instance -> txm_module_instance_data_end;
+
+ /* Determine if the module has a usable data region at all. */
+ if (data_start > data_end)
+ {
+
+ /* An unset or inverted region owns nothing. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ else
+ {
+
+ /* Widen the region to the pages the MMU actually maps for it. */
+ window_start = data_start & TXM_MODULE_PAGE_BASE_MASK;
+ window_end = data_end | TXM_MODULE_PAGE_OFFSET_MASK;
+
+ /* Determine if every byte is recorded module data. */
+ if ((first_byte >= data_start) && (last_byte <= data_end))
+ {
+
+ /* The whole range belongs to the module. */
+ window = TXM_MODULE_DATA_WINDOW_CONTAINED;
+ }
+ else if ((first_byte <= window_end) && (last_byte >= window_start))
+ {
+
+ /* The range overlaps the mapped window without being contained by the
+ recorded region, so it leaves the module's data partway through. */
+ window = TXM_MODULE_DATA_WINDOW_PARTIAL;
+ }
+ else
+ {
+
+ /* The range lies elsewhere, so only the MMU can speak for it. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ }
+
+ /* Return the classification. */
+ return(window);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_page_walk Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function translates every page a range touches and reports */
+/* whether all of them answered as expected. The walk stops at the */
+/* first page that disagrees, and a range spanning more pages than */
+/* the configured maximum is refused so that one kernel request can */
+/* never impose unbounded work. */
+/* */
+/* INPUT */
+/* */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* write_request Access intent to translate for */
+/* expected Result every page must return */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if every page answered as expected */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_address_probe */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_page_walk(ALIGN_TYPE first_byte, ALIGN_TYPE last_byte, UINT write_request, UINT expected)
+{
+
+ALIGN_TYPE first_page;
+ALIGN_TYPE last_page;
+ALIGN_TYPE page;
+ULONG pages;
+ULONG index;
+UINT matched;
+
+
+ /* Reduce the range to the pages it touches. */
+ first_page = first_byte & TXM_MODULE_PAGE_BASE_MASK;
+ last_page = last_byte & TXM_MODULE_PAGE_BASE_MASK;
+
+ /* Count them. Both addresses are page-aligned, so the division is exact. */
+ pages = ((ULONG) ((last_page - first_page) / ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT))) + ((ULONG) 1);
+
+ /* Determine if the range is longer than this port is willing to translate. */
+ if (pages > ((ULONG) TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES))
+ {
+
+ /* Refuse rather than spend unbounded time in a kernel request. Refusing is safe
+ for both callers: neither can then claim the range is inside or outside. */
+ matched = TX_FALSE;
+ }
+ else
+ {
+
+ /* Assume every page agrees until one does not. */
+ matched = TX_TRUE;
+ index = (ULONG) 0;
+
+ /* Translate each page in turn, stopping as soon as the answer is settled. The
+ address is rebuilt from the index so that the walk cannot wrap past the top
+ page of the address space. */
+ while ((index < pages) && (matched == TX_TRUE))
+ {
+
+ /* Build the address of this page. */
+ page = first_page + (((ALIGN_TYPE) index) * ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT));
+
+ /* Ask the MMU whether the module may reach this page with the requested access. */
+ if (_txm_module_manager_address_probe((ULONG) page, write_request) != expected)
+ {
+
+ /* This page disagrees, so the range as a whole does. */
+ matched = TX_FALSE;
+ }
+
+ /* Move to the next page. */
+ index++;
+ }
+ }
+
+ /* Return whether the whole range answered as expected. */
+ return(matched);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_inside_data_check Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether every byte of a caller-supplied */
+/* range lies inside the module's data or shared memory and is */
+/* reachable by the module with the requested access. The module's own */
+/* data region is answered from the manager's own records, which are */
+/* exact. Anything else is answered by translating every page the */
+/* range touches, because this port keeps no record of the shared and */
+/* external regions the application maps into the module. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the whole range is reachable with that access */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not usable until every condition is met. */
+ status = TX_FALSE;
+
+ /* A range can only belong to a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_CONTAINED))
+ {
+
+ /* The module's data region is mapped for unprivileged reading and
+ writing, so both intents are satisfied without translating. */
+ status = TX_TRUE;
+ }
+ else if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Shared or external memory: every page must grant the access. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, write_request, TX_TRUE);
+ }
+ else
+ {
+
+ /* The range leaves the module's data partway through. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_outside_data_check Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether no byte of a caller-supplied range */
+/* is reachable by the module. It is deliberately not the negation of */
+/* the inside check: a range that reaches into the module's memory */
+/* only partway is inside neither answer, and reporting it as outside */
+/* would let it pass as a kernel object. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if no byte of the range is reachable by the module */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not provably outside the module. */
+ status = TX_FALSE;
+
+ /* A range can only be placed against a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Clear of the module's data region, so the remaining question is
+ whether any page of it is shared or external memory. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, TXM_MODULE_MANAGER_ACCESS_READ, TX_FALSE);
+ }
+ else
+ {
+
+ /* The range touches the module's data region. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
diff --git a/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_mm_register_setup.c
index bdaa548d..a86cfbe4 100644
--- a/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_mm_register_setup.c
+++ b/ports_module/cortex_a7/ac5/module_manager/src/txm_module_manager_mm_register_setup.c
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -35,24 +37,34 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* FUNCTION RELEASE */
/* */
-/* _txm_module_manager_inside_data_check Cortex-A7/MMU/AC5 */
-/* 6.1 */
+/* _txm_module_manager_address_probe Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
+/* Eclipse ThreadX contributors */
/* */
/* DESCRIPTION */
/* */
-/* This function determines if pointer is within the module's data or */
-/* shared memory. */
+/* This function asks the MMU whether unprivileged code running in the */
+/* currently loaded translation context may reach one address with the */
+/* requested access. Reading and writing are asked separately, because */
+/* a module's code and its read-only shared memory translate for */
+/* reading while a kernel service writing to them would fault or, */
+/* worse, succeed from privileged mode. */
+/* */
+/* This answers for one address only. Callers that hold a range must */
+/* ask about every page the range touches. */
/* */
/* INPUT */
/* */
-/* pointer Data pointer */
+/* address Address to translate */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
/* */
/* OUTPUT */
/* */
-/* Completion Status */
+/* TX_TRUE if the translation granted the requested access */
/* */
/* CALLS */
/* */
@@ -60,17 +72,40 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* CALLED BY */
/* */
-/* TXM_MODULE_MANAGER_DATA_POINTER_CHECK */
+/* _txm_module_manager_page_walk */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
+/* 08-24-2026 Eclipse ThreadX Added the write intent and */
+/* contributors moved range handling to */
+/* the caller, resulting in */
+/* version 6.5.0 */
/* */
/**************************************************************************/
-UINT _txm_module_manager_inside_data_check(ULONG pointer)
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request)
{
-ULONG translation;
+ULONG translation;
- /* ATS1CUR operation on address supplied in pointer, Stage 1 unprivileged read. */
- __asm("MCR p15, 0, pointer, c7, c8, 2");
- __asm("ISB"); /* Ensure completion of the MCR write to CP15. */
+
+ /* Determine which unprivileged access the caller needs. */
+ if (write_request == ((UINT) TXM_MODULE_MANAGER_ACCESS_WRITE))
+ {
+
+ /* ATS1CUW operation on address supplied in address, Stage 1 unprivileged write. */
+ __asm("MCR p15, 0, address, c7, c8, 3");
+ }
+ else
+ {
+
+ /* ATS1CUR operation on address supplied in address, Stage 1 unprivileged read. */
+ __asm("MCR p15, 0, address, c7, c8, 2");
+ }
+
+ __asm("ISB"); /* Ensure completion of the MCR write to CP15. */
__asm("MRC p15, 0, translation, c7, c4, 0"); /* Read result from 32-bit PAR into translation. */
if (translation & TXM_ADDRESS_TRANSLATION_FAULT_BIT)
@@ -82,6 +117,62 @@ ULONG translation;
}
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_current_asid_get Cortex-A7/MMU/AC5 */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function returns the ASID of the translation context that is */
+/* currently loaded. Address translation always answers for that */
+/* context, so a caller validating a pointer on behalf of a module */
+/* must confirm that the loaded context is that module's before it */
+/* believes the answer. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* ASID of the currently loaded translation context */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+ULONG _txm_module_manager_current_asid_get(VOID)
+{
+
+ULONG contextidr;
+
+
+ /* Read CONTEXTIDR, whose low byte holds the ASID in the short-descriptor format. */
+ __asm("MRC p15, 0, contextidr, c13, c0, 1");
+
+ return(contextidr & TXM_CONTEXTIDR_ASID_MASK);
+}
+
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
diff --git a/ports_module/cortex_a7/gnu/example_build/build_threadx.bat b/ports_module/cortex_a7/gnu/example_build/build_threadx.bat
index 0e094b38..3e687587 100644
--- a/ports_module/cortex_a7/gnu/example_build/build_threadx.bat
+++ b/ports_module/cortex_a7/gnu/example_build/build_threadx.bat
@@ -223,6 +223,7 @@ arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -m
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_properties_get.c
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_queue_notify_trampoline.c
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_semaphore_notify_trampoline.c
+arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../module_manager/src/txm_module_manager_inside_data_check.c
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../module_manager/src/txm_module_manager_mm_register_setup.c
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_start.c
arm-none-eabi-gcc -c -g -O0 -mcpu=cortex-a7 -mfloat-abi=hard -mfpu=neon-vfpv4 -marm -mthumb-interwork -DTX_ENABLE_VFP_SUPPORT -I../inc -I../../../../common/inc -I../../../../common_modules/inc -I../../../../common_modules/module_manager/inc ../../../../common_modules/module_manager/src/txm_module_manager_stop.c
@@ -278,7 +279,7 @@ arm-none-eabi-ar -r tx.a txm_module_manager_in_place_load.o txm_module_manager_i
arm-none-eabi-ar -r tx.a txm_module_manager_kernel_dispatch.o txm_module_manager_maximum_module_priority_set.o txm_module_manager_memory_fault_handler.o
arm-none-eabi-ar -r tx.a txm_module_manager_memory_fault_notify.o txm_module_manager_memory_load.o txm_module_manager_object_pointer_get.o
arm-none-eabi-ar -r tx.a txm_module_manager_object_pool_create.o txm_module_manager_queue_notify_trampoline.o txm_module_manager_semaphore_notify_trampoline.o
-arm-none-eabi-ar -r tx.a txm_module_manager_mm_register_setup.o txm_module_manager_start.o txm_module_manager_stop.o
+arm-none-eabi-ar -r tx.a txm_module_manager_inside_data_check.o txm_module_manager_mm_register_setup.o txm_module_manager_start.o txm_module_manager_stop.o
arm-none-eabi-ar -r tx.a txm_module_manager_thread_create.o txm_module_manager_thread_notify_trampoline.o txm_module_manager_thread_reset.o
arm-none-eabi-ar -r tx.a txm_module_manager_timer_notify_trampoline.o txm_module_manager_unload.o txm_module_manager_thread_stack_build.o
arm-none-eabi-ar -r tx.a txm_module_manager_user_mode_entry.o
diff --git a/ports_module/cortex_a7/gnu/inc/txm_module_port.h b/ports_module/cortex_a7/gnu/inc/txm_module_port.h
index 9cd5a280..11947992 100644
--- a/ports_module/cortex_a7/gnu/inc/txm_module_port.h
+++ b/ports_module/cortex_a7/gnu/inc/txm_module_port.h
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -240,6 +242,22 @@ The following extensions must also be defined in tx_port.h:
#define TXM_ADDRESS_TRANSLATION_FAULT_BIT 1
+/* Mask selecting the ASID field of CONTEXTIDR in the short-descriptor format. */
+#define TXM_CONTEXTIDR_ASID_MASK 0x000000FF
+
+/* Access intents understood by the module data range check. */
+#define TXM_MODULE_MANAGER_ACCESS_READ 0
+#define TXM_MODULE_MANAGER_ACCESS_WRITE 1
+
+/* Define the largest number of MMU pages a single caller-supplied range may span before
+ the data range check rejects it. This bounds the worst-case, module-controlled cost of
+ validating one kernel request, which keeps the check deterministic. The default permits
+ a 4 MB range. Applications that hand larger shared-memory ranges to kernel services can
+ define a larger value. */
+#ifndef TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES
+#define TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES 1024
+#endif
+
#define TXM_ASID_RESERVED 0xFFFFFFFF
#define TXM_MODULE_ASID_ERROR 0xF6
@@ -375,9 +393,26 @@ typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT
/* Define the macros to perform port-specific checks when passing pointers to the kernel. */
-/* Define macro to make sure object is inside the module's data or shared memory. */
+/* Define macros to make sure a whole object is inside the module's data or shared memory.
+ The MMU is the authority for shared and external memory, so the check translates every
+ page the range touches with the requested unprivileged access. Passing the size and the
+ access intent through is what keeps the check honest: translating only the first address,
+ and only for reading, would accept a range that leaves the module's mapping partway
+ through, or a read-only range used as a kernel write destination. */
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_READ)
+
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_WRITE)
+
+/* The unqualified check keeps its historical name and takes the stricter write intent. */
#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \
- _txm_module_manager_inside_data_check((ULONG) obj_ptr)
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size)
+
+/* Negating the inside check would report a range that only partly reaches into the module
+ as being outside it, so the outside direction gets its own check. */
+#define TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size))
/* Define some internal prototypes to this module port. */
@@ -397,7 +432,10 @@ UINT _txm_module_manager_mm_initialize(VOID);
VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_level2_page_clear(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_module_manager_remove_asid(TXM_MODULE_INSTANCE *module_instance); \
-UINT _txm_module_manager_inside_data_check(ULONG pointer);
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request); \
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size); \
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request); \
+ULONG _txm_module_manager_current_asid_get(VOID);
#define TXM_MODULE_MANAGER_VERSION_ID \
CHAR _txm_module_manager_version_id[] = \
diff --git a/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_inside_data_check.c b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_inside_data_check.c
new file mode 100644
index 00000000..942fd9f6
--- /dev/null
+++ b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_inside_data_check.c
@@ -0,0 +1,538 @@
+/***************************************************************************
+ * Copyright (c) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Module Manager */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#include "tx_api.h"
+#include "txm_module.h"
+
+
+/* Define the ways a caller-supplied range can sit relative to the module's data region. */
+
+#define TXM_MODULE_DATA_WINDOW_OUTSIDE 0 /* No byte touches the data mapping. */
+#define TXM_MODULE_DATA_WINDOW_CONTAINED 1 /* Every byte is recorded module data. */
+#define TXM_MODULE_DATA_WINDOW_PARTIAL 2 /* Some, but not all, bytes are. */
+
+
+/* Define the page mask derived from the smallest MMU page this port maps. */
+
+#define TXM_MODULE_PAGE_OFFSET_MASK ((ALIGN_TYPE) (TXM_MODULE_MEMORY_ALIGNMENT - 1))
+#define TXM_MODULE_PAGE_BASE_MASK (~TXM_MODULE_PAGE_OFFSET_MASK)
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_range_end_get Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function derives the inclusive address of the last byte of a */
+/* caller-supplied range. An empty range is rejected, matching the */
+/* portable check macros, and so is a range whose last byte is not */
+/* representable, so that no caller ever works from a wrapped address. */
+/* */
+/* INPUT */
+/* */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* last_byte Destination for the last address */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the range is non-empty and does not wrap */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_range_end_get(ALIGN_TYPE obj_ptr, ULONG obj_size, ALIGN_TYPE *last_byte)
+{
+
+ALIGN_TYPE span;
+ALIGN_TYPE highest;
+UINT status;
+
+
+ /* Assume the range cannot be used until proven otherwise. */
+ status = TX_FALSE;
+
+ /* An empty range has no last byte and is rejected, just as the portable macros do. */
+ if (obj_size != ((ULONG) 0))
+ {
+
+ /* Work with the inclusive span so that a range reaching the top of the address
+ space stays representable. */
+ span = ((ALIGN_TYPE) obj_size) - ((ALIGN_TYPE) 1);
+
+ /* Determine the highest first byte this span can start from. */
+ highest = (~((ALIGN_TYPE) 0)) - span;
+
+ /* Determine if the range would run past the end of the address space. */
+ if (obj_ptr <= highest)
+ {
+
+ /* Record the last byte of the range. */
+ *last_byte = obj_ptr + span;
+
+ /* The range is usable. */
+ status = TX_TRUE;
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_data_window_classify Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function reports how a range sits relative to the module's own */
+/* data region. The module manager records that region exactly, while */
+/* the MMU can only map it a page at a time, so the mapping may reach */
+/* past the recorded end. The recorded region therefore stays the */
+/* authority for the whole mapped window: a range that leaves the */
+/* recorded region but still lands in the window is reported as */
+/* partial, and its caller rejects it rather than asking the MMU, */
+/* which would accept the rounding slack. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* */
+/* OUTPUT */
+/* */
+/* One of the TXM_MODULE_DATA_WINDOW_* classifications */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_data_window_classify(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE first_byte, ALIGN_TYPE last_byte)
+{
+
+ALIGN_TYPE data_start;
+ALIGN_TYPE data_end;
+ALIGN_TYPE window_start;
+ALIGN_TYPE window_end;
+UINT window;
+
+
+ /* Pick up the recorded data region. The end address is inclusive. */
+ data_start = (ALIGN_TYPE) module_instance -> txm_module_instance_data_start;
+ data_end = (ALIGN_TYPE) module_instance -> txm_module_instance_data_end;
+
+ /* Determine if the module has a usable data region at all. */
+ if (data_start > data_end)
+ {
+
+ /* An unset or inverted region owns nothing. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ else
+ {
+
+ /* Widen the region to the pages the MMU actually maps for it. */
+ window_start = data_start & TXM_MODULE_PAGE_BASE_MASK;
+ window_end = data_end | TXM_MODULE_PAGE_OFFSET_MASK;
+
+ /* Determine if every byte is recorded module data. */
+ if ((first_byte >= data_start) && (last_byte <= data_end))
+ {
+
+ /* The whole range belongs to the module. */
+ window = TXM_MODULE_DATA_WINDOW_CONTAINED;
+ }
+ else if ((first_byte <= window_end) && (last_byte >= window_start))
+ {
+
+ /* The range overlaps the mapped window without being contained by the
+ recorded region, so it leaves the module's data partway through. */
+ window = TXM_MODULE_DATA_WINDOW_PARTIAL;
+ }
+ else
+ {
+
+ /* The range lies elsewhere, so only the MMU can speak for it. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ }
+
+ /* Return the classification. */
+ return(window);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_page_walk Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function translates every page a range touches and reports */
+/* whether all of them answered as expected. The walk stops at the */
+/* first page that disagrees, and a range spanning more pages than */
+/* the configured maximum is refused so that one kernel request can */
+/* never impose unbounded work. */
+/* */
+/* INPUT */
+/* */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* write_request Access intent to translate for */
+/* expected Result every page must return */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if every page answered as expected */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_address_probe */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_page_walk(ALIGN_TYPE first_byte, ALIGN_TYPE last_byte, UINT write_request, UINT expected)
+{
+
+ALIGN_TYPE first_page;
+ALIGN_TYPE last_page;
+ALIGN_TYPE page;
+ULONG pages;
+ULONG index;
+UINT matched;
+
+
+ /* Reduce the range to the pages it touches. */
+ first_page = first_byte & TXM_MODULE_PAGE_BASE_MASK;
+ last_page = last_byte & TXM_MODULE_PAGE_BASE_MASK;
+
+ /* Count them. Both addresses are page-aligned, so the division is exact. */
+ pages = ((ULONG) ((last_page - first_page) / ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT))) + ((ULONG) 1);
+
+ /* Determine if the range is longer than this port is willing to translate. */
+ if (pages > ((ULONG) TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES))
+ {
+
+ /* Refuse rather than spend unbounded time in a kernel request. Refusing is safe
+ for both callers: neither can then claim the range is inside or outside. */
+ matched = TX_FALSE;
+ }
+ else
+ {
+
+ /* Assume every page agrees until one does not. */
+ matched = TX_TRUE;
+ index = (ULONG) 0;
+
+ /* Translate each page in turn, stopping as soon as the answer is settled. The
+ address is rebuilt from the index so that the walk cannot wrap past the top
+ page of the address space. */
+ while ((index < pages) && (matched == TX_TRUE))
+ {
+
+ /* Build the address of this page. */
+ page = first_page + (((ALIGN_TYPE) index) * ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT));
+
+ /* Ask the MMU whether the module may reach this page with the requested access. */
+ if (_txm_module_manager_address_probe((ULONG) page, write_request) != expected)
+ {
+
+ /* This page disagrees, so the range as a whole does. */
+ matched = TX_FALSE;
+ }
+
+ /* Move to the next page. */
+ index++;
+ }
+ }
+
+ /* Return whether the whole range answered as expected. */
+ return(matched);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_inside_data_check Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether every byte of a caller-supplied */
+/* range lies inside the module's data or shared memory and is */
+/* reachable by the module with the requested access. The module's own */
+/* data region is answered from the manager's own records, which are */
+/* exact. Anything else is answered by translating every page the */
+/* range touches, because this port keeps no record of the shared and */
+/* external regions the application maps into the module. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the whole range is reachable with that access */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not usable until every condition is met. */
+ status = TX_FALSE;
+
+ /* A range can only belong to a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_CONTAINED))
+ {
+
+ /* The module's data region is mapped for unprivileged reading and
+ writing, so both intents are satisfied without translating. */
+ status = TX_TRUE;
+ }
+ else if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Shared or external memory: every page must grant the access. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, write_request, TX_TRUE);
+ }
+ else
+ {
+
+ /* The range leaves the module's data partway through. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_outside_data_check Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether no byte of a caller-supplied range */
+/* is reachable by the module. It is deliberately not the negation of */
+/* the inside check: a range that reaches into the module's memory */
+/* only partway is inside neither answer, and reporting it as outside */
+/* would let it pass as a kernel object. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if no byte of the range is reachable by the module */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not provably outside the module. */
+ status = TX_FALSE;
+
+ /* A range can only be placed against a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Clear of the module's data region, so the remaining question is
+ whether any page of it is shared or external memory. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, TXM_MODULE_MANAGER_ACCESS_READ, TX_FALSE);
+ }
+ else
+ {
+
+ /* The range touches the module's data region. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
diff --git a/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_mm_register_setup.c
index 60202970..b2161fde 100644
--- a/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_mm_register_setup.c
+++ b/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_mm_register_setup.c
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -35,24 +37,34 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* FUNCTION RELEASE */
/* */
-/* _txm_module_manager_inside_data_check Cortex-A7/MMU/GNU */
-/* 6.2.1 */
+/* _txm_module_manager_address_probe Cortex-A7/MMU/GNU */
+/* 6.5.0 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
+/* Eclipse ThreadX contributors */
/* */
/* DESCRIPTION */
/* */
-/* This function determines if pointer is within the module's data or */
-/* shared memory. */
+/* This function asks the MMU whether unprivileged code running in the */
+/* currently loaded translation context may reach one address with the */
+/* requested access. Reading and writing are asked separately, because */
+/* a module's code and its read-only shared memory translate for */
+/* reading while a kernel service writing to them would fault or, */
+/* worse, succeed from privileged mode. */
+/* */
+/* This answers for one address only. Callers that hold a range must */
+/* ask about every page the range touches. */
/* */
/* INPUT */
/* */
-/* pointer Data pointer */
+/* address Address to translate */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
/* */
/* OUTPUT */
/* */
-/* Completion Status */
+/* TX_TRUE if the translation granted the requested access */
/* */
/* CALLS */
/* */
@@ -60,18 +72,41 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* CALLED BY */
/* */
-/* TXM_MODULE_MANAGER_DATA_POINTER_CHECK */
+/* _txm_module_manager_page_walk */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
+/* 08-24-2026 Eclipse ThreadX Added the write intent and */
+/* contributors moved range handling to */
+/* the caller, resulting in */
+/* version 6.5.0 */
/* */
/**************************************************************************/
-UINT _txm_module_manager_inside_data_check(ULONG pointer)
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request)
{
-ULONG translation;
+ULONG translation;
+
+
+ /* Determine which unprivileged access the caller needs. */
+ if (write_request == ((UINT) TXM_MODULE_MANAGER_ACCESS_WRITE))
+ {
+
+ /* ATS1CUW operation on address supplied in address, Stage 1 unprivileged write. */
+ __asm volatile ("MCR p15, 0, %0, c7, c8, 3" : : "r"(address) : );
+ }
+ else
+ {
+
+ /* ATS1CUR operation on address supplied in address, Stage 1 unprivileged read. */
+ __asm volatile ("MCR p15, 0, %0, c7, c8, 2" : : "r"(address) : );
+ }
- /* ATS1CUR operation on address supplied in pointer, Stage 1 unprivileged read. */
- __asm volatile ("MCR p15, 0, %0, c7, c8, 2" : : "r"(pointer) : );
__asm volatile ("ISB"); /* Ensure completion of the MCR write to CP15. */
- __asm volatile ("MRC p15, 0, %0, c7, c4, 0" : "=r"(translation) : : ); /* Read result from 32-bit PAR into translation. */
+ __asm volatile ("MRC p15, 0, %0, c7, c4, 0" : "=r"(translation) : : ); /* Read result from 32-bit PAR into translation. */
if (translation & TXM_ADDRESS_TRANSLATION_FAULT_BIT)
{
@@ -82,6 +117,62 @@ ULONG translation;
}
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_current_asid_get Cortex-A7/MMU/GNU */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function returns the ASID of the translation context that is */
+/* currently loaded. Address translation always answers for that */
+/* context, so a caller validating a pointer on behalf of a module */
+/* must confirm that the loaded context is that module's before it */
+/* believes the answer. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* ASID of the currently loaded translation context */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+ULONG _txm_module_manager_current_asid_get(VOID)
+{
+
+ULONG contextidr;
+
+
+ /* Read CONTEXTIDR, whose low byte holds the ASID in the short-descriptor format. */
+ __asm volatile ("MRC p15, 0, %0, c13, c0, 1" : "=r"(contextidr) : : );
+
+ return(contextidr & TXM_CONTEXTIDR_ASID_MASK);
+}
+
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
diff --git a/ports_module/cortex_a7/iar/example_build/tx.ewp b/ports_module/cortex_a7/iar/example_build/tx.ewp
index 9d486218..97f9ffe7 100644
--- a/ports_module/cortex_a7/iar/example_build/tx.ewp
+++ b/ports_module/cortex_a7/iar/example_build/tx.ewp
@@ -2832,6 +2832,9 @@
$PROJ_DIR$\..\module_manager\src\txm_module_manager_mm_initialize.c
+
+ $PROJ_DIR$\..\module_manager\src\txm_module_manager_inside_data_check.c
+
$PROJ_DIR$\..\module_manager\src\txm_module_manager_mm_register_setup.c
diff --git a/ports_module/cortex_a7/iar/inc/txm_module_port.h b/ports_module/cortex_a7/iar/inc/txm_module_port.h
index 247868ea..dbaf7e17 100644
--- a/ports_module/cortex_a7/iar/inc/txm_module_port.h
+++ b/ports_module/cortex_a7/iar/inc/txm_module_port.h
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -251,6 +253,22 @@ The following extensions must also be defined in tx_port.h:
#define TXM_ADDRESS_TRANSLATION_FAULT_BIT 1
+/* Mask selecting the ASID field of CONTEXTIDR in the short-descriptor format. */
+#define TXM_CONTEXTIDR_ASID_MASK 0x000000FF
+
+/* Access intents understood by the module data range check. */
+#define TXM_MODULE_MANAGER_ACCESS_READ 0
+#define TXM_MODULE_MANAGER_ACCESS_WRITE 1
+
+/* Define the largest number of MMU pages a single caller-supplied range may span before
+ the data range check rejects it. This bounds the worst-case, module-controlled cost of
+ validating one kernel request, which keeps the check deterministic. The default permits
+ a 4 MB range. Applications that hand larger shared-memory ranges to kernel services can
+ define a larger value. */
+#ifndef TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES
+#define TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES 1024
+#endif
+
#define TXM_ASID_RESERVED 0xFFFFFFFF
#define TXM_MODULE_ASID_ERROR 0xF6
@@ -386,9 +404,26 @@ typedef struct TXM_MODULE_MANAGER_MEMORY_FAULT_INFO_STRUCT
/* Define the macros to perform port-specific checks when passing pointers to the kernel. */
-/* Define macro to make sure object is inside the module's data or shared memory. */
+/* Define macros to make sure a whole object is inside the module's data or shared memory.
+ The MMU is the authority for shared and external memory, so the check translates every
+ page the range touches with the requested unprivileged access. Passing the size and the
+ access intent through is what keeps the check honest: translating only the first address,
+ and only for reading, would accept a range that leaves the module's mapping partway
+ through, or a read-only range used as a kernel write destination. */
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_READ)
+
+#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size), TXM_MODULE_MANAGER_ACCESS_WRITE)
+
+/* The unqualified check keeps its historical name and takes the stricter write intent. */
#define TXM_MODULE_MANAGER_CHECK_INSIDE_DATA(module_instance, obj_ptr, obj_size) \
- _txm_module_manager_inside_data_check((ULONG) obj_ptr)
+ TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE(module_instance, obj_ptr, obj_size)
+
+/* Negating the inside check would report a range that only partly reaches into the module
+ as being outside it, so the outside direction gets its own check. */
+#define TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA(module_instance, obj_ptr, obj_size) \
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) (obj_ptr), (ULONG) (obj_size))
/* Define some internal prototypes to this module port. */
@@ -408,7 +443,10 @@ UINT _txm_module_manager_mm_initialize(VOID);
VOID _txm_module_manager_mm_register_setup(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_level2_page_clear(TXM_MODULE_INSTANCE *module_instance); \
VOID _txm_module_manager_remove_asid(TXM_MODULE_INSTANCE *module_instance); \
-UINT _txm_module_manager_inside_data_check(ULONG pointer);
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request); \
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size); \
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request); \
+ULONG _txm_module_manager_current_asid_get(VOID);
#define TXM_MODULE_MANAGER_VERSION_ID \
CHAR _txm_module_manager_version_id[] = \
diff --git a/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_inside_data_check.c b/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_inside_data_check.c
new file mode 100644
index 00000000..972cae84
--- /dev/null
+++ b/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_inside_data_check.c
@@ -0,0 +1,538 @@
+/***************************************************************************
+ * Copyright (c) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Component */
+/** */
+/** Module Manager */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+#define TX_SOURCE_CODE
+
+#include "tx_api.h"
+#include "txm_module.h"
+
+
+/* Define the ways a caller-supplied range can sit relative to the module's data region. */
+
+#define TXM_MODULE_DATA_WINDOW_OUTSIDE 0 /* No byte touches the data mapping. */
+#define TXM_MODULE_DATA_WINDOW_CONTAINED 1 /* Every byte is recorded module data. */
+#define TXM_MODULE_DATA_WINDOW_PARTIAL 2 /* Some, but not all, bytes are. */
+
+
+/* Define the page mask derived from the smallest MMU page this port maps. */
+
+#define TXM_MODULE_PAGE_OFFSET_MASK ((ALIGN_TYPE) (TXM_MODULE_MEMORY_ALIGNMENT - 1))
+#define TXM_MODULE_PAGE_BASE_MASK (~TXM_MODULE_PAGE_OFFSET_MASK)
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_range_end_get Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function derives the inclusive address of the last byte of a */
+/* caller-supplied range. An empty range is rejected, matching the */
+/* portable check macros, and so is a range whose last byte is not */
+/* representable, so that no caller ever works from a wrapped address. */
+/* */
+/* INPUT */
+/* */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* last_byte Destination for the last address */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the range is non-empty and does not wrap */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_range_end_get(ALIGN_TYPE obj_ptr, ULONG obj_size, ALIGN_TYPE *last_byte)
+{
+
+ALIGN_TYPE span;
+ALIGN_TYPE highest;
+UINT status;
+
+
+ /* Assume the range cannot be used until proven otherwise. */
+ status = TX_FALSE;
+
+ /* An empty range has no last byte and is rejected, just as the portable macros do. */
+ if (obj_size != ((ULONG) 0))
+ {
+
+ /* Work with the inclusive span so that a range reaching the top of the address
+ space stays representable. */
+ span = ((ALIGN_TYPE) obj_size) - ((ALIGN_TYPE) 1);
+
+ /* Determine the highest first byte this span can start from. */
+ highest = (~((ALIGN_TYPE) 0)) - span;
+
+ /* Determine if the range would run past the end of the address space. */
+ if (obj_ptr <= highest)
+ {
+
+ /* Record the last byte of the range. */
+ *last_byte = obj_ptr + span;
+
+ /* The range is usable. */
+ status = TX_TRUE;
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_data_window_classify Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function reports how a range sits relative to the module's own */
+/* data region. The module manager records that region exactly, while */
+/* the MMU can only map it a page at a time, so the mapping may reach */
+/* past the recorded end. The recorded region therefore stays the */
+/* authority for the whole mapped window: a range that leaves the */
+/* recorded region but still lands in the window is reported as */
+/* partial, and its caller rejects it rather than asking the MMU, */
+/* which would accept the rounding slack. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* */
+/* OUTPUT */
+/* */
+/* One of the TXM_MODULE_DATA_WINDOW_* classifications */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_data_window_classify(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE first_byte, ALIGN_TYPE last_byte)
+{
+
+ALIGN_TYPE data_start;
+ALIGN_TYPE data_end;
+ALIGN_TYPE window_start;
+ALIGN_TYPE window_end;
+UINT window;
+
+
+ /* Pick up the recorded data region. The end address is inclusive. */
+ data_start = (ALIGN_TYPE) module_instance -> txm_module_instance_data_start;
+ data_end = (ALIGN_TYPE) module_instance -> txm_module_instance_data_end;
+
+ /* Determine if the module has a usable data region at all. */
+ if (data_start > data_end)
+ {
+
+ /* An unset or inverted region owns nothing. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ else
+ {
+
+ /* Widen the region to the pages the MMU actually maps for it. */
+ window_start = data_start & TXM_MODULE_PAGE_BASE_MASK;
+ window_end = data_end | TXM_MODULE_PAGE_OFFSET_MASK;
+
+ /* Determine if every byte is recorded module data. */
+ if ((first_byte >= data_start) && (last_byte <= data_end))
+ {
+
+ /* The whole range belongs to the module. */
+ window = TXM_MODULE_DATA_WINDOW_CONTAINED;
+ }
+ else if ((first_byte <= window_end) && (last_byte >= window_start))
+ {
+
+ /* The range overlaps the mapped window without being contained by the
+ recorded region, so it leaves the module's data partway through. */
+ window = TXM_MODULE_DATA_WINDOW_PARTIAL;
+ }
+ else
+ {
+
+ /* The range lies elsewhere, so only the MMU can speak for it. */
+ window = TXM_MODULE_DATA_WINDOW_OUTSIDE;
+ }
+ }
+
+ /* Return the classification. */
+ return(window);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_page_walk Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function translates every page a range touches and reports */
+/* whether all of them answered as expected. The walk stops at the */
+/* first page that disagrees, and a range spanning more pages than */
+/* the configured maximum is refused so that one kernel request can */
+/* never impose unbounded work. */
+/* */
+/* INPUT */
+/* */
+/* first_byte Address of the first byte */
+/* last_byte Address of the last byte */
+/* write_request Access intent to translate for */
+/* expected Result every page must return */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if every page answered as expected */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_address_probe */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+static UINT _txm_module_manager_page_walk(ALIGN_TYPE first_byte, ALIGN_TYPE last_byte, UINT write_request, UINT expected)
+{
+
+ALIGN_TYPE first_page;
+ALIGN_TYPE last_page;
+ALIGN_TYPE page;
+ULONG pages;
+ULONG index;
+UINT matched;
+
+
+ /* Reduce the range to the pages it touches. */
+ first_page = first_byte & TXM_MODULE_PAGE_BASE_MASK;
+ last_page = last_byte & TXM_MODULE_PAGE_BASE_MASK;
+
+ /* Count them. Both addresses are page-aligned, so the division is exact. */
+ pages = ((ULONG) ((last_page - first_page) / ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT))) + ((ULONG) 1);
+
+ /* Determine if the range is longer than this port is willing to translate. */
+ if (pages > ((ULONG) TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES))
+ {
+
+ /* Refuse rather than spend unbounded time in a kernel request. Refusing is safe
+ for both callers: neither can then claim the range is inside or outside. */
+ matched = TX_FALSE;
+ }
+ else
+ {
+
+ /* Assume every page agrees until one does not. */
+ matched = TX_TRUE;
+ index = (ULONG) 0;
+
+ /* Translate each page in turn, stopping as soon as the answer is settled. The
+ address is rebuilt from the index so that the walk cannot wrap past the top
+ page of the address space. */
+ while ((index < pages) && (matched == TX_TRUE))
+ {
+
+ /* Build the address of this page. */
+ page = first_page + (((ALIGN_TYPE) index) * ((ALIGN_TYPE) TXM_MODULE_MEMORY_ALIGNMENT));
+
+ /* Ask the MMU whether the module may reach this page with the requested access. */
+ if (_txm_module_manager_address_probe((ULONG) page, write_request) != expected)
+ {
+
+ /* This page disagrees, so the range as a whole does. */
+ matched = TX_FALSE;
+ }
+
+ /* Move to the next page. */
+ index++;
+ }
+ }
+
+ /* Return whether the whole range answered as expected. */
+ return(matched);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_inside_data_check Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether every byte of a caller-supplied */
+/* range lies inside the module's data or shared memory and is */
+/* reachable by the module with the requested access. The module's own */
+/* data region is answered from the manager's own records, which are */
+/* exact. Anything else is answered by translating every page the */
+/* range touches, because this port keeps no record of the shared and */
+/* external regions the application maps into the module. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if the whole range is reachable with that access */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_READ */
+/* TXM_MODULE_MANAGER_CHECK_INSIDE_DATA_WRITE */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size, UINT write_request)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not usable until every condition is met. */
+ status = TX_FALSE;
+
+ /* A range can only belong to a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_CONTAINED))
+ {
+
+ /* The module's data region is mapped for unprivileged reading and
+ writing, so both intents are satisfied without translating. */
+ status = TX_TRUE;
+ }
+ else if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Shared or external memory: every page must grant the access. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, write_request, TX_TRUE);
+ }
+ else
+ {
+
+ /* The range leaves the module's data partway through. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
+
+
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_outside_data_check Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function determines whether no byte of a caller-supplied range */
+/* is reachable by the module. It is deliberately not the negation of */
+/* the inside check: a range that reaches into the module's memory */
+/* only partway is inside neither answer, and reporting it as outside */
+/* would let it pass as a kernel object. */
+/* */
+/* INPUT */
+/* */
+/* module_instance Pointer to module instance */
+/* obj_ptr Address of the first byte */
+/* obj_size Size of the range in bytes */
+/* */
+/* OUTPUT */
+/* */
+/* TX_TRUE if no byte of the range is reachable by the module */
+/* */
+/* CALLS */
+/* */
+/* _txm_module_manager_range_end_get */
+/* _txm_module_manager_current_asid_get */
+/* _txm_module_manager_data_window_classify */
+/* _txm_module_manager_page_walk */
+/* */
+/* CALLED BY */
+/* */
+/* TXM_MODULE_MANAGER_CHECK_OUTSIDE_DATA */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr, ULONG obj_size)
+{
+
+ALIGN_TYPE last_byte;
+UINT window;
+UINT status;
+
+
+ /* Assume the range is not provably outside the module. */
+ status = TX_FALSE;
+
+ /* A range can only be placed against a module that exists. */
+ if (module_instance != TX_NULL)
+ {
+
+ /* Determine the last byte, rejecting empty and wrapping ranges. */
+ if (_txm_module_manager_range_end_get(obj_ptr, obj_size, &last_byte) == TX_TRUE)
+ {
+
+ /* Address translation answers for whichever context is loaded, so it can only
+ be trusted while the requesting module's context is the loaded one. */
+ if (_txm_module_manager_current_asid_get() == (module_instance -> txm_module_instance_asid))
+ {
+
+ /* Place the range relative to the module's own data region. */
+ window = _txm_module_manager_data_window_classify(module_instance, obj_ptr, last_byte);
+
+ if (window == ((UINT) TXM_MODULE_DATA_WINDOW_OUTSIDE))
+ {
+
+ /* Clear of the module's data region, so the remaining question is
+ whether any page of it is shared or external memory. */
+ status = _txm_module_manager_page_walk(obj_ptr, last_byte, TXM_MODULE_MANAGER_ACCESS_READ, TX_FALSE);
+ }
+ else
+ {
+
+ /* The range touches the module's data region. */
+ status = TX_FALSE;
+ }
+ }
+ }
+ }
+
+ /* Return the completion status. */
+ return(status);
+}
diff --git a/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_mm_register_setup.c b/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_mm_register_setup.c
index 3d477a4a..3adc61c3 100644
--- a/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_mm_register_setup.c
+++ b/ports_module/cortex_a7/iar/module_manager/src/txm_module_manager_mm_register_setup.c
@@ -9,6 +9,8 @@
* SPDX-License-Identifier: MIT
**************************************************************************/
+// Some portions generated by Claude Code (Opus 5).
+
/**************************************************************************/
/**************************************************************************/
@@ -35,24 +37,34 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* FUNCTION RELEASE */
/* */
-/* _txm_module_manager_inside_data_check Cortex-A7/MMU/IAR */
-/* 6.1 */
+/* _txm_module_manager_address_probe Cortex-A7/MMU/IAR */
+/* 6.5.0 */
/* AUTHOR */
/* */
/* Scott Larson, Microsoft Corporation */
+/* Eclipse ThreadX contributors */
/* */
/* DESCRIPTION */
/* */
-/* This function determines if pointer is within the module's data or */
-/* shared memory. */
+/* This function asks the MMU whether unprivileged code running in the */
+/* currently loaded translation context may reach one address with the */
+/* requested access. Reading and writing are asked separately, because */
+/* a module's code and its read-only shared memory translate for */
+/* reading while a kernel service writing to them would fault or, */
+/* worse, succeed from privileged mode. */
+/* */
+/* This answers for one address only. Callers that hold a range must */
+/* ask about every page the range touches. */
/* */
/* INPUT */
/* */
-/* pointer Data pointer */
+/* address Address to translate */
+/* write_request TXM_MODULE_MANAGER_ACCESS_READ or */
+/* TXM_MODULE_MANAGER_ACCESS_WRITE */
/* */
/* OUTPUT */
/* */
-/* Completion Status */
+/* TX_TRUE if the translation granted the requested access */
/* */
/* CALLS */
/* */
@@ -60,17 +72,40 @@ extern ULONG _txm_ttbr1_page_table[TXM_MAXIMUM_MODULES][TXM_MASTER_PAGE_TABLE_EN
/* */
/* CALLED BY */
/* */
-/* TXM_MODULE_MANAGER_DATA_POINTER_CHECK */
+/* _txm_module_manager_page_walk */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 03-08-2023 Scott Larson Initial Version 6.2.1 */
+/* 08-24-2026 Eclipse ThreadX Added the write intent and */
+/* contributors moved range handling to */
+/* the caller, resulting in */
+/* version 6.5.0 */
/* */
/**************************************************************************/
-UINT _txm_module_manager_inside_data_check(ULONG pointer)
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request)
{
-ULONG translation;
+ULONG translation;
- /* ATS1CUR operation on address supplied in pointer, Stage 1 unprivileged read. */
- asm("MCR p15, 0, %0, c7, c8, 2" : : "r"(pointer) : );
- asm("ISB"); /* Ensure completion of the MCR write to CP15. */
+
+ /* Determine which unprivileged access the caller needs. */
+ if (write_request == ((UINT) TXM_MODULE_MANAGER_ACCESS_WRITE))
+ {
+
+ /* ATS1CUW operation on address supplied in address, Stage 1 unprivileged write. */
+ asm("MCR p15, 0, %0, c7, c8, 3" : : "r"(address) : );
+ }
+ else
+ {
+
+ /* ATS1CUR operation on address supplied in address, Stage 1 unprivileged read. */
+ asm("MCR p15, 0, %0, c7, c8, 2" : : "r"(address) : );
+ }
+
+ asm("ISB"); /* Ensure completion of the MCR write to CP15. */
asm("MRC p15, 0, %0, c7, c4, 0" : "=r"(translation) : : ); /* Read result from 32-bit PAR into translation. */
if (translation & TXM_ADDRESS_TRANSLATION_FAULT_BIT)
@@ -82,6 +117,62 @@ ULONG translation;
}
+/**************************************************************************/
+/* */
+/* FUNCTION RELEASE */
+/* */
+/* _txm_module_manager_current_asid_get Cortex-A7/MMU/IAR */
+/* 6.5.0 */
+/* AUTHOR */
+/* */
+/* Eclipse ThreadX contributors */
+/* */
+/* DESCRIPTION */
+/* */
+/* This function returns the ASID of the translation context that is */
+/* currently loaded. Address translation always answers for that */
+/* context, so a caller validating a pointer on behalf of a module */
+/* must confirm that the loaded context is that module's before it */
+/* believes the answer. */
+/* */
+/* INPUT */
+/* */
+/* None */
+/* */
+/* OUTPUT */
+/* */
+/* ASID of the currently loaded translation context */
+/* */
+/* CALLS */
+/* */
+/* None */
+/* */
+/* CALLED BY */
+/* */
+/* _txm_module_manager_inside_data_check */
+/* _txm_module_manager_outside_data_check */
+/* */
+/* RELEASE HISTORY */
+/* */
+/* DATE NAME DESCRIPTION */
+/* */
+/* 08-24-2026 Eclipse ThreadX Initial Version 6.5.0 */
+/* contributors */
+/* */
+/**************************************************************************/
+ULONG _txm_module_manager_current_asid_get(VOID)
+{
+
+ULONG contextidr;
+
+
+ /* Read CONTEXTIDR, whose low byte holds the ASID in the short-descriptor format. */
+ asm("MRC p15, 0, %0, c13, c0, 1" : "=r"(contextidr) : : );
+
+ return(contextidr & TXM_CONTEXTIDR_ASID_MASK);
+}
+
+
/**************************************************************************/
/* */
/* FUNCTION RELEASE */
diff --git a/scripts/check_module_mmu_a7.sh b/scripts/check_module_mmu_a7.sh
new file mode 100755
index 00000000..593edb31
--- /dev/null
+++ b/scripts/check_module_mmu_a7.sh
@@ -0,0 +1,120 @@
+#!/bin/bash
+##############################################################################
+# Copyright (c) 2026 Eclipse ThreadX contributors
+#
+# This program and the accompanying materials are made available under the
+# terms of the MIT License which is available at
+# https://opensource.org/licenses/MIT.
+#
+# AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+# The AI-generated portions may be considered public domain (CC0-1.0)
+# and not subject to the project's licence. The human contributor has
+# reviewed and verified that the code is correct.
+#
+# SPDX-License-Identifier: MIT and CC0-1.0
+##############################################################################
+
+# Runs the Cortex-A7 module range check against a live MMU.
+#
+# The host test for that check stands a simulated page map behind the port's
+# CP15 primitive, so it never executes an address translation. An encoding that
+# named the wrong operation, or a PAR fault bit read the wrong way round, would
+# pass it. This check builds a bare-metal image that turns the MMU on and drives
+# the real check through the real translations, then runs it under an emulator.
+#
+# scripts/check_module_mmu_a7.sh
+#
+# Environment:
+# CROSS_CC arm-none-eabi C compiler; defaults to arm-none-eabi-gcc.
+# QEMU Arm system emulator; defaults to qemu-system-arm.
+#
+# Exit status is 0 when the image runs and every expectation holds, 1 when an
+# expectation fails, and 0 with a notice when the tools are absent, so that a
+# machine without a cross toolchain does not fail the build.
+
+set -u
+
+CC="${CROSS_CC:-arm-none-eabi-gcc}"
+QEMU_BIN="${QEMU:-qemu-system-arm}"
+
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+SRC="$ROOT/test/tx/module_manager/threadx_module_manager_cortex_a7_mmu_test.c"
+PORT="$ROOT/ports_module/cortex_a7/gnu/module_manager/src/txm_module_manager_inside_data_check.c"
+
+for tool in "$CC" "$QEMU_BIN"; do
+ if ! command -v "$tool" >/dev/null 2>&1 && [ ! -x "$tool" ]; then
+ echo "Skipping: $tool not found."
+ echo " Needs an arm-none-eabi toolchain and qemu-system-arm."
+ exit 0
+ fi
+done
+
+# The port supplies the two architecture primitives from its register setup
+# file, which cannot be compiled here because it also builds the module page
+# tables. They are provided by the image itself, copied from that file, so what
+# runs is the same instruction sequence the port issues.
+work="$(mktemp -d)"
+trap 'rm -rf "$work"' EXIT
+
+cat > "$work/primitives.c" <<'PRIMITIVES'
+#define TX_SOURCE_CODE
+#include "tx_api.h"
+#include "txm_module.h"
+
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request)
+{
+ULONG translation;
+
+ if (write_request == ((UINT) TXM_MODULE_MANAGER_ACCESS_WRITE))
+ {
+ __asm volatile ("MCR p15, 0, %0, c7, c8, 3" : : "r"(address) : );
+ }
+ else
+ {
+ __asm volatile ("MCR p15, 0, %0, c7, c8, 2" : : "r"(address) : );
+ }
+
+ __asm volatile ("ISB");
+ __asm volatile ("MRC p15, 0, %0, c7, c4, 0" : "=r"(translation) : : );
+
+ if (translation & TXM_ADDRESS_TRANSLATION_FAULT_BIT)
+ {
+ return(TX_FALSE);
+ }
+
+ return(TX_TRUE);
+}
+
+ULONG _txm_module_manager_current_asid_get(VOID)
+{
+ULONG contextidr;
+
+ __asm volatile ("MRC p15, 0, %0, c13, c0, 1" : "=r"(contextidr) : : );
+
+ return(contextidr & TXM_CONTEXTIDR_ASID_MASK);
+}
+PRIMITIVES
+
+"$CC" -mcpu=cortex-a7 -marm -O1 -std=gnu99 -Wall -Wextra -Werror \
+ --specs=rdimon.specs \
+ -I"$ROOT/ports_module/cortex_a7/gnu/inc" \
+ -I"$ROOT/common/inc" \
+ -I"$ROOT/common_modules/inc" \
+ -I"$ROOT/common_modules/module_manager/inc" \
+ -o "$work/mmu_test.elf" "$SRC" "$PORT" "$work/primitives.c" || {
+ echo "FAIL: the Cortex-A7 MMU test image did not build."
+ exit 1
+}
+
+output="$("$QEMU_BIN" -M realview-pb-a8 -cpu cortex-a7 -m 128 -nographic \
+ -semihosting -kernel "$work/mmu_test.elf" 2>/dev/null)"
+status=$?
+
+echo "$output"
+
+if [ $status -ne 0 ] || ! echo "$output" | grep -q "SUCCESS!"; then
+ echo "FAIL: the Cortex-A7 MMU test did not report success."
+ exit 1
+fi
+
+exit 0
diff --git a/test/tx/cmake/module_manager/CMakeLists.txt b/test/tx/cmake/module_manager/CMakeLists.txt
index 2ebd9684..17511fd5 100644
--- a/test/tx/cmake/module_manager/CMakeLists.txt
+++ b/test/tx/cmake/module_manager/CMakeLists.txt
@@ -236,3 +236,19 @@ target_compile_options(
add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_queue_message_range_test
threadx_module_manager_queue_message_range_test)
+
+add_executable(
+ threadx_module_manager_cortex_a7_range_check_test
+ ${SOURCE_DIR}/threadx_module_manager_cortex_a7_range_check_test.c
+ ${cortex_a7_module_dir}/module_manager/src/txm_module_manager_inside_data_check.c)
+
+target_include_directories(
+ threadx_module_manager_cortex_a7_range_check_test
+ PRIVATE ${REPO_ROOT}/common/inc
+ ${REPO_ROOT}/ports/cortex_a7/gnu/inc
+ ${REPO_ROOT}/common_modules/inc
+ ${REPO_ROOT}/common_modules/module_manager/inc
+ ${cortex_a7_module_dir}/inc)
+
+add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_cortex_a7_range_check_test
+ threadx_module_manager_cortex_a7_range_check_test)
diff --git a/test/tx/module_manager/threadx_module_manager_cortex_a7_mmu_test.c b/test/tx/module_manager/threadx_module_manager_cortex_a7_mmu_test.c
new file mode 100644
index 00000000..8cc08108
--- /dev/null
+++ b/test/tx/module_manager/threadx_module_manager_cortex_a7_mmu_test.c
@@ -0,0 +1,205 @@
+/***************************************************************************
+ * Copyright (c) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Test */
+/** */
+/** Module Manager Cortex-A7 range validation, against a live MMU */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+/* The host test for this check stands a simulated page map behind the port's
+ one architecture primitive, which lets it run everywhere but leaves the CP15
+ address translation itself unexercised: an encoding that named the wrong
+ operation, or a PAR fault bit read the wrong way round, would pass it.
+
+ This test closes that gap. It builds a short-descriptor translation table,
+ turns the MMU on, and drives the real check through the real translation
+ operations on a real Cortex-A7 translation regime. It is a bare-metal image,
+ built and run by scripts/check_module_mmu_a7.sh under an emulator.
+
+ The sections below are mapped for their access permissions only; nothing is
+ dereferenced through them, and none of the addresses name a real target. */
+
+#include
+
+#define TX_SOURCE_CODE
+
+#include "tx_api.h"
+#include "txm_module.h"
+
+
+/* Short-descriptor section entry fields. AP[2:0] selects the access
+ permissions: 011 is unprivileged read/write, 010 is unprivileged read only,
+ 001 leaves the section privileged-only. */
+
+#define TEST_SECTION_TYPE 0x00000002
+#define TEST_AP_LOW(value) (((ULONG) (value)) << 10)
+#define TEST_AP_USER_RW TEST_AP_LOW(3)
+#define TEST_AP_USER_RO TEST_AP_LOW(2)
+#define TEST_AP_PRIVILEGED_ONLY TEST_AP_LOW(1)
+
+#define TEST_LEVEL1_ENTRIES 4096
+#define TEST_SECTION_SIZE 0x00100000
+
+/* One section of each kind the check has to tell apart, plus an unmapped
+ section immediately after the read-only one so a range can be made to leave
+ its mapping partway through. */
+
+#define TEST_SHARED_RW 0x40000000
+#define TEST_SHARED_RO 0x40100000
+#define TEST_GUARD 0x40200000
+#define TEST_MODULE_DATA 0x41000000
+#define TEST_MODULE_DATA_SIZE 0x00001000
+#define TEST_KERNEL_ONLY 0x30000000
+
+static ULONG test_level1_table[TEST_LEVEL1_ENTRIES] __attribute__((aligned(16384)));
+static UINT test_failures = 0;
+
+
+/* Reproduce the behaviour this check replaced: one translation of the first
+ address, for reading, with the size discarded. Kept so the boundary cases
+ can show both answers against the same live MMU. */
+static UINT test_first_address_only_check(ALIGN_TYPE obj_ptr)
+{
+ return(_txm_module_manager_address_probe((ULONG) obj_ptr, TXM_MODULE_MANAGER_ACCESS_READ));
+}
+
+
+/* Build a flat translation table, then enable the MMU. */
+static VOID test_mmu_enable(VOID)
+{
+
+ULONG index;
+ULONG sctlr;
+ULONG zero = 0;
+ULONG dacr = 1; /* Domain 0 as a client, so AP is checked. */
+
+
+ /* Everything is privileged-only until a section below says otherwise. */
+ for (index = 0; index < TEST_LEVEL1_ENTRIES; index++)
+ {
+ test_level1_table[index] = (index * TEST_SECTION_SIZE) | TEST_SECTION_TYPE | TEST_AP_PRIVILEGED_ONLY;
+ }
+
+ test_level1_table[TEST_SHARED_RW / TEST_SECTION_SIZE] =
+ TEST_SHARED_RW | TEST_SECTION_TYPE | TEST_AP_USER_RW;
+ test_level1_table[TEST_SHARED_RO / TEST_SECTION_SIZE] =
+ TEST_SHARED_RO | TEST_SECTION_TYPE | TEST_AP_USER_RO;
+ test_level1_table[TEST_MODULE_DATA / TEST_SECTION_SIZE] =
+ TEST_MODULE_DATA | TEST_SECTION_TYPE | TEST_AP_USER_RW;
+
+ /* Leave the guard section faulting. */
+ test_level1_table[TEST_GUARD / TEST_SECTION_SIZE] = 0;
+
+ __asm volatile ("MCR p15, 0, %0, c2, c0, 2" : : "r"(zero)); /* TTBCR */
+ __asm volatile ("MCR p15, 0, %0, c2, c0, 0" : : "r"((ULONG) test_level1_table)); /* TTBR0 */
+ __asm volatile ("MCR p15, 0, %0, c3, c0, 0" : : "r"(dacr)); /* DACR */
+ __asm volatile ("MCR p15, 0, %0, c13, c0, 1" : : "r"(zero)); /* CONTEXTIDR */
+ __asm volatile ("MCR p15, 0, %0, c8, c7, 0" : : "r"(zero)); /* TLBIALL */
+ __asm volatile ("DSB");
+ __asm volatile ("ISB");
+
+ __asm volatile ("MRC p15, 0, %0, c1, c0, 0" : "=r"(sctlr));
+ sctlr = sctlr | ((ULONG) 1); /* SCTLR.M */
+ __asm volatile ("MCR p15, 0, %0, c1, c0, 0" : : "r"(sctlr));
+ __asm volatile ("ISB");
+}
+
+
+static VOID test_expect(const char *description, UINT actual, UINT expected)
+{
+ if (actual != expected)
+ {
+ printf(" FAIL: %s (expected %u, got %u)\n", description, expected, actual);
+ test_failures++;
+ }
+}
+
+
+int main(VOID)
+{
+
+TXM_MODULE_INSTANCE module;
+TXM_MODULE_INSTANCE *module_instance;
+ALIGN_TYPE straddle;
+
+
+ printf("Cortex-A7 module range validation against a live MMU................... ");
+
+ test_mmu_enable();
+
+ module_instance = &module;
+ module_instance -> txm_module_instance_id = TXM_MODULE_ID;
+ module_instance -> txm_module_instance_data_start = (VOID *) TEST_MODULE_DATA;
+ module_instance -> txm_module_instance_data_end = (VOID *) (TEST_MODULE_DATA + TEST_MODULE_DATA_SIZE - 1);
+ module_instance -> txm_module_instance_code_start = (VOID *) 0;
+ module_instance -> txm_module_instance_code_end = (VOID *) 0;
+ module_instance -> txm_module_instance_asid = 0;
+
+ /* The module's own data is answered from the manager's records. */
+ test_expect("recorded module data is writable",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_MODULE_DATA, 4,
+ TXM_MODULE_MANAGER_ACCESS_WRITE), TX_TRUE);
+ test_expect("a range crossing the recorded data end is rejected",
+ _txm_module_manager_inside_data_check(module_instance,
+ (ALIGN_TYPE) (TEST_MODULE_DATA + TEST_MODULE_DATA_SIZE - 2), 4,
+ TXM_MODULE_MANAGER_ACCESS_WRITE), TX_FALSE);
+
+ /* Shared memory is answered by the MMU, one page at a time. */
+ test_expect("shared read/write memory is writable",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_SHARED_RW, 4,
+ TXM_MODULE_MANAGER_ACCESS_WRITE), TX_TRUE);
+ test_expect("shared read-only memory is readable",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_SHARED_RO, 4,
+ TXM_MODULE_MANAGER_ACCESS_READ), TX_TRUE);
+
+ /* The write intent is the reason the port asks for two translations rather
+ than one; a read-only mapping must not serve as a kernel destination. */
+ test_expect("shared read-only memory is not writable",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_SHARED_RO, 4,
+ TXM_MODULE_MANAGER_ACCESS_WRITE), TX_FALSE);
+
+ /* A range that leaves its mapping partway through. This is the case the
+ replaced check accepted, and the reason this test exists. */
+ straddle = (ALIGN_TYPE) (TEST_GUARD - 2);
+ test_expect("a range crossing into the guard section is rejected",
+ _txm_module_manager_inside_data_check(module_instance, straddle, 4,
+ TXM_MODULE_MANAGER_ACCESS_READ), TX_FALSE);
+ test_expect("...and translating only its first address would have accepted it",
+ test_first_address_only_check(straddle), TX_TRUE);
+
+ /* Privileged memory is reachable by neither intent, and is outside the module. */
+ test_expect("privileged-only memory is not readable",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_KERNEL_ONLY, 4,
+ TXM_MODULE_MANAGER_ACCESS_READ), TX_FALSE);
+ test_expect("privileged-only memory is outside the module",
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) TEST_KERNEL_ONLY, 64), TX_TRUE);
+ test_expect("shared memory is not outside the module",
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) TEST_SHARED_RW, 64), TX_FALSE);
+
+ if (test_failures == 0)
+ {
+ printf("SUCCESS!\n");
+ return(0);
+ }
+
+ printf("ERROR: %u expectation(s) failed\n", test_failures);
+ return(1);
+}
diff --git a/test/tx/module_manager/threadx_module_manager_cortex_a7_range_check_test.c b/test/tx/module_manager/threadx_module_manager_cortex_a7_range_check_test.c
new file mode 100644
index 00000000..a0cca868
--- /dev/null
+++ b/test/tx/module_manager/threadx_module_manager_cortex_a7_range_check_test.c
@@ -0,0 +1,443 @@
+/***************************************************************************
+ * Copyright (c) 2026 Eclipse ThreadX contributors
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the MIT License which is available at
+ * https://opensource.org/licenses/MIT.
+ *
+ * AI Disclosure: This file was largely AI-generated by Claude Code (Opus 5).
+ * The AI-generated portions may be considered public domain (CC0-1.0)
+ * and not subject to the project's licence. The human contributor has
+ * reviewed and verified that the code is correct.
+ *
+ * SPDX-License-Identifier: MIT and CC0-1.0
+ **************************************************************************/
+
+
+/**************************************************************************/
+/**************************************************************************/
+/** */
+/** ThreadX Test */
+/** */
+/** Module Manager Cortex-A7 data range validation */
+/** */
+/**************************************************************************/
+/**************************************************************************/
+
+/* This test exercises the Cortex-A7 module data range check through the same
+ macros the module dispatchers use. The check normally asks the MMU about one
+ page at a time, so the test stands a simulated page map behind that one
+ architecture primitive and drives every outcome from the host build.
+
+ No address in this test is dereferenced and none of them name a real target.
+ They are labels in a table that the stubbed primitive answers questions about,
+ which is what lets the test describe a range that leaves a module's mapping
+ without ever building something that could read past one. */
+
+#include
+
+#define TX_SOURCE_CODE
+
+#include "tx_api.h"
+#include "txm_module.h"
+#include "txm_module_manager_util.h"
+
+
+/* Define the simulated address space.
+
+ The module's recorded data region deliberately ends partway through its last
+ page, because that is what the module manager's page-granular mapping does in
+ practice: the mapping reaches to the end of the page while the recorded region
+ does not. The bytes in between are the rounding slack, and the check must not
+ hand them out. */
+
+#define TEST_PAGE_SIZE 4096
+
+#define TEST_DATA_START 0x20001000UL
+#define TEST_DATA_END 0x200027FFUL /* Ends mid-page: slack follows. */
+#define TEST_DATA_WINDOW_END 0x20002FFFUL /* Last byte the MMU maps for it. */
+
+#define TEST_CODE_START 0x10001000UL
+#define TEST_CODE_END 0x10001FFFUL
+
+#define TEST_SHARED_RW_START 0x20010000UL /* Two contiguous read/write pages. */
+#define TEST_SHARED_RW_END 0x20011FFFUL
+#define TEST_GUARD_PAGE 0x20012000UL /* Unmapped, directly after them. */
+
+#define TEST_SPLIT_FIRST_PAGE 0x20030000UL /* Mapped. */
+#define TEST_SPLIT_MIDDLE_PAGE 0x20031000UL /* Unmapped. */
+#define TEST_SPLIT_LAST_PAGE 0x20032000UL /* Mapped. */
+
+#define TEST_SHARED_RO_START 0x20020000UL /* Readable, not writable. */
+#define TEST_SHARED_RO_END 0x20020FFFUL
+
+#define TEST_KERNEL_OBJECT 0x30000000UL /* Unmapped for the module. */
+
+#define TEST_MODULE_ASID 5UL
+#define TEST_OTHER_MODULE_ASID 6UL
+
+
+/* Define one entry of the simulated page map. */
+
+typedef struct TEST_PAGE_STRUCT
+{
+ ULONG test_page_base;
+ UINT test_page_readable;
+ UINT test_page_writable;
+} TEST_PAGE;
+
+
+static TEST_PAGE test_page_map[] =
+{
+ /* The module's own data pages, mapped read/write to the end of the page. */
+ {TEST_DATA_START, TX_TRUE, TX_TRUE},
+ {TEST_DATA_START + TEST_PAGE_SIZE, TX_TRUE, TX_TRUE},
+
+ /* The module's code, readable but never writable from unprivileged mode. */
+ {TEST_CODE_START, TX_TRUE, TX_FALSE},
+
+ /* Read/write shared memory, followed by an unmapped guard page. */
+ {TEST_SHARED_RW_START, TX_TRUE, TX_TRUE},
+ {TEST_SHARED_RW_START + TEST_PAGE_SIZE, TX_TRUE, TX_TRUE},
+
+ /* Read-only shared memory. */
+ {TEST_SHARED_RO_START, TX_TRUE, TX_FALSE},
+
+ /* Two mapped pages with an unmapped page between them. */
+ {TEST_SPLIT_FIRST_PAGE, TX_TRUE, TX_TRUE},
+ {TEST_SPLIT_LAST_PAGE, TX_TRUE, TX_TRUE}
+};
+
+#define TEST_PAGE_MAP_ENTRIES (sizeof(test_page_map) / sizeof(test_page_map[0]))
+
+
+/* Define the state the stubbed architecture primitives answer from. */
+
+static ULONG test_current_asid = TEST_MODULE_ASID;
+static ULONG test_probe_count = 0UL;
+static UINT test_failures = 0U;
+
+
+/* Stand in for the CP15 address translation the real port performs. */
+UINT _txm_module_manager_address_probe(ULONG address, UINT write_request)
+{
+
+ULONG page_base;
+ULONG index;
+UINT granted;
+
+
+ test_probe_count++;
+
+ page_base = address & (~((ULONG) (TEST_PAGE_SIZE - 1)));
+ granted = TX_FALSE;
+
+ for (index = 0UL; index < (ULONG) TEST_PAGE_MAP_ENTRIES; index++)
+ {
+ if (test_page_map[index].test_page_base == page_base)
+ {
+ if (write_request == TXM_MODULE_MANAGER_ACCESS_WRITE)
+ {
+ granted = test_page_map[index].test_page_writable;
+ }
+ else
+ {
+ granted = test_page_map[index].test_page_readable;
+ }
+ }
+ }
+
+ return(granted);
+}
+
+
+/* Stand in for the CONTEXTIDR read the real port performs. */
+ULONG _txm_module_manager_current_asid_get(VOID)
+{
+ return(test_current_asid);
+}
+
+
+/* Model the implementation this test guards against. It translated the first
+ address of the range and nothing else, and it only ever asked about reading.
+ Keeping it here lets each boundary case show both answers side by side. */
+static UINT test_first_address_only_check(ALIGN_TYPE obj_ptr)
+{
+ return(_txm_module_manager_address_probe((ULONG) obj_ptr, TXM_MODULE_MANAGER_ACCESS_READ));
+}
+
+
+/* Record the outcome of one expectation. */
+static VOID test_expect(const char *description, UINT actual, UINT expected)
+{
+ if (actual != expected)
+ {
+ printf("FAIL: %s (expected %u, got %u)\n", description, expected, actual);
+ test_failures++;
+ }
+}
+
+
+/* Build a module instance describing the simulated module. */
+static VOID test_module_build(TXM_MODULE_INSTANCE *module_instance)
+{
+ module_instance -> txm_module_instance_id = TXM_MODULE_ID;
+ module_instance -> txm_module_instance_data_start = (VOID *) TEST_DATA_START;
+ module_instance -> txm_module_instance_data_end = (VOID *) TEST_DATA_END;
+ module_instance -> txm_module_instance_code_start = (VOID *) TEST_CODE_START;
+ module_instance -> txm_module_instance_code_end = (VOID *) TEST_CODE_END;
+ module_instance -> txm_module_instance_asid = TEST_MODULE_ASID;
+}
+
+
+int main(void)
+{
+
+TXM_MODULE_INSTANCE module;
+TXM_MODULE_INSTANCE *module_instance;
+ALIGN_TYPE address;
+ULONG probes_before;
+ULONG huge_size;
+
+
+ printf("Cortex-A7 module data range validation test............................ ");
+
+ module_instance = &module;
+ test_module_build(module_instance);
+
+ /**********************************************************************/
+ /* Normal valid behaviour. */
+ /**********************************************************************/
+
+ test_expect("one byte inside module data is writable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 1UL), TX_TRUE);
+
+ test_expect("one word inside module data is writable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("one word inside module data is readable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, (ALIGN_TYPE) TEST_DATA_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("a range spanning both module data pages is writable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 0x1800UL), TX_TRUE);
+
+ /* The module's own data is answered from the manager's records, so it costs
+ no translations at all. This is what keeps the common case deterministic. */
+ probes_before = test_probe_count;
+ (VOID) TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 0x1800UL);
+ test_expect("module data is answered without translating",
+ (UINT) (test_probe_count == probes_before), TX_TRUE);
+
+ /**********************************************************************/
+ /* Null and invalid parameters. */
+ /**********************************************************************/
+
+ /* A null buffer is how the dispatchers say "not supplied", and the macros
+ accept it. Only the range check below it must reject a null module. */
+ test_expect("a null buffer pointer is accepted by the buffer macros",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) 0, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("a null module instance is rejected",
+ _txm_module_manager_inside_data_check(TX_NULL, (ALIGN_TYPE) TEST_DATA_START, (ULONG) sizeof(ULONG), TXM_MODULE_MANAGER_ACCESS_WRITE), TX_FALSE);
+
+ test_expect("a null module instance is not outside anything either",
+ _txm_module_manager_outside_data_check(TX_NULL, (ALIGN_TYPE) TEST_KERNEL_OBJECT, (ULONG) sizeof(ULONG)), TX_FALSE);
+
+ /* A module torn down to an empty data region owns nothing, and the address
+ range must then be settled by the MMU rather than by stale records. */
+ module.txm_module_instance_data_start = (VOID *) TEST_DATA_END;
+ module.txm_module_instance_data_end = (VOID *) TEST_DATA_START;
+ test_expect("an inverted data region grants nothing from records",
+ _txm_module_manager_inside_data_check(module_instance, (ALIGN_TYPE) TEST_KERNEL_OBJECT, (ULONG) sizeof(ULONG), TXM_MODULE_MANAGER_ACCESS_WRITE), TX_FALSE);
+ test_module_build(module_instance);
+
+ /**********************************************************************/
+ /* Exact boundaries, and the one byte past each of them. */
+ /**********************************************************************/
+
+ test_expect("a range starting exactly at data start is accepted",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 4UL), TX_TRUE);
+
+ test_expect("a range starting one byte before data start is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) (TEST_DATA_START - 1UL), 4UL), TX_FALSE);
+
+ test_expect("a range ending exactly at data end is accepted",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) (TEST_DATA_END - 3UL), 4UL), TX_TRUE);
+
+ test_expect("the last byte of data on its own is accepted",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_END, 1UL), TX_TRUE);
+
+ /* One byte further is the rounding slack: mapped, but not the module's. */
+ address = (ALIGN_TYPE) (TEST_DATA_END - 3UL);
+ test_expect("a range ending one byte past data end is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, address, 5UL), TX_FALSE);
+ test_expect("...and the first-address-only check would have accepted it",
+ test_first_address_only_check(address), TX_TRUE);
+
+ test_expect("the one-past-the-end byte is rejected on its own",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) (TEST_DATA_END + 1UL), 1UL), TX_FALSE);
+
+ test_expect("the whole rounding slack is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) (TEST_DATA_END + 1UL), (ULONG) (TEST_DATA_WINDOW_END - TEST_DATA_END)), TX_FALSE);
+
+ /* A word that straddles the recorded end crosses it by one word. */
+ address = (ALIGN_TYPE) (TEST_DATA_END - 1UL);
+ test_expect("a word straddling data end is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, address, (ULONG) sizeof(ULONG)), TX_FALSE);
+ test_expect("...and the first-address-only check would have accepted it",
+ test_first_address_only_check(address), TX_TRUE);
+
+ /**********************************************************************/
+ /* Integer overflow and underflow. */
+ /**********************************************************************/
+
+ test_expect("a zero-length range is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 0UL), TX_FALSE);
+
+ /* A range whose end wraps must be refused before anything is computed from
+ the wrapped address. */
+ probes_before = test_probe_count;
+ test_expect("a range whose end wraps past the top of memory is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 0xFFFFFFFFUL), TX_FALSE);
+ test_expect("...without translating the wrapped address",
+ (UINT) (test_probe_count == probes_before), TX_TRUE);
+
+ test_expect("a range starting at the very top of memory with size two is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) 0xFFFFFFFFUL, 2UL), TX_FALSE);
+
+ test_expect("a one-byte range at the very top of memory does not wrap, and is simply unmapped",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) 0xFFFFFFFFUL, 1UL), TX_FALSE);
+
+ test_expect("a zero-length range is not outside the module either",
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) TEST_KERNEL_OBJECT, 0UL), TX_FALSE);
+
+ /**********************************************************************/
+ /* Shared memory, guard pages, and holes. */
+ /**********************************************************************/
+
+ test_expect("a range inside read/write shared memory is writable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SHARED_RW_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("a range spanning two shared pages is writable",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SHARED_RW_START, 0x1800UL), TX_TRUE);
+
+ test_expect("a range ending exactly at the end of shared memory is accepted",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) (TEST_SHARED_RW_END - 3UL), 4UL), TX_TRUE);
+
+ /* One byte more crosses into the guard page. */
+ address = (ALIGN_TYPE) (TEST_SHARED_RW_END - 3UL);
+ test_expect("a range crossing one byte into the guard page is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, address, 5UL), TX_FALSE);
+ test_expect("...and the first-address-only check would have accepted it",
+ test_first_address_only_check(address), TX_TRUE);
+
+ test_expect("a three-page range with an unmapped middle page is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SPLIT_FIRST_PAGE, (ULONG) ((TEST_SPLIT_LAST_PAGE + 4UL) - TEST_SPLIT_FIRST_PAGE)), TX_FALSE);
+
+ test_expect("the first page of that range on its own is still accepted",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SPLIT_FIRST_PAGE, 4UL), TX_TRUE);
+
+ /**********************************************************************/
+ /* Read intent versus write intent. */
+ /**********************************************************************/
+
+ test_expect("read-only shared memory can be read from",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, (ALIGN_TYPE) TEST_SHARED_RO_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("read-only shared memory cannot be written to",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SHARED_RO_START, (ULONG) sizeof(ULONG)), TX_FALSE);
+
+ test_expect("a read-only shared range crossing its own end is rejected for reading too",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, (ALIGN_TYPE) (TEST_SHARED_RO_END - 3UL), 5UL), TX_FALSE);
+
+ test_expect("module code can be read from",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, (ALIGN_TYPE) TEST_CODE_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ test_expect("module code cannot be used as a kernel write destination",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_CODE_START, (ULONG) sizeof(ULONG)), TX_FALSE);
+
+ test_expect("a string may be dereferenced out of module code",
+ TXM_MODULE_MANAGER_PARAM_CHECK_DEREFERENCE_STRING(module_instance, (ALIGN_TYPE) TEST_CODE_START), TX_TRUE);
+
+ test_expect("a range running off the end of module code is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_READ(module_instance, (ALIGN_TYPE) (TEST_CODE_END - 3UL), 5UL), TX_FALSE);
+
+ /**********************************************************************/
+ /* The outside direction. */
+ /**********************************************************************/
+
+ test_expect("a kernel object clear of the module is outside it",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) TEST_KERNEL_OBJECT, 64UL), TX_TRUE);
+
+ test_expect("the module's own data is not outside it",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) TEST_DATA_START, 64UL), TX_FALSE);
+
+ test_expect("shared memory is not outside the module",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) TEST_SHARED_RW_START, 64UL), TX_FALSE);
+
+ test_expect("module code is not outside the module",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) TEST_CODE_START, 64UL), TX_FALSE);
+
+ /* A range that reaches into the module only partway is inside neither answer.
+ Reporting it as outside would let it pass as a kernel object, which is the
+ mirror image of the defect this test guards against. */
+ test_expect("a range straddling the end of module data is not outside it",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) (TEST_DATA_END - 3UL), 16UL), TX_FALSE);
+
+ test_expect("a range straddling the guard page after shared memory is not outside the module",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) (TEST_SHARED_RW_END - 3UL), 16UL), TX_FALSE);
+
+ /**********************************************************************/
+ /* Cross-module and application-owned memory. */
+ /**********************************************************************/
+
+ /* Translation answers for whichever context is loaded. If another module's
+ context is loaded, this module's records and the MMU disagree, so neither
+ check may answer at all. */
+ test_current_asid = TEST_OTHER_MODULE_ASID;
+
+ test_expect("nothing is inside the module while another context is loaded",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, (ULONG) sizeof(ULONG)), TX_FALSE);
+
+ test_expect("nothing is provably outside it either",
+ TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, (ALIGN_TYPE) TEST_KERNEL_OBJECT, 64UL), TX_FALSE);
+
+ test_current_asid = TEST_MODULE_ASID;
+
+ test_expect("the module is served again once its own context is loaded",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_DATA_START, (ULONG) sizeof(ULONG)), TX_TRUE);
+
+ /**********************************************************************/
+ /* The bound on how much work one request may ask for. */
+ /**********************************************************************/
+
+ huge_size = ((ULONG) TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES) * ((ULONG) TEST_PAGE_SIZE);
+
+ probes_before = test_probe_count;
+ test_expect("a range one page longer than the maximum is rejected",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SHARED_RW_START, huge_size + 1UL), TX_FALSE);
+ test_expect("...without translating any of it",
+ (UINT) (test_probe_count == probes_before), TX_TRUE);
+
+ test_expect("an over-long range is not outside the module either",
+ _txm_module_manager_outside_data_check(module_instance, (ALIGN_TYPE) TEST_KERNEL_OBJECT, huge_size + 1UL), TX_FALSE);
+
+ /* A range at exactly the maximum is still walked, and the walk stops at the
+ first page that fails rather than translating all of it. */
+ probes_before = test_probe_count;
+ test_expect("a range of exactly the maximum length is walked and rejected here",
+ TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, (ALIGN_TYPE) TEST_SHARED_RW_START, huge_size), TX_FALSE);
+ test_expect("...stopping at the first page that fails",
+ (UINT) ((test_probe_count - probes_before) == 3UL), TX_TRUE);
+
+ /**********************************************************************/
+
+ if (test_failures == 0U)
+ {
+ printf("SUCCESS!\n");
+ return(0);
+ }
+
+ printf("ERROR: %u expectation(s) failed\n", test_failures);
+ return(1);
+}
diff --git a/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c b/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c
index a8477253..95a85ddc 100644
--- a/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c
+++ b/test/tx/module_manager/threadx_module_manager_delete_ownership_test.c
@@ -347,17 +347,56 @@ UINT _txe_mutex_put(TX_MUTEX *mutex_ptr)
/* Stand in for the module port's data range check. */
-UINT _txm_module_manager_inside_data_check(ULONG obj_ptr)
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size, UINT write_request)
{
ULONG data_start;
ULONG data_end;
+ (VOID) module_instance;
+ (VOID) write_request;
+
data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
- if ((obj_ptr >= data_start) && (obj_ptr < data_end))
+ /* The whole range has to lie inside the module's data, not just its first
+ address, and the subtraction is done on the end rather than the start so that
+ a size which would carry the range past the top of the address space cannot
+ wrap into an accepting answer. */
+ if (((ULONG) obj_ptr >= data_start) && (obj_size <= (data_end - (ULONG) obj_ptr)))
+ {
+ return(TX_TRUE);
+ }
+
+ return(TX_FALSE);
+}
+
+
+/* Outside is not the negation of inside: a range that only partly reaches into the
+ module's data is neither. It has to end before that data begins or begin after it
+ ends, and a size that would carry the end past the top of the address space is
+ refused rather than allowed to compare as though it had not. */
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size)
+{
+
+ULONG data_start;
+ULONG data_end;
+
+
+ (VOID) module_instance;
+
+ data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
+ data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
+
+ if (obj_size > (~((ULONG) 0) - (ULONG) obj_ptr))
+ {
+ return(TX_FALSE);
+ }
+
+ if ((((ULONG) obj_ptr + obj_size) <= data_start) || ((ULONG) obj_ptr >= data_end))
{
return(TX_TRUE);
}
diff --git a/test/tx/module_manager/threadx_module_manager_live_object_deallocation_test.c b/test/tx/module_manager/threadx_module_manager_live_object_deallocation_test.c
index 3eaf95c6..a351ce3b 100644
--- a/test/tx/module_manager/threadx_module_manager_live_object_deallocation_test.c
+++ b/test/tx/module_manager/threadx_module_manager_live_object_deallocation_test.c
@@ -318,17 +318,56 @@ UINT _txe_mutex_put(TX_MUTEX *mutex_ptr)
/* Stand in for the module port's data range check. */
-UINT _txm_module_manager_inside_data_check(ULONG obj_ptr)
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size, UINT write_request)
{
ULONG data_start;
ULONG data_end;
+ (VOID) module_instance;
+ (VOID) write_request;
+
data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
- if ((obj_ptr >= data_start) && (obj_ptr < data_end))
+ /* The whole range has to lie inside the module's data, not just its first
+ address, and the subtraction is done on the end rather than the start so that
+ a size which would carry the range past the top of the address space cannot
+ wrap into an accepting answer. */
+ if (((ULONG) obj_ptr >= data_start) && (obj_size <= (data_end - (ULONG) obj_ptr)))
+ {
+ return(TX_TRUE);
+ }
+
+ return(TX_FALSE);
+}
+
+
+/* Outside is not the negation of inside: a range that only partly reaches into the
+ module's data is neither. It has to end before that data begins or begin after it
+ ends, and a size that would carry the end past the top of the address space is
+ refused rather than allowed to compare as though it had not. */
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size)
+{
+
+ULONG data_start;
+ULONG data_end;
+
+
+ (VOID) module_instance;
+
+ data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
+ data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
+
+ if (obj_size > (~((ULONG) 0) - (ULONG) obj_ptr))
+ {
+ return(TX_FALSE);
+ }
+
+ if ((((ULONG) obj_ptr + obj_size) <= data_start) || ((ULONG) obj_ptr >= data_end))
{
return(TX_TRUE);
}
diff --git a/test/tx/module_manager/threadx_module_manager_object_allocate_overflow_test.c b/test/tx/module_manager/threadx_module_manager_object_allocate_overflow_test.c
index 5989ca48..38314ba4 100644
--- a/test/tx/module_manager/threadx_module_manager_object_allocate_overflow_test.c
+++ b/test/tx/module_manager/threadx_module_manager_object_allocate_overflow_test.c
@@ -148,14 +148,30 @@ ULONG _tx_timer_created_count;
/* This test models no module memory of its own, so no address is ever inside the
calling module's data. */
-UINT _txm_module_manager_inside_data_check(ULONG obj_ptr)
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size, UINT write_request)
{
+ (VOID) module_instance;
(VOID) obj_ptr;
+ (VOID) obj_size;
+ (VOID) write_request;
return(TX_FALSE);
}
+/* No module data is modelled here, so every range lies outside it. */
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size)
+{
+ (VOID) module_instance;
+ (VOID) obj_ptr;
+ (VOID) obj_size;
+
+ return(TX_TRUE);
+}
+
+
/* Not reached from the allocation path under test. */
VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size,
ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment)
diff --git a/test/tx/module_manager/threadx_module_manager_object_authentication_test.c b/test/tx/module_manager/threadx_module_manager_object_authentication_test.c
index 6c79efae..396f57f2 100644
--- a/test/tx/module_manager/threadx_module_manager_object_authentication_test.c
+++ b/test/tx/module_manager/threadx_module_manager_object_authentication_test.c
@@ -330,17 +330,56 @@ UINT _txe_mutex_put(TX_MUTEX *mutex_ptr)
/* Stand in for the module port's data range check, which the portable
outside-the-module test is built on. */
-UINT _txm_module_manager_inside_data_check(ULONG obj_ptr)
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size, UINT write_request)
{
ULONG data_start;
ULONG data_end;
+ (VOID) module_instance;
+ (VOID) write_request;
+
data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
- if ((obj_ptr >= data_start) && (obj_ptr < data_end))
+ /* The whole range has to lie inside the module's data, not just its first
+ address, and the subtraction is done on the end rather than the start so that
+ a size which would carry the range past the top of the address space cannot
+ wrap into an accepting answer. */
+ if (((ULONG) obj_ptr >= data_start) && (obj_size <= (data_end - (ULONG) obj_ptr)))
+ {
+ return(TX_TRUE);
+ }
+
+ return(TX_FALSE);
+}
+
+
+/* Outside is not the negation of inside: a range that only partly reaches into the
+ module's data is neither. It has to end before that data begins or begin after it
+ ends, and a size that would carry the end past the top of the address space is
+ refused rather than allowed to compare as though it had not. */
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size)
+{
+
+ULONG data_start;
+ULONG data_end;
+
+
+ (VOID) module_instance;
+
+ data_start = (ULONG) (ALIGN_TYPE) test_module_data.test_data_bytes;
+ data_end = data_start + ((ULONG) TEST_MODULE_MEMORY_BYTES);
+
+ if (obj_size > (~((ULONG) 0) - (ULONG) obj_ptr))
+ {
+ return(TX_FALSE);
+ }
+
+ if ((((ULONG) obj_ptr + obj_size) <= data_start) || ((ULONG) obj_ptr >= data_end))
{
return(TX_TRUE);
}
diff --git a/test/tx/module_manager/threadx_module_manager_thread_kernel_stack_test.c b/test/tx/module_manager/threadx_module_manager_thread_kernel_stack_test.c
index e971b159..1ab91145 100644
--- a/test/tx/module_manager/threadx_module_manager_thread_kernel_stack_test.c
+++ b/test/tx/module_manager/threadx_module_manager_thread_kernel_stack_test.c
@@ -252,15 +252,30 @@ ULONG index;
/* Stand in for the module port's data-range check. Nothing here is inside a module's
own data, so every address the manager asks about is outside it. */
-UINT _txm_module_manager_inside_data_check(ULONG obj_ptr)
+UINT _txm_module_manager_inside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size, UINT write_request)
{
-
+ (VOID) module_instance;
(VOID) obj_ptr;
+ (VOID) obj_size;
+ (VOID) write_request;
return(TX_FALSE);
}
+/* No module data is modelled here, so every range lies outside it. */
+UINT _txm_module_manager_outside_data_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE obj_ptr,
+ ULONG obj_size)
+{
+ (VOID) module_instance;
+ (VOID) obj_ptr;
+ (VOID) obj_size;
+
+ return(TX_TRUE);
+}
+
+
/* Stand in for the module port's alignment adjustment. It is referenced by a
loading path this test does not drive, and is never reached from here. */
VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size,