mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Merge commit from fork
* Fixed the Cortex-A7 module data check to validate whole ranges The Cortex-A7 module port received a module instance, a start address and a byte size from the common Module Manager, then discarded the instance and the size and asked the MMU to translate the start address alone, for reading only. A range was therefore accepted whenever its first byte happened to be readable by the module, so privileged dispatch code could read or write past the end of the module's mapping, or use read-only module code as a write destination. The check now takes the size and an access intent. The module's own data region is answered from the manager's records, which name it exactly and cost no translations; everything else is answered by translating every page the range touches with the requested unprivileged access. Empty ranges, ranges whose last byte would wrap, and ranges that leave the recorded data region partway through are all refused, and the walk is bounded by TXM_MODULE_MANAGER_DATA_CHECK_MAX_PAGES so one module request cannot impose unbounded work on the kernel. Translation is only believed while the requesting module's own context is loaded. The outside direction gets its own check rather than negating the inside one: a range that reaches into the module only partway is inside neither answer, and negating a whole-range check would have let it pass as a kernel object. Other ports keep their single data check through backward-compatible fallbacks in the common header; their preprocessed dispatch output is byte-identical. Regression coverage runs on the host by standing a simulated page map behind the one architecture primitive, and reaches 100% line, branch and call coverage of the new logic. Twenty-four of its expectations fail against the previous implementation. Assisted-by: Claude Code (Opus 5) * Drove the Cortex-A7 range check through a live MMU The host test for this check stands a simulated page map behind the port's CP15 primitive, so it never executes an address translation. That leaves the part most easily got wrong unexercised: an encoding naming the wrong operation, or a PAR fault bit read the wrong way round, passes it without complaint. This adds a bare-metal image that builds a short-descriptor translation table, enables the MMU, and drives the real check through the real translations on a real Cortex-A7 translation regime, plus a script that builds and runs it under an emulator. Sections are mapped for unprivileged read/write, unprivileged read only, and privileged only, with an unmapped section behind the read-only one so that a range can be made to leave its mapping partway through. That last case is the one the replaced check accepted, and the test asserts both answers against the same live MMU: the current check rejects the range, and translating only its first address accepts it. It also confirms what the simulated map could only assume, that ATS1CUW denies a write to a read-only mapping while ATS1CUR allows the read. The write intent is the reason the port asks for two translations rather than one. The script skips with a notice when the cross toolchain or the emulator is absent, so a machine without them does not fail the build. Assisted-by: Claude Code (Opus 5)
This commit is contained in:
@@ -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)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user