diff --git a/common_modules/module_manager/inc/txm_module_manager_dispatch.h b/common_modules/module_manager/inc/txm_module_manager_dispatch.h index 3763d35d..9c0e5d21 100644 --- a/common_modules/module_manager/inc/txm_module_manager_dispatch.h +++ b/common_modules/module_manager/inc/txm_module_manager_dispatch.h @@ -10,6 +10,8 @@ **************************************************************************/ // Portions of this file were generated with AI assistance. +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -34,7 +36,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BLOCK_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BLOCK_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(VOID *))) @@ -102,7 +104,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BLOCK_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BLOCK_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -136,7 +138,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BLOCK_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BLOCK_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -189,7 +191,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BLOCK_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BLOCK_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -270,7 +272,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BLOCK_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BLOCK_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -336,7 +338,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BYTE_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BYTE_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(VOID *))) @@ -406,7 +408,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BYTE_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BYTE_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -440,7 +442,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BYTE_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BYTE_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -496,7 +498,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BYTE_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BYTE_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -604,7 +606,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_BYTE_POOL))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_BYTE_POOL_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -696,7 +698,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -728,7 +730,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, extra_parameters[1], sizeof(ULONG))) @@ -765,7 +767,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -814,7 +816,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -897,7 +899,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -924,7 +926,7 @@ VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *); if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_EVENT_FLAGS_GROUP))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_EVENT_FLAGS_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* Since we need to write to the object, ensure it's valid. */ @@ -1001,7 +1003,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1030,7 +1032,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1059,7 +1061,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -1114,7 +1116,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -1213,7 +1215,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1235,7 +1237,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_MUTEX))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_MUTEX_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1298,7 +1300,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1326,7 +1328,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1351,7 +1353,7 @@ TX_QUEUE *queue_ptr; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* We need to get the size of the message from the queue. */ @@ -1386,7 +1388,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -1441,7 +1443,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -1540,7 +1542,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1565,7 +1567,7 @@ TX_QUEUE *queue_ptr; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* We need to get the max size of the buffer from the queue. */ @@ -1597,7 +1599,7 @@ TX_QUEUE *queue_ptr; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* We need to get the size of the message from the queue. */ @@ -1629,7 +1631,7 @@ VOID (*queue_send_notify)(TX_QUEUE *); if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_QUEUE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_QUEUE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* Since we need to write to the object, ensure it's valid. */ @@ -1673,7 +1675,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1730,7 +1732,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1759,7 +1761,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1787,7 +1789,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -1836,7 +1838,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -1917,7 +1919,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1939,7 +1941,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -1964,7 +1966,7 @@ VOID (*semaphore_put_notify)(TX_SEMAPHORE *); if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_SEMAPHORE))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_SEMAPHORE_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* Since we need to write to the object, ensure it's valid. */ @@ -2071,7 +2073,7 @@ ALIGN_TYPE stack_status; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2121,7 +2123,7 @@ VOID (*thread_entry_exit_notify)(TX_THREAD *, UINT); if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); /* Since we need to write to the object, ensure it's valid. */ @@ -2188,7 +2190,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -2274,7 +2276,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) @@ -2421,7 +2423,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_2, sizeof(UINT))) @@ -2455,7 +2457,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_2, sizeof(UINT))) @@ -2492,7 +2494,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2514,7 +2516,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2571,7 +2573,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2600,7 +2602,7 @@ TX_THREAD *thread_ptr; { return(TXM_MODULE_INVALID_MEMORY); } - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2650,7 +2652,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2674,7 +2676,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_2, sizeof(ULONG))) @@ -2701,7 +2703,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_THREAD))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_THREAD_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2749,7 +2751,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2773,7 +2775,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2870,7 +2872,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2892,7 +2894,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); } @@ -2925,7 +2927,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(CHAR *))) @@ -2975,7 +2977,7 @@ ALIGN_TYPE return_value; if (module_instance -> txm_module_instance_property_flags & TXM_MODULE_MEMORY_PROTECTION) { - if (!TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, param_0, sizeof(TX_TIMER))) + if (!TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, param_0, TXM_TIMER_OBJECT)) return(TXM_MODULE_INVALID_MEMORY); if (!TXM_MODULE_MANAGER_PARAM_CHECK_BUFFER_WRITE(module_instance, param_1, sizeof(ULONG))) 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 45c7f409..77631bea 100644 --- a/common_modules/module_manager/inc/txm_module_manager_util.h +++ b/common_modules/module_manager/inc/txm_module_manager_util.h @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -89,10 +91,31 @@ ((TXM_MODULE_MANAGER_ENSURE_INSIDE_MODULE_DATA(module_instance, buffer_ptr, buffer_size)) || \ ((void *) (buffer_ptr) == TX_NULL)) -/* Kernel objects should be outside the module at the very least. */ +/* Kernel objects a module names must be authenticated before a privileged service is + allowed to dereference them. Being outside the module is necessary but nowhere near + sufficient: the manager's object pool is outside every module, so an address shifted + into the interior of one of the module's own privileged allocations satisfies that + test while denoting no object at all. Authentication answers the question the + location test cannot -- is this the exact address of a live kernel object of the type + this service expects -- and it answers it from manager and kernel bookkeeping rather + than from fields of the purported object, which a module can influence. + + TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE is the complete check and is what + the dispatch table uses. It is given the object type rather than a control block size + so that it can also establish the type, which a size cannot: distinct object types of + equal size exist. + + TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE is retained for dispatchers outside this + repository that pass a size. It authenticates addresses in the manager's object pool, + which is what the shifted-pointer attack needs, but without a type it can neither + establish the type nor authenticate an application-owned object, so in-repository code + should use the typed form. */ #define TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_USE(module_instance, obj_ptr, obj_size) \ (_txm_module_manager_param_check_object_for_use(module_instance, obj_ptr, obj_size)) +#define TXM_MODULE_MANAGER_PARAM_CHECK_TYPED_OBJECT_FOR_USE(module_instance, obj_ptr, obj_type) \ + (_txm_module_manager_param_check_typed_object_for_use(module_instance, obj_ptr, obj_type)) + /* When creating an object, the object must be inside the object pool. */ #define TXM_MODULE_MANAGER_PARAM_CHECK_OBJECT_FOR_CREATION(module_instance, obj_ptr, obj_size) \ (_txm_module_manager_param_check_object_for_creation(module_instance, obj_ptr, obj_size)) @@ -122,6 +145,11 @@ UINT _txm_module_manager_object_name_compare(CHAR *object_name1, UINT object_ UCHAR _txm_module_manager_created_object_check(TXM_MODULE_INSTANCE *module_instance, void *object_ptr); UINT _txm_module_manager_param_check_object_for_creation(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, ULONG object_size); UINT _txm_module_manager_param_check_object_for_use(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, ULONG object_size); +UINT _txm_module_manager_param_check_typed_object_for_use(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, UINT object_type); +UINT _txm_module_manager_allocated_object_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, ULONG object_size); +UINT _txm_module_manager_object_type_size_get(UINT object_type, ULONG *object_size); +UINT _txm_module_manager_created_object_type_check(ALIGN_TYPE object_ptr, UINT object_type); +UINT _txm_module_manager_object_id_check(ALIGN_TYPE object_ptr, UINT object_type); UINT _txm_module_manager_util_code_allocation_size_and_alignment_get(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_alignment_dest, ULONG *code_allocation_size_dest); #endif diff --git a/common_modules/module_manager/src/txm_module_manager_object_deallocate.c b/common_modules/module_manager/src/txm_module_manager_object_deallocate.c index 95e28e4d..c34a0005 100644 --- a/common_modules/module_manager/src/txm_module_manager_object_deallocate.c +++ b/common_modules/module_manager/src/txm_module_manager_object_deallocate.c @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -125,6 +127,27 @@ UINT return_value; } } + /* Clear the first word of the object being given back, which for every kernel + object is its control block ID. + + Object authentication reads that ID to establish the type of an object and + that it is still created, having first established that the address is the + exact start of an allocation. The kernel clears the ID when it deletes an + object, so the ordinary sequence of delete and then deallocate has already + cleared it. What has not is a deallocation of an object that was never + deleted: the memory returns to the pool still carrying a valid ID, and the + next allocation to be placed there is a raw allocation that presents one. + Clearing it here means the manager stops vouching for a control block at + the moment it stops owning the memory, whatever order the module chose. + + An allocation too small to hold an ID cannot be presenting one, and is + left alone rather than written past its end. */ + if (module_allocated_object_ptr -> txm_module_object_size >= ((ULONG) sizeof(ULONG))) + { + + *((ULONG *) object_ptr) = TX_CLEAR_ID; + } + /* Release the object memory. */ return_value = (ULONG) _txe_byte_release((VOID *) module_allocated_object_ptr); } diff --git a/common_modules/module_manager/src/txm_module_manager_thread_reset.c b/common_modules/module_manager/src/txm_module_manager_thread_reset.c index 75dd4a49..1a33964e 100644 --- a/common_modules/module_manager/src/txm_module_manager_thread_reset.c +++ b/common_modules/module_manager/src/txm_module_manager_thread_reset.c @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -106,6 +108,20 @@ TXM_MODULE_THREAD_ENTRY_INFO *thread_entry_info; status = TX_NOT_DONE; } } + + /* Resetting a thread means rebuilding its stack around a module's shell entry + function, so the thread has to be one a module manager created. A thread the + application created carries no module instance, and the fields this function + goes on to read from it -- the shell entry function it builds the new stack + frame around -- would be read through a null pointer in privileged mode. A + module can name such a thread: any thread the system created can be found by + name, and one that has run to completion satisfies the state test above. */ + if (thread_ptr -> tx_thread_module_instance_ptr == TX_NULL) + { + + /* Not a module thread, so there is nothing here to reset. */ + status = TX_NOT_DONE; + } } /* Is the request valid? */ diff --git a/common_modules/module_manager/src/txm_module_manager_util.c b/common_modules/module_manager/src/txm_module_manager_util.c index 78031b5f..1b60f900 100644 --- a/common_modules/module_manager/src/txm_module_manager_util.c +++ b/common_modules/module_manager/src/txm_module_manager_util.c @@ -9,6 +9,8 @@ * SPDX-License-Identifier: MIT **************************************************************************/ +// Some portions generated by Claude Code (Opus 5). + /**************************************************************************/ /**************************************************************************/ @@ -25,6 +27,15 @@ #define TX_SOURCE_CODE +#include "tx_api.h" +#include "tx_thread.h" +#include "tx_timer.h" +#include "tx_queue.h" +#include "tx_event_flags.h" +#include "tx_semaphore.h" +#include "tx_mutex.h" +#include "tx_block_pool.h" +#include "tx_byte_pool.h" #include "txm_module.h" #include "txm_module_manager_util.h" @@ -461,11 +472,414 @@ UINT _txm_module_manager_param_check_object_for_use(TXM_MODULE_INSTANCE *modu return(TX_FALSE); } - /* Determine if the object pointer is inside the module object pool. */ + /* Determine if the object is outside the calling module. */ if (TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, object_ptr, object_size) == TX_FALSE) { - /* Object pointer is not inside the object pool, which is invalid. */ + /* Object pointer is not outside the module, which is invalid. */ + return(TX_FALSE); + } + + /* Being outside the module does not make an address an object. The manager's + object pool is outside every module, so an address shifted into the interior of + one of this module's own privileged allocations satisfies the test above while + denoting no object at all, and the bytes the shifted address then presents as a + control block are bytes the module chose through ordinary create and set + services. An address in the object pool is therefore only usable if it is the + exact address the manager handed out for an allocation of this size. + + This function is given a size rather than a type, so it can go no further than + that: it cannot establish which type of object it is, and it cannot authenticate + an application-owned object outside the pool. Dispatchers in this repository use + _txm_module_manager_param_check_typed_object_for_use, which does both. */ + if ((_txm_module_manager_object_pool_created == TX_TRUE) && + (object_ptr >= (ALIGN_TYPE) _txm_module_manager_object_pool.tx_byte_pool_start) && + (object_ptr < (ALIGN_TYPE) (_txm_module_manager_object_pool.tx_byte_pool_start + _txm_module_manager_object_pool.tx_byte_pool_size))) + { + + if (_txm_module_manager_allocated_object_check(module_instance, object_ptr, object_size) == TX_FALSE) + { + + /* An address in the object pool that is not the exact start of one of this + module's allocations, which is invalid. */ + return(TX_FALSE); + } + } + + /* Define application-specific object memory check. */ +#ifdef TXM_MODULE_MANGER_APPLICATION_VALID_OBJECT_MEMORY_CHECK + + /* Bring in the application-spefic objeft memory check, defined by the user. */ + TXM_MODULE_MANGER_APPLICATION_VALID_OBJECT_MEMORY_CHECK +#endif /* TXM_MODULE_MANGER_APPLICATION_VALID_OBJECT_MEMORY_ENABLE */ + + /* Everything is okay with the object, return TX_TRUE. */ + return(TX_TRUE); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_object_type_size_get PORTABLE C */ +/* 6.4.3 */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function returns the size of the control block belonging to a */ +/* module object type. It is the size a service of that type is */ +/* entitled to dereference, and the size the manager recorded when it */ +/* allocated object memory for that type. */ +/* */ +/* INPUT */ +/* */ +/* object_type Module object type */ +/* object_size Destination for the control block */ +/* size of that type */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TRUE Known object type */ +/* TX_FALSE Unknown object type */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_param_check_typed_object_for_use */ +/* Module object authentication */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* xx-xx-2026 Eclipse ThreadX Initial Version 6.4.3 */ +/* contributors */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_object_type_size_get(UINT object_type, ULONG *object_size) +{ + +UINT status; + + + /* Assume the type is one this manager knows. */ + status = TX_TRUE; + + switch (object_type) + { + + case TXM_BLOCK_POOL_OBJECT: + + *object_size = (ULONG) sizeof(TX_BLOCK_POOL); + break; + + case TXM_BYTE_POOL_OBJECT: + + *object_size = (ULONG) sizeof(TX_BYTE_POOL); + break; + + case TXM_EVENT_FLAGS_OBJECT: + + *object_size = (ULONG) sizeof(TX_EVENT_FLAGS_GROUP); + break; + + case TXM_MUTEX_OBJECT: + + *object_size = (ULONG) sizeof(TX_MUTEX); + break; + + case TXM_QUEUE_OBJECT: + + *object_size = (ULONG) sizeof(TX_QUEUE); + break; + + case TXM_SEMAPHORE_OBJECT: + + *object_size = (ULONG) sizeof(TX_SEMAPHORE); + break; + + case TXM_THREAD_OBJECT: + + *object_size = (ULONG) sizeof(TX_THREAD); + break; + + case TXM_TIMER_OBJECT: + + *object_size = (ULONG) sizeof(TX_TIMER); + break; + + default: + + /* Not a type the manager authenticates. Report it rather than guessing a + size, so that a caller cannot be given a range to validate that has no + relationship to the object the service will dereference. */ + *object_size = ((ULONG) 0); + status = TX_FALSE; + break; + } + + return(status); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_allocated_object_check PORTABLE C */ +/* 6.4.3 */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function determines whether an address is the exact address */ +/* the manager handed the specified module for one of its object */ +/* allocations, and whether that allocation is the size the caller */ +/* expects. */ +/* */ +/* The allocation list is the manager's own record, built as it hands */ +/* object memory out. Comparing against it is what makes an address */ +/* inside the object pool distinguishable from the exact start of an */ +/* allocation. Reading a header in front of a candidate address cannot */ +/* make that distinction: the bytes in front of an address chosen */ +/* inside an allocation are part of the object, and a module can place */ +/* values there through ordinary create and set services. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Requesting module instance pointer*/ +/* object_ptr Address of object memory area */ +/* object_size Expected size of the allocation */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TRUE An exact allocation of that size */ +/* TX_FALSE Anything else */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_param_check_object_for_use */ +/* Module object authentication */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* xx-xx-2026 Eclipse ThreadX Initial Version 6.4.3 */ +/* contributors */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_allocated_object_check(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, ULONG object_size) +{ + +TX_INTERRUPT_SAVE_AREA + +TXM_MODULE_ALLOCATED_OBJECT *allocated_object_ptr; +ULONG objects_examined; +UINT status; + + + /* Assume the address is not one of this module's allocations. */ + status = TX_FALSE; + + /* Disable interrupts. The allocation list is maintained by threads holding the + manager protection mutex, so a scan that runs to completion with interrupts + disabled cannot observe it being changed, and unlike taking the mutex it adds + no blocking point and no priority inversion to a kernel request. */ + TX_DISABLE + + allocated_object_ptr = module_instance -> txm_module_instance_object_list_head; + objects_examined = ((ULONG) 0); + + /* Loop through the objects allocated to this module. The loop is bounded by the + count the manager maintains alongside the list, so the cost of one check is + bounded by the number of objects this module has allocated, and a list whose + links have been damaged cannot make the scan run on. */ + while ((objects_examined < module_instance -> txm_module_instance_object_list_count) && + (allocated_object_ptr != TX_NULL)) + { + + /* The address the module was given is the one immediately after the private + header, so that is the only address in this allocation that names it. */ + if (((ALIGN_TYPE) (allocated_object_ptr + 1)) == object_ptr) + { + + /* Is the allocation the size the caller expects? An allocation made for a + smaller object does not become a larger one because a service was asked + to treat it as one. */ + if (allocated_object_ptr -> txm_module_object_size == object_size) + { + + status = TX_TRUE; + } + + /* An address matches at most one allocation, so there is nothing further + to look at either way. */ + break; + } + + /* Move to the next allocated object. */ + objects_examined++; + allocated_object_ptr = allocated_object_ptr -> txm_module_allocated_object_next; + } + + /* Restore interrupts. */ + TX_RESTORE + + return(status); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_param_check_typed_object_for_use */ +/* PORTABLE C */ +/* 6.4.3 */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function authenticates a kernel object a module has named, */ +/* before a privileged service is allowed to dereference it. It */ +/* establishes that the address is the exact address of a live kernel */ +/* object of the type the service expects. */ +/* */ +/* The address is accepted only if it is on the kernel's created list */ +/* for that type. That list is the record the create and delete */ +/* services maintain, so being on it establishes at once that the */ +/* address is an object start rather than an address inside an object, */ +/* that the object is of this type, and that it has not been deleted. */ +/* It is also how an object the application created and shared with a */ +/* module is authenticated, since the manager allocated no such object */ +/* and has no record of its own to consult. */ +/* */ +/* Nothing a module can influence is used to establish a type. In */ +/* particular the control block ID is not, on its own, evidence of */ +/* anything: a module can arrange for the value of an ID to appear */ +/* inside an object it legitimately owns, which is what lets a shifted */ +/* pointer pass an ID test, and distinct object types of equal size */ +/* exist, so an ID is not even a type at an address known to be an */ +/* object start. It is checked, after the created list has settled the */ +/* question, because it is the check the _txe_ services make and it is */ +/* compiled away with them under TX_DISABLE_ERROR_CHECKING. */ +/* */ +/* INPUT */ +/* */ +/* module_instance Requesting module instance pointer*/ +/* object_ptr Address of object memory area */ +/* object_type Module object type the service */ +/* expects */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TRUE Authenticated object pointer */ +/* TX_FALSE Invalid object pointer */ +/* */ +/* CALLS */ +/* */ +/* _txm_module_manager_object_type_size_get */ +/* Get control block size */ +/* _txm_module_manager_created_object_type_check */ +/* Check kernel created list */ +/* _txm_module_manager_object_id_check Check control block ID */ +/* */ +/* CALLED BY */ +/* */ +/* txm_module_manager_* Module manager functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* xx-xx-2026 Eclipse ThreadX Initial Version 6.4.3 */ +/* contributors */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_param_check_typed_object_for_use(TXM_MODULE_INSTANCE *module_instance, ALIGN_TYPE object_ptr, UINT object_type) +{ + +ULONG object_size; +UINT exact_object; + + + /* Determine if the object pointer is NULL. */ + if ((void *) object_ptr == TX_NULL) + { + + /* Object pointer is NULL, which is invalid. */ + return(TX_FALSE); + } + + /* Pickup the size of the control block this type of service dereferences. */ + if (_txm_module_manager_object_type_size_get(object_type, &object_size) == TX_FALSE) + { + + /* Not an object type this manager authenticates, so it cannot be used. */ + return(TX_FALSE); + } + + /* Determine if the object is outside the calling module. A kernel object inside + the module's own memory is one the module can write behind the kernel's back, + so it is rejected here as it always has been. This also rejects a size that + wraps when added to the address. */ + if (TXM_MODULE_MANAGER_ENSURE_OUTSIDE_MODULE(module_instance, object_ptr, object_size) == TX_FALSE) + { + + /* Object pointer is not outside the module, which is invalid. */ + return(TX_FALSE); + } + + /* Establish that the address is the exact address of a live object of this type, + from the kernel's created list for the type. That list is the record the create + and delete services maintain, so being on it establishes at once that the + address is an object start, that the object is of this type, and that it has not + been deleted. + + It is deliberately the only ground on which an address is accepted. The + manager's own allocation list would establish the address and the size of an + object a module allocated, and would do it by walking a shorter list, but it + records no type, and the type cannot then be taken from the control block + itself: distinct object types of equal size exist -- on a 32-bit target a byte + pool, a mutex and a timer are all the same size -- so a control block whose ID + has been made to read as one of them would be accepted as that type. Nothing a + module can influence is used to establish a type. + + The address is not dereferenced to reach this decision. An address a module + named is not read until a kernel record says there is an object there. */ + exact_object = _txm_module_manager_created_object_type_check(object_ptr, object_type); + + if (exact_object == TX_FALSE) + { + + /* The address is not the start of a live object of this type. */ + return(TX_FALSE); + } + + /* The address is a live object of the requested type. Confirm it against the + control block's own ID, which is the check the _txe_ services would make and + which is compiled away with them under TX_DISABLE_ERROR_CHECKING. */ + if (_txm_module_manager_object_id_check(object_ptr, object_type) == TX_FALSE) + { + + /* The created list and the control block disagree, so the object is not in a + state the manager is prepared to vouch for. */ return(TX_FALSE); } @@ -481,6 +895,402 @@ UINT _txm_module_manager_param_check_object_for_use(TXM_MODULE_INSTANCE *modu } +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_created_object_type_check PORTABLE C */ +/* 6.4.3 */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function determines whether an address is the exact address */ +/* of an object on the kernel's created list for a module object type. */ +/* */ +/* This is how an object the application created and shared with a */ +/* module is authenticated. Such an object is not in the manager's */ +/* object pool and the manager has no allocation record of it, so the */ +/* kernel's own created list is the only record of it that a module */ +/* cannot influence. Being on that list establishes at once that the */ +/* address is an object start, that the object is of this type, and */ +/* that it is created. */ +/* */ +/* INPUT */ +/* */ +/* object_ptr Address of object memory area */ +/* object_type Module object type */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TRUE A created object of that type */ +/* TX_FALSE Anything else */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_param_check_typed_object_for_use */ +/* Module object authentication */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* xx-xx-2026 Eclipse ThreadX Initial Version 6.4.3 */ +/* contributors */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_created_object_type_check(ALIGN_TYPE object_ptr, UINT object_type) +{ + +TX_INTERRUPT_SAVE_AREA + +ULONG objects_examined; +ULONG objects_created; +ALIGN_TYPE candidate_ptr; +UINT status; +TX_BLOCK_POOL *block_pool_ptr; +TX_BYTE_POOL *byte_pool_ptr; +TX_EVENT_FLAGS_GROUP *event_flags_ptr; +TX_MUTEX *mutex_ptr; +TX_QUEUE *queue_ptr; +TX_SEMAPHORE *semaphore_ptr; +TX_THREAD *thread_ptr; +TX_TIMER *timer_ptr; + + + /* Assume the address is not on the created list for this type. */ + status = TX_FALSE; + + /* Disable interrupts. The created lists are maintained with interrupts disabled, + so a scan that runs to completion this way sees a consistent list, and it adds + no blocking point to a kernel request. The cost of one check is bounded by the + number of objects of this type the system has created, which is the price of + authenticating an object the manager did not allocate; a module using its own + allocated objects does not reach this function. */ + TX_DISABLE + + /* Start each list from its head and pick up the count that bounds the walk, so a + list whose links have been damaged cannot make the scan run on. */ + switch (object_type) + { + + case TXM_BLOCK_POOL_OBJECT: + + block_pool_ptr = _tx_block_pool_created_ptr; + objects_created = _tx_block_pool_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (block_pool_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) block_pool_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + block_pool_ptr = block_pool_ptr -> tx_block_pool_created_next; + } + break; + + case TXM_BYTE_POOL_OBJECT: + + byte_pool_ptr = _tx_byte_pool_created_ptr; + objects_created = _tx_byte_pool_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (byte_pool_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) byte_pool_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + byte_pool_ptr = byte_pool_ptr -> tx_byte_pool_created_next; + } + break; + + case TXM_EVENT_FLAGS_OBJECT: + + event_flags_ptr = _tx_event_flags_created_ptr; + objects_created = _tx_event_flags_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (event_flags_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) event_flags_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + event_flags_ptr = event_flags_ptr -> tx_event_flags_group_created_next; + } + break; + + case TXM_MUTEX_OBJECT: + + mutex_ptr = _tx_mutex_created_ptr; + objects_created = _tx_mutex_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (mutex_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) mutex_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + mutex_ptr = mutex_ptr -> tx_mutex_created_next; + } + break; + + case TXM_QUEUE_OBJECT: + + queue_ptr = _tx_queue_created_ptr; + objects_created = _tx_queue_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (queue_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) queue_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + queue_ptr = queue_ptr -> tx_queue_created_next; + } + break; + + case TXM_SEMAPHORE_OBJECT: + + semaphore_ptr = _tx_semaphore_created_ptr; + objects_created = _tx_semaphore_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (semaphore_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) semaphore_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + semaphore_ptr = semaphore_ptr -> tx_semaphore_created_next; + } + break; + + case TXM_THREAD_OBJECT: + + thread_ptr = _tx_thread_created_ptr; + objects_created = _tx_thread_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (thread_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) thread_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + thread_ptr = thread_ptr -> tx_thread_created_next; + } + break; + + case TXM_TIMER_OBJECT: + + timer_ptr = _tx_timer_created_ptr; + objects_created = _tx_timer_created_count; + objects_examined = ((ULONG) 0); + + while ((objects_examined < objects_created) && (timer_ptr != TX_NULL)) + { + candidate_ptr = (ALIGN_TYPE) timer_ptr; + if (candidate_ptr == object_ptr) + { + status = TX_TRUE; + break; + } + objects_examined++; + timer_ptr = timer_ptr -> tx_timer_created_next; + } + break; + + default: + + /* Not a type the manager authenticates. */ + break; + } + + /* Restore interrupts. */ + TX_RESTORE + + return(status); +} + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _txm_module_manager_object_id_check PORTABLE C */ +/* 6.4.3 */ +/* AUTHOR */ +/* */ +/* Eclipse ThreadX contributors */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function determines whether the control block at an object */ +/* start carries the ID of a module object type. The kernel writes */ +/* that ID when it creates an object and clears it when it deletes */ +/* one, so at an address already established to be an object start the */ +/* ID reports both the type and whether the object is still created. */ +/* */ +/* This check must not be used on its own to decide that an address is */ +/* an object. A module can place the value of an ID inside an object */ +/* it legitimately owns, so an ID found at an address the manager has */ +/* not otherwise authenticated proves nothing. */ +/* */ +/* The check matters most where the error checking layer is absent: */ +/* with TX_DISABLE_ERROR_CHECKING the _txe_ services that would */ +/* otherwise test the ID are compiled away, and this is then the only */ +/* ID test between a module and a privileged dereference. */ +/* */ +/* INPUT */ +/* */ +/* object_ptr Address of an object start */ +/* object_type Module object type */ +/* */ +/* OUTPUT */ +/* */ +/* TX_TRUE A created object of that type */ +/* TX_FALSE Anything else */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _txm_module_manager_param_check_typed_object_for_use */ +/* Module object authentication */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* xx-xx-2026 Eclipse ThreadX Initial Version 6.4.3 */ +/* contributors */ +/* */ +/**************************************************************************/ +UINT _txm_module_manager_object_id_check(ALIGN_TYPE object_ptr, UINT object_type) +{ + +TX_INTERRUPT_SAVE_AREA + +ULONG object_id; +ULONG expected_id; +UINT status; + + + /* Read the ID through a pointer to the type the caller named, so that the field + read is the one that type declares rather than an assumed offset. */ + TX_DISABLE + + switch (object_type) + { + + case TXM_BLOCK_POOL_OBJECT: + + object_id = ((TX_BLOCK_POOL *) object_ptr) -> tx_block_pool_id; + expected_id = TX_BLOCK_POOL_ID; + break; + + case TXM_BYTE_POOL_OBJECT: + + object_id = ((TX_BYTE_POOL *) object_ptr) -> tx_byte_pool_id; + expected_id = TX_BYTE_POOL_ID; + break; + + case TXM_EVENT_FLAGS_OBJECT: + + object_id = ((TX_EVENT_FLAGS_GROUP *) object_ptr) -> tx_event_flags_group_id; + expected_id = TX_EVENT_FLAGS_ID; + break; + + case TXM_MUTEX_OBJECT: + + object_id = ((TX_MUTEX *) object_ptr) -> tx_mutex_id; + expected_id = TX_MUTEX_ID; + break; + + case TXM_QUEUE_OBJECT: + + object_id = ((TX_QUEUE *) object_ptr) -> tx_queue_id; + expected_id = TX_QUEUE_ID; + break; + + case TXM_SEMAPHORE_OBJECT: + + object_id = ((TX_SEMAPHORE *) object_ptr) -> tx_semaphore_id; + expected_id = TX_SEMAPHORE_ID; + break; + + case TXM_THREAD_OBJECT: + + object_id = ((TX_THREAD *) object_ptr) -> tx_thread_id; + expected_id = TX_THREAD_ID; + break; + + case TXM_TIMER_OBJECT: + + object_id = ((TX_TIMER *) object_ptr) -> tx_timer_id; + expected_id = TX_TIMER_ID; + break; + + default: + + /* Not a type the manager authenticates. Choose values that cannot match. */ + object_id = TX_CLEAR_ID; + expected_id = ~((ULONG) TX_CLEAR_ID); + break; + } + + /* Restore interrupts. */ + TX_RESTORE + + if (object_id == expected_id) + { + + status = TX_TRUE; + } + else + { + + status = TX_FALSE; + } + + return(status); +} + + /**************************************************************************/ /* */ /* FUNCTION RELEASE */ diff --git a/test/tx/cmake/module_manager/CMakeLists.txt b/test/tx/cmake/module_manager/CMakeLists.txt index 38808a26..35a6f8bb 100644 --- a/test/tx/cmake/module_manager/CMakeLists.txt +++ b/test/tx/cmake/module_manager/CMakeLists.txt @@ -78,3 +78,28 @@ target_compile_options( add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_thread_kernel_stack_test threadx_module_manager_thread_kernel_stack_test) + +# This test drives the manager services directly rather than through a dispatcher, +# so it needs none of the dispatch table and no guard list. +add_executable( + threadx_module_manager_object_authentication_test + ${SOURCE_DIR}/threadx_module_manager_object_authentication_test.c + ${module_manager_dir}/src/txm_module_manager_util.c + ${module_manager_dir}/src/txm_module_manager_object_allocate.c + ${module_manager_dir}/src/txm_module_manager_object_deallocate.c + ${module_manager_dir}/src/txm_module_manager_thread_reset.c) + +target_include_directories( + threadx_module_manager_object_authentication_test + PRIVATE ${SOURCE_DIR} + ${REPO_ROOT}/common/inc + ${REPO_ROOT}/common_modules/inc + ${module_manager_dir}/inc + ${cortex_a7_module_dir}/inc) + +target_compile_options( + threadx_module_manager_object_authentication_test + PRIVATE -include ${SOURCE_DIR}/threadx_module_manager_host_test_port.h) + +add_test(${CMAKE_BUILD_TYPE}::threadx_module_manager_object_authentication_test + threadx_module_manager_object_authentication_test) 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 new file mode 100644 index 00000000..ca583956 --- /dev/null +++ b/test/tx/module_manager/threadx_module_manager_object_authentication_test.c @@ -0,0 +1,1133 @@ +/*************************************************************************** + * 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 kernel object authentication */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +/* A memory-protected module names the kernel objects it wants operated on by + address, and the Module Manager decides whether a privileged service may + dereference that address. Deciding it by asking only whether the address lies + outside the module is not enough, and the reason is the object pool: the pool + is outside every module, so an address shifted into the interior of one of the + module's own privileged allocations passes that test while denoting no object. + The bytes the shifted address then presents as a control block are bytes the + module put there through ordinary create and set services, so the control block + ID at the front of them can be made to read as any type the module likes. + + This test drives the manager's authentication of such addresses on the host. + The object pool behind it is modelled rather than run -- a bump allocator that + reuses the block it most recently released, which is how the address of a freed + object comes back for the cases about stale addresses -- but the two records + authentication actually consults are built the way the system builds them: the + module allocation list is built by running the real + _txm_module_manager_object_allocate, and the kernel created lists are built the + way the create and delete services maintain them, by writing the control block + ID and linking the object in, and by clearing the ID and unlinking it. + + What the test asserts is that an address is accepted when, and only when, it is + the exact address of a live object of the type the service expects. Every + aligned interior offset of a legitimate object is rejected as a foreign type, + including the offsets where this test has planted that type's own ID, which is + the case the reported attack is built on. */ + +#include + +#include "threadx_module_manager_host_test_port.h" + +#include "tx_thread.h" +#include "tx_trace.h" +#include "tx_timer.h" +#include "tx_queue.h" +#include "tx_event_flags.h" +#include "tx_semaphore.h" +#include "tx_mutex.h" +#include "tx_block_pool.h" +#include "tx_byte_pool.h" +#include "txm_module.h" +#include "txm_module_manager_util.h" + + +/* Define the stand-in interrupt lock the port shim counts through. */ + +unsigned int test_interrupt_disable_depth; +unsigned int test_interrupt_disable_max_depth; +unsigned int test_interrupt_restore_underflows; + + +/* Define the modelled object pool. */ + +#define TEST_POOL_BYTES ((ULONG) 8192) +#define TEST_MODULE_MEMORY_BYTES 512U +#define TEST_MAX_BLOCKS 32U + +static union +{ + ALIGN_TYPE test_pool_alignment; + UCHAR test_pool_bytes[TEST_POOL_BYTES]; +} test_pool_arena; + +static ULONG test_pool_offset; + + +/* Define the memory that stands for the calling module's own code and data. A + kernel object here is one the module could write behind the kernel's back, so + authentication has to refuse it however genuine it looks. */ + +static union +{ + ALIGN_TYPE test_data_alignment; + UCHAR test_data_bytes[TEST_MODULE_MEMORY_BYTES]; +} test_module_data; + +static union +{ + ALIGN_TYPE test_code_alignment; + UCHAR test_code_bytes[TEST_MODULE_MEMORY_BYTES]; +} test_module_code; + + +/* Define the objects that stand for application-owned objects shared with a + module. These live outside the pool, so the manager has no allocation record of + them and the kernel created list is the only record of them there is. */ + +static TX_QUEUE test_host_queue; +static TX_THREAD test_host_thread; +static TX_MUTEX test_host_stranger; + + +/* Define the block the modelled pool most recently released, so that a later + allocation of the same size lands on the address a freed object had. */ + +static UCHAR *test_released_start; +static ULONG test_released_bytes; + + +/* Define the manager state the code under test reaches for. */ + +TX_BYTE_POOL _txm_module_manager_object_pool; +UINT _txm_module_manager_object_pool_created; +TX_MUTEX _txm_module_manager_mutex; +TX_THREAD *_tx_thread_current_ptr; + + +/* Define the kernel created lists authentication walks. */ + +TX_BLOCK_POOL *_tx_block_pool_created_ptr; +ULONG _tx_block_pool_created_count; +TX_BYTE_POOL *_tx_byte_pool_created_ptr; +ULONG _tx_byte_pool_created_count; +TX_EVENT_FLAGS_GROUP *_tx_event_flags_created_ptr; +ULONG _tx_event_flags_created_count; +TX_MUTEX *_tx_mutex_created_ptr; +ULONG _tx_mutex_created_count; +TX_QUEUE *_tx_queue_created_ptr; +ULONG _tx_queue_created_count; +TX_SEMAPHORE *_tx_semaphore_created_ptr; +ULONG _tx_semaphore_created_count; +TX_THREAD *_tx_thread_created_ptr; +ULONG _tx_thread_created_count; +TX_TIMER *_tx_timer_created_ptr; +ULONG _tx_timer_created_count; + + +#ifdef TX_ENABLE_EVENT_TRACE + +/* Define the trace state the reset path's trace insert refers to when the tree is + configured with event tracing. The insert does nothing while the buffer pointer + is null, which is the state a system that has not enabled tracing is in, so the + cases below run identically in every configuration this tree builds. */ + +TX_TRACE_HEADER *_tx_trace_header_ptr; +TX_TRACE_BUFFER_ENTRY *_tx_trace_buffer_start_ptr; +TX_TRACE_BUFFER_ENTRY *_tx_trace_buffer_end_ptr; +TX_TRACE_BUFFER_ENTRY *_tx_trace_buffer_current_ptr; +ULONG _tx_trace_event_enable_bits; +ULONG _tx_trace_simulated_time; +VOID (*_tx_trace_full_notify_function)(VOID *buffer); +volatile ULONG _tx_thread_system_state; + +#endif + + +static UINT test_failures; +static ULONG test_checks; +static ULONG test_stack_builds; + + +/* Record the outcome of one expectation. */ +static VOID test_expect(const char *description, ULONG actual, ULONG expected) +{ + test_checks++; + + if (actual != expected) + { + printf("FAIL: %s (expected %lu, got %lu)\n", description, (unsigned long) expected, (unsigned long) actual); + test_failures++; + } +} + + +/* Report every module object type this manager authenticates, and the name and + control block size of each, so that the cases below can be written once and run + against all of them. */ + +typedef struct TEST_OBJECT_KIND_STRUCT +{ + UINT test_kind_type; + const char *test_kind_name; + ULONG test_kind_size; + ULONG test_kind_id; +} TEST_OBJECT_KIND; + +static const TEST_OBJECT_KIND test_object_kinds[] = +{ + { TXM_BLOCK_POOL_OBJECT, "block pool", (ULONG) sizeof(TX_BLOCK_POOL), TX_BLOCK_POOL_ID }, + { TXM_BYTE_POOL_OBJECT, "byte pool", (ULONG) sizeof(TX_BYTE_POOL), TX_BYTE_POOL_ID }, + { TXM_EVENT_FLAGS_OBJECT, "event flags", (ULONG) sizeof(TX_EVENT_FLAGS_GROUP), TX_EVENT_FLAGS_ID }, + { TXM_MUTEX_OBJECT, "mutex", (ULONG) sizeof(TX_MUTEX), TX_MUTEX_ID }, + { TXM_QUEUE_OBJECT, "queue", (ULONG) sizeof(TX_QUEUE), TX_QUEUE_ID }, + { TXM_SEMAPHORE_OBJECT, "semaphore", (ULONG) sizeof(TX_SEMAPHORE), TX_SEMAPHORE_ID }, + { TXM_THREAD_OBJECT, "thread", (ULONG) sizeof(TX_THREAD), TX_THREAD_ID }, + { TXM_TIMER_OBJECT, "timer", (ULONG) sizeof(TX_TIMER), TX_TIMER_ID } +}; + +#define TEST_OBJECT_KIND_COUNT (sizeof(test_object_kinds) / sizeof(test_object_kinds[0])) + + +/* Round a request up the way _tx_byte_allocate does. */ +static ULONG test_round_up(ULONG memory_size) +{ + return((((memory_size + ((ULONG) sizeof(ALIGN_TYPE))) - ((ULONG) 1)) / ((ULONG) sizeof(ALIGN_TYPE))) * ((ULONG) sizeof(ALIGN_TYPE))); +} + + +/* Stand in for the byte pool the manager allocates object memory from. + + The two size bounds are the real ones. The placement is a bump allocator with + one refinement: a request that exactly fits the block most recently released is + given that block back. A first-fit byte pool reuses freed memory, and the cases + about an address that has been freed and allocated again need the address to + actually come back. */ +UINT _txe_byte_allocate(TX_BYTE_POOL *pool_ptr, VOID **memory_ptr, ULONG memory_size, ULONG wait_option) +{ + +ULONG rounded_size; +UCHAR *block_start; + + + (VOID) wait_option; + + if ((pool_ptr != &_txm_module_manager_object_pool) || (memory_ptr == TX_NULL)) + { + return(TX_POOL_ERROR); + } + + if (memory_size == ((ULONG) 0)) + { + return(TX_SIZE_ERROR); + } + + if (memory_size > pool_ptr -> tx_byte_pool_size) + { + return(TX_SIZE_ERROR); + } + + rounded_size = test_round_up(memory_size); + + if ((test_released_start != TX_NULL) && (test_released_bytes == rounded_size)) + { + block_start = test_released_start; + test_released_start = TX_NULL; + test_released_bytes = ((ULONG) 0); + } + else + { + if ((test_pool_offset + rounded_size) > TEST_POOL_BYTES) + { + return(TX_NO_MEMORY); + } + + block_start = &test_pool_arena.test_pool_bytes[test_pool_offset]; + test_pool_offset = test_pool_offset + rounded_size; + } + + pool_ptr -> tx_byte_pool_available = pool_ptr -> tx_byte_pool_available - rounded_size; + + *memory_ptr = (VOID *) block_start; + + return(TX_SUCCESS); +} + + +/* Stand in for the release side, remembering the block so it can come back. */ +UINT _txe_byte_release(VOID *memory_ptr) +{ + +UCHAR *block_start; + + + block_start = (UCHAR *) memory_ptr; + + if ((block_start < test_pool_arena.test_pool_bytes) || + (block_start >= &test_pool_arena.test_pool_bytes[TEST_POOL_BYTES])) + { + return(TX_PTR_ERROR); + } + + /* The model does not track block lengths, and the only released block a case + needs to come back is the one it just released, whose length it knows. The + length is recorded by the caller through test_release_size. */ + test_released_start = block_start; + + return(TX_SUCCESS); +} + + +/* Tell the model how long the block just released was. */ +static VOID test_release_size(ULONG object_size) +{ + test_released_bytes = test_round_up(object_size + ((ULONG) sizeof(TXM_MODULE_ALLOCATED_OBJECT))); +} + + +/* Stand in for the protection mutex. */ +UINT _txe_mutex_get(TX_MUTEX *mutex_ptr, ULONG wait_option) +{ + (VOID) mutex_ptr; + (VOID) wait_option; + + return(TX_SUCCESS); +} + + +UINT _txe_mutex_put(TX_MUTEX *mutex_ptr) +{ + (VOID) mutex_ptr; + + return(TX_SUCCESS); +} + + +/* 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) +{ + +ULONG data_start; +ULONG data_end; + + + 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)) + { + return(TX_TRUE); + } + + return(TX_FALSE); +} + + +/* Stand in for the port primitives the manager sources under test refer to but + that no case here exercises. */ +VOID _txm_module_manager_alignment_adjust(TXM_MODULE_PREAMBLE *module_preamble, ULONG *code_size, + ULONG *code_alignment, ULONG *data_size, ULONG *data_alignment) +{ + (VOID) module_preamble; + (VOID) code_size; + (VOID) code_alignment; + (VOID) data_size; + (VOID) data_alignment; +} + + +VOID _txm_module_manager_thread_stack_build(TX_THREAD *thread_ptr, VOID (*shell_function)(TX_THREAD *, TXM_MODULE_INSTANCE *)) +{ + (VOID) thread_ptr; + (VOID) shell_function; + + test_stack_builds++; +} + + +/* Do to an object what the kernel's create service does to it, as far as + authentication can see: write the control block ID, and link the object onto + the created list for its type. + + The chain each list is built as ends in a self-link on its oldest member rather + than closing back on the head, so that a walk bounded by the created count + visits every member and a walk that ignored the count would not run off the + end. What is being tested is the bound, not the model. */ +static VOID test_object_create(VOID *object_ptr, UINT object_type) +{ + switch (object_type) + { + + case TXM_BLOCK_POOL_OBJECT: + { + TX_BLOCK_POOL *block_pool_ptr = (TX_BLOCK_POOL *) object_ptr; + + block_pool_ptr -> tx_block_pool_id = TX_BLOCK_POOL_ID; + block_pool_ptr -> tx_block_pool_created_next = (_tx_block_pool_created_ptr == TX_NULL) ? block_pool_ptr : _tx_block_pool_created_ptr; + _tx_block_pool_created_ptr = block_pool_ptr; + _tx_block_pool_created_count++; + break; + } + + case TXM_BYTE_POOL_OBJECT: + { + TX_BYTE_POOL *byte_pool_ptr = (TX_BYTE_POOL *) object_ptr; + + byte_pool_ptr -> tx_byte_pool_id = TX_BYTE_POOL_ID; + byte_pool_ptr -> tx_byte_pool_created_next = (_tx_byte_pool_created_ptr == TX_NULL) ? byte_pool_ptr : _tx_byte_pool_created_ptr; + _tx_byte_pool_created_ptr = byte_pool_ptr; + _tx_byte_pool_created_count++; + break; + } + + case TXM_EVENT_FLAGS_OBJECT: + { + TX_EVENT_FLAGS_GROUP *event_flags_ptr = (TX_EVENT_FLAGS_GROUP *) object_ptr; + + event_flags_ptr -> tx_event_flags_group_id = TX_EVENT_FLAGS_ID; + event_flags_ptr -> tx_event_flags_group_created_next = (_tx_event_flags_created_ptr == TX_NULL) ? event_flags_ptr : _tx_event_flags_created_ptr; + _tx_event_flags_created_ptr = event_flags_ptr; + _tx_event_flags_created_count++; + break; + } + + case TXM_MUTEX_OBJECT: + { + TX_MUTEX *mutex_ptr = (TX_MUTEX *) object_ptr; + + mutex_ptr -> tx_mutex_id = TX_MUTEX_ID; + mutex_ptr -> tx_mutex_created_next = (_tx_mutex_created_ptr == TX_NULL) ? mutex_ptr : _tx_mutex_created_ptr; + _tx_mutex_created_ptr = mutex_ptr; + _tx_mutex_created_count++; + break; + } + + case TXM_QUEUE_OBJECT: + { + TX_QUEUE *queue_ptr = (TX_QUEUE *) object_ptr; + + queue_ptr -> tx_queue_id = TX_QUEUE_ID; + queue_ptr -> tx_queue_created_next = (_tx_queue_created_ptr == TX_NULL) ? queue_ptr : _tx_queue_created_ptr; + _tx_queue_created_ptr = queue_ptr; + _tx_queue_created_count++; + break; + } + + case TXM_SEMAPHORE_OBJECT: + { + TX_SEMAPHORE *semaphore_ptr = (TX_SEMAPHORE *) object_ptr; + + semaphore_ptr -> tx_semaphore_id = TX_SEMAPHORE_ID; + semaphore_ptr -> tx_semaphore_created_next = (_tx_semaphore_created_ptr == TX_NULL) ? semaphore_ptr : _tx_semaphore_created_ptr; + _tx_semaphore_created_ptr = semaphore_ptr; + _tx_semaphore_created_count++; + break; + } + + case TXM_THREAD_OBJECT: + { + TX_THREAD *thread_ptr = (TX_THREAD *) object_ptr; + + thread_ptr -> tx_thread_id = TX_THREAD_ID; + thread_ptr -> tx_thread_created_next = (_tx_thread_created_ptr == TX_NULL) ? thread_ptr : _tx_thread_created_ptr; + _tx_thread_created_ptr = thread_ptr; + _tx_thread_created_count++; + break; + } + + case TXM_TIMER_OBJECT: + default: + { + TX_TIMER *timer_ptr = (TX_TIMER *) object_ptr; + + timer_ptr -> tx_timer_id = TX_TIMER_ID; + timer_ptr -> tx_timer_created_next = (_tx_timer_created_ptr == TX_NULL) ? timer_ptr : _tx_timer_created_ptr; + _tx_timer_created_ptr = timer_ptr; + _tx_timer_created_count++; + break; + } + } +} + + +/* Do to an object what the kernel's delete service does: clear the control block + ID, and take the object off the created list for its type. + + Every case that deletes has one object of that type on the list, which is the + shape that matters here, so the model empties the list rather than unlinking a + member of a longer one. */ +static VOID test_object_delete(VOID *object_ptr, UINT object_type) +{ + switch (object_type) + { + + case TXM_BLOCK_POOL_OBJECT: + + ((TX_BLOCK_POOL *) object_ptr) -> tx_block_pool_id = TX_CLEAR_ID; + _tx_block_pool_created_ptr = TX_NULL; + _tx_block_pool_created_count = ((ULONG) 0); + break; + + case TXM_BYTE_POOL_OBJECT: + + ((TX_BYTE_POOL *) object_ptr) -> tx_byte_pool_id = TX_CLEAR_ID; + _tx_byte_pool_created_ptr = TX_NULL; + _tx_byte_pool_created_count = ((ULONG) 0); + break; + + case TXM_EVENT_FLAGS_OBJECT: + + ((TX_EVENT_FLAGS_GROUP *) object_ptr) -> tx_event_flags_group_id = TX_CLEAR_ID; + _tx_event_flags_created_ptr = TX_NULL; + _tx_event_flags_created_count = ((ULONG) 0); + break; + + case TXM_MUTEX_OBJECT: + + ((TX_MUTEX *) object_ptr) -> tx_mutex_id = TX_CLEAR_ID; + _tx_mutex_created_ptr = TX_NULL; + _tx_mutex_created_count = ((ULONG) 0); + break; + + case TXM_QUEUE_OBJECT: + + ((TX_QUEUE *) object_ptr) -> tx_queue_id = TX_CLEAR_ID; + _tx_queue_created_ptr = TX_NULL; + _tx_queue_created_count = ((ULONG) 0); + break; + + case TXM_SEMAPHORE_OBJECT: + + ((TX_SEMAPHORE *) object_ptr) -> tx_semaphore_id = TX_CLEAR_ID; + _tx_semaphore_created_ptr = TX_NULL; + _tx_semaphore_created_count = ((ULONG) 0); + break; + + case TXM_THREAD_OBJECT: + + ((TX_THREAD *) object_ptr) -> tx_thread_id = TX_CLEAR_ID; + _tx_thread_created_ptr = TX_NULL; + _tx_thread_created_count = ((ULONG) 0); + break; + + case TXM_TIMER_OBJECT: + default: + + ((TX_TIMER *) object_ptr) -> tx_timer_id = TX_CLEAR_ID; + _tx_timer_created_ptr = TX_NULL; + _tx_timer_created_count = ((ULONG) 0); + break; + } +} + + +/* Bring the pool, the created lists and the modules back to a known state. + + Everything is reset together deliberately. The allocation lists point into the + pool and the created lists point at objects in it, so a case that left either + behind would have the next case reading the wreckage of an earlier one rather + than what it was written to check. */ +static VOID test_reset(TXM_MODULE_INSTANCE *module_a, TXM_MODULE_INSTANCE *module_b) +{ + +UINT index; + + + for (index = 0U; index < (UINT) TEST_POOL_BYTES; index++) + { + test_pool_arena.test_pool_bytes[index] = (UCHAR) 0; + } + + for (index = 0U; index < TEST_MODULE_MEMORY_BYTES; index++) + { + test_module_data.test_data_bytes[index] = (UCHAR) 0; + test_module_code.test_code_bytes[index] = (UCHAR) 0; + } + + test_pool_offset = ((ULONG) 0); + test_released_start = TX_NULL; + test_released_bytes = ((ULONG) 0); + + _txm_module_manager_object_pool.tx_byte_pool_id = TX_BYTE_POOL_ID; + _txm_module_manager_object_pool.tx_byte_pool_start = test_pool_arena.test_pool_bytes; + _txm_module_manager_object_pool.tx_byte_pool_size = TEST_POOL_BYTES; + _txm_module_manager_object_pool.tx_byte_pool_available = TEST_POOL_BYTES; + _txm_module_manager_object_pool_created = TX_TRUE; + + _tx_block_pool_created_ptr = TX_NULL; + _tx_block_pool_created_count = ((ULONG) 0); + _tx_byte_pool_created_ptr = TX_NULL; + _tx_byte_pool_created_count = ((ULONG) 0); + _tx_event_flags_created_ptr = TX_NULL; + _tx_event_flags_created_count = ((ULONG) 0); + _tx_mutex_created_ptr = TX_NULL; + _tx_mutex_created_count = ((ULONG) 0); + _tx_queue_created_ptr = TX_NULL; + _tx_queue_created_count = ((ULONG) 0); + _tx_semaphore_created_ptr = TX_NULL; + _tx_semaphore_created_count = ((ULONG) 0); + _tx_thread_created_ptr = TX_NULL; + _tx_thread_created_count = ((ULONG) 0); + _tx_timer_created_ptr = TX_NULL; + _tx_timer_created_count = ((ULONG) 0); + + module_a -> txm_module_instance_object_list_count = ((ULONG) 0); + module_a -> txm_module_instance_object_list_head = TX_NULL; + module_b -> txm_module_instance_object_list_count = ((ULONG) 0); + module_b -> txm_module_instance_object_list_head = TX_NULL; +} + + +/* Allocate object memory for a module through the real manager path. */ +static VOID *test_allocate(TXM_MODULE_INSTANCE *module_instance, ULONG object_size) +{ + +VOID *object_ptr; +UINT status; + + + object_ptr = TX_NULL; + status = _txm_module_manager_object_allocate(&object_ptr, object_size, module_instance); + + if (status != TX_SUCCESS) + { + printf("FAIL: the modelled pool refused an allocation of %lu bytes (status %u)\n", + (unsigned long) object_size, status); + test_failures++; + + return(TX_NULL); + } + + return(object_ptr); +} + + +/* Allocate and create one object of a type, the way a module does. */ +static VOID *test_allocate_and_create(TXM_MODULE_INSTANCE *module_instance, const TEST_OBJECT_KIND *kind) +{ + +VOID *object_ptr; + + + object_ptr = test_allocate(module_instance, kind -> test_kind_size); + + if (object_ptr != TX_NULL) + { + test_object_create(object_ptr, kind -> test_kind_type); + } + + return(object_ptr); +} + + +/* Ask the manager to authenticate an address, and report what it said. */ +static ULONG test_authenticate(TXM_MODULE_INSTANCE *module_instance, VOID *object_ptr, UINT object_type) +{ + +ULONG accepted; + + + test_interrupt_disable_depth = 0U; + test_interrupt_disable_max_depth = 0U; + test_interrupt_restore_underflows = 0U; + + accepted = (ULONG) _txm_module_manager_param_check_typed_object_for_use(module_instance, + (ALIGN_TYPE) object_ptr, + object_type); + + /* Whatever it decided, it must have left the interrupt lock as it found it. */ + if ((test_interrupt_disable_depth != 0U) || (test_interrupt_restore_underflows != 0U)) + { + printf("FAIL: authentication left the interrupt lock unbalanced (depth %u, underflows %u)\n", + test_interrupt_disable_depth, test_interrupt_restore_underflows); + test_failures++; + } + + return(accepted); +} + + +/* Plant a value at an offset into an object, the way a module reaches interior + words of its own privileged allocations through ordinary create and set + services, and report what was there before. */ +static ULONG test_plant(VOID *object_ptr, ULONG offset, ULONG value) +{ + +ULONG *word_ptr; +ULONG previous; + + + word_ptr = (ULONG *) (VOID *) (((UCHAR *) object_ptr) + offset); + previous = *word_ptr; + *word_ptr = value; + + return(previous); +} + + +int main(void) +{ + +TXM_MODULE_INSTANCE module_a; +TXM_MODULE_INSTANCE module_b; +TXM_MODULE_INSTANCE *owner; +const TEST_OBJECT_KIND *kind; +const TEST_OBJECT_KIND *other_kind; +VOID *object; +VOID *other_object; +VOID *raw_object; +VOID *shifted; +UCHAR *shifted_bytes; +ULONG offset; +ULONG interior_offsets; +ULONG accepted_interiors; +ULONG index; +ULONG other_index; +ULONG saved; +UINT status; +char description[128]; + + + /* Report expectations before anything can crash, so that a case that brings + the process down is still preceded by everything that passed. */ + setvbuf(stdout, TX_NULL, _IONBF, 0); + + test_failures = 0U; + test_checks = ((ULONG) 0); + + /* Set up two memory-protected modules. Module A does the asking throughout; + module B is there to own objects A did not allocate. */ + module_a.txm_module_instance_property_flags = TXM_MODULE_MEMORY_PROTECTION; + module_a.txm_module_instance_code_start = (VOID *) test_module_code.test_code_bytes; + module_a.txm_module_instance_code_end = (VOID *) &test_module_code.test_code_bytes[TEST_MODULE_MEMORY_BYTES - 1U]; + module_a.txm_module_instance_data_start = (VOID *) test_module_data.test_data_bytes; + module_a.txm_module_instance_data_end = (VOID *) &test_module_data.test_data_bytes[TEST_MODULE_MEMORY_BYTES - 1U]; + + module_b = module_a; + + _tx_thread_current_ptr = TX_NULL; + + /**********************************************************************/ + /* The reported attack: an address shifted into a legitimate object. */ + /**********************************************************************/ + + /* A module allocates and creates a timer of its own, entirely legitimately, + and then presents addresses inside it as though they were a thread. The + word at each of those addresses is set to TX_THREAD_ID first, which is what + the reported chain arranges through ordinary create and set services, so + that every address offered is one that an ID test on its own would accept. */ + test_reset(&module_a, &module_b); + + kind = &test_object_kinds[6]; + object = test_allocate(&module_a, (ULONG) sizeof(TX_THREAD)); + test_object_create(object, TXM_TIMER_OBJECT); + + test_expect("a timer's own address is not accepted as a thread", + test_authenticate(&module_a, object, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + interior_offsets = ((ULONG) 0); + accepted_interiors = ((ULONG) 0); + + for (offset = (ULONG) sizeof(ULONG); offset < (ULONG) sizeof(TX_THREAD); offset = offset + ((ULONG) sizeof(ULONG))) + { + shifted_bytes = ((UCHAR *) object) + offset; + shifted = (VOID *) shifted_bytes; + + saved = test_plant(object, offset, TX_THREAD_ID); + + interior_offsets++; + + if (test_authenticate(&module_a, shifted, TXM_THREAD_OBJECT) == (ULONG) TX_TRUE) + { + accepted_interiors++; + } + + (VOID) test_plant(object, offset, saved); + } + + test_expect("every aligned interior offset of a legitimate object was offered", + (ULONG) (interior_offsets > ((ULONG) 0)), (ULONG) TX_TRUE); + test_expect("no aligned interior address carrying a planted thread ID is accepted as a thread", + accepted_interiors, ((ULONG) 0)); + + /* The unaligned offsets the report names, and the address one past the end. */ + (VOID) test_plant(object, ((ULONG) 0), TX_THREAD_ID); + + shifted = (VOID *) (((UCHAR *) object) + 1); + test_expect("an address one byte into a legitimate object is not accepted", + test_authenticate(&module_a, shifted, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + shifted = (VOID *) (((UCHAR *) object) + 4); + test_expect("an address four bytes into a legitimate object is not accepted", + test_authenticate(&module_a, shifted, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + shifted = (VOID *) (((UCHAR *) object) + (ULONG) sizeof(TX_THREAD)); + test_expect("the address one past a legitimate object is not accepted", + test_authenticate(&module_a, shifted, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + /**********************************************************************/ + /* Exact addresses of live objects, for every type the manager knows. */ + /**********************************************************************/ + + for (index = ((ULONG) 0); index < (ULONG) TEST_OBJECT_KIND_COUNT; index++) + { + kind = &test_object_kinds[index]; + + test_reset(&module_a, &module_b); + + object = test_allocate_and_create(&module_a, kind); + + (void) snprintf(description, sizeof(description), + "the exact address of a live %s is accepted as one", kind -> test_kind_name); + test_expect(description, test_authenticate(&module_a, object, kind -> test_kind_type), (ULONG) TX_TRUE); + + /* Every other type must refuse it, whatever its ID has been made to say. */ + for (other_index = ((ULONG) 0); other_index < (ULONG) TEST_OBJECT_KIND_COUNT; other_index++) + { + if (other_index == index) + { + continue; + } + + other_kind = &test_object_kinds[other_index]; + + saved = test_plant(object, ((ULONG) 0), other_kind -> test_kind_id); + + (void) snprintf(description, sizeof(description), + "a %s carrying a %s ID is not accepted as a %s", + kind -> test_kind_name, other_kind -> test_kind_name, other_kind -> test_kind_name); + test_expect(description, test_authenticate(&module_a, object, other_kind -> test_kind_type), (ULONG) TX_FALSE); + + (VOID) test_plant(object, ((ULONG) 0), saved); + } + + /* A raw allocation of exactly the right size, never created. */ + raw_object = test_allocate(&module_a, kind -> test_kind_size); + + (void) snprintf(description, sizeof(description), + "an uncreated allocation the size of a %s is not accepted as one", kind -> test_kind_name); + test_expect(description, test_authenticate(&module_a, raw_object, kind -> test_kind_type), (ULONG) TX_FALSE); + + /* Deleting the object takes it off the created list and clears its ID. */ + test_object_delete(object, kind -> test_kind_type); + + (void) snprintf(description, sizeof(description), + "a deleted %s is not accepted as one", kind -> test_kind_name); + test_expect(description, test_authenticate(&module_a, object, kind -> test_kind_type), (ULONG) TX_FALSE); + } + + /**********************************************************************/ + /* Addresses that have been freed, and addresses that have come back. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + kind = &test_object_kinds[4]; + object = test_allocate_and_create(&module_a, kind); + + test_expect("a live queue is accepted before anything is given back", + test_authenticate(&module_a, object, TXM_QUEUE_OBJECT), (ULONG) TX_TRUE); + + /* Give the memory back without deleting the object first. The manager stops + vouching for the control block at the moment it stops owning the memory, so + the address is refused even though the created list has not been told. */ + _tx_thread_current_ptr = (TX_THREAD *) test_allocate(&module_a, (ULONG) sizeof(TX_THREAD)); + _tx_thread_current_ptr -> tx_thread_module_instance_ptr = &module_a; + + test_release_size(kind -> test_kind_size); + status = _txm_module_manager_object_deallocate(object); + test_expect("the object memory is given back", (ULONG) status, (ULONG) TX_SUCCESS); + + test_expect("an address whose memory was given back without a delete is refused", + test_authenticate(&module_a, object, TXM_QUEUE_OBJECT), (ULONG) TX_FALSE); + + /* The same address now comes back as a fresh, raw allocation. It is once + again an exact allocation start of the right size, and it is still on the + created list, so nothing but the cleared ID stands between it and being + taken for the object that used to be there. */ + raw_object = test_allocate(&module_a, kind -> test_kind_size); + test_expect("the freed address is handed out again", + (ULONG) (raw_object == object), (ULONG) TX_TRUE); + test_expect("and is not accepted as the object that used to be there", + test_authenticate(&module_a, raw_object, TXM_QUEUE_OBJECT), (ULONG) TX_FALSE); + + _tx_thread_current_ptr = TX_NULL; + + /**********************************************************************/ + /* Objects the application owns, and objects nobody owns. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + /* An application-owned object lives outside the pool, so the manager has no + allocation record of it. The kernel created list is the record that stands + in its place, and it is what makes sharing such an object with a module + possible at all. */ + test_object_create(&test_host_queue, TXM_QUEUE_OBJECT); + test_object_create(&test_host_thread, TXM_THREAD_OBJECT); + + test_expect("an application-owned queue on the created list is accepted", + test_authenticate(&module_a, &test_host_queue, TXM_QUEUE_OBJECT), (ULONG) TX_TRUE); + test_expect("an application-owned thread on the created list is accepted", + test_authenticate(&module_a, &test_host_thread, TXM_THREAD_OBJECT), (ULONG) TX_TRUE); + test_expect("but not as the wrong type", + test_authenticate(&module_a, &test_host_queue, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + /* Memory outside the pool that carries the right ID but is on no created + list. Without the created list this would be indistinguishable from a real + object, and every word of privileged memory that happened to hold an ID + would be usable as one. */ + test_host_stranger.tx_mutex_id = TX_MUTEX_ID; + test_expect("memory carrying a mutex ID but on no created list is refused", + test_authenticate(&module_a, &test_host_stranger, TXM_MUTEX_OBJECT), (ULONG) TX_FALSE); + + test_object_delete(&test_host_queue, TXM_QUEUE_OBJECT); + test_expect("a deleted application-owned queue is refused", + test_authenticate(&module_a, &test_host_queue, TXM_QUEUE_OBJECT), (ULONG) TX_FALSE); + + test_object_delete(&test_host_thread, TXM_THREAD_OBJECT); + + /**********************************************************************/ + /* Objects another module allocated. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + kind = &test_object_kinds[5]; + object = test_allocate_and_create(&module_b, kind); + + /* A created object belonging to another module is authentic, and is accepted + as such: it is the exact address of a live semaphore. Whether module A is + *authorised* to operate on module B's semaphore is a separate question + about ownership, which this check does not answer and does not claim to. */ + test_expect("another module's live semaphore is authentic", + test_authenticate(&module_a, object, TXM_SEMAPHORE_OBJECT), (ULONG) TX_TRUE); + + /* An allocation another module has not created is not an object at all, and + neither module can use it as one. */ + raw_object = test_allocate(&module_b, kind -> test_kind_size); + test_expect("another module's uncreated allocation is not an object", + test_authenticate(&module_a, raw_object, TXM_SEMAPHORE_OBJECT), (ULONG) TX_FALSE); + test_expect("nor is it one to the module that allocated it", + test_authenticate(&module_b, raw_object, TXM_SEMAPHORE_OBJECT), (ULONG) TX_FALSE); + + /* An interior address of another module's object is refused the same way an + interior address of the caller's own object is. */ + shifted = (VOID *) (((UCHAR *) object) + (ULONG) sizeof(ULONG)); + (VOID) test_plant(object, (ULONG) sizeof(ULONG), TX_SEMAPHORE_ID); + test_expect("an interior address of another module's object is refused", + test_authenticate(&module_a, shifted, TXM_SEMAPHORE_OBJECT), (ULONG) TX_FALSE); + + /**********************************************************************/ + /* Addresses and types that are not objects at all. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + object = test_allocate_and_create(&module_a, &test_object_kinds[6]); + + test_expect("a null address is refused", + test_authenticate(&module_a, TX_NULL, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + test_expect("an object type the manager does not know is refused", + test_authenticate(&module_a, object, 0U), (ULONG) TX_FALSE); + test_expect("and so is one past the types it does know", + test_authenticate(&module_a, object, TXM_TIMER_OBJECT + 1U), (ULONG) TX_FALSE); + + /* A control block inside the calling module's own memory is one the module can + write behind the kernel's back, so it is refused however genuine it looks. */ + owner = &module_a; + test_object_create(test_module_data.test_data_bytes, TXM_MUTEX_OBJECT); + test_expect("a mutex in the module's own data is refused", + test_authenticate(owner, test_module_data.test_data_bytes, TXM_MUTEX_OBJECT), (ULONG) TX_FALSE); + + test_object_create(test_module_code.test_code_bytes, TXM_SEMAPHORE_OBJECT); + test_expect("a semaphore in the module's own code is refused", + test_authenticate(owner, test_module_code.test_code_bytes, TXM_SEMAPHORE_OBJECT), (ULONG) TX_FALSE); + + /**********************************************************************/ + /* The bound on every scan. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + object = test_allocate_and_create(&module_a, &test_object_kinds[6]); + + /* A count that claims more objects than the list holds must not make either + scan walk past the end of it. The chain the model builds ends in a self + link, so a scan that ignored the count would spin rather than crash; the + count is what stops it. */ + module_a.txm_module_instance_object_list_count = ((ULONG) 64); + _tx_thread_created_count = ((ULONG) 64); + + test_expect("a live thread is still accepted when the counts overstate the lists", + test_authenticate(&module_a, object, TXM_THREAD_OBJECT), (ULONG) TX_TRUE); + + other_object = (VOID *) &test_host_stranger; + test_expect("and an address on neither list is still refused", + test_authenticate(&module_a, other_object, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + /* An empty allocation list with a head that has not been cleared must not be + followed either. */ + module_a.txm_module_instance_object_list_count = ((ULONG) 0); + _tx_thread_created_count = ((ULONG) 1); + + test_expect("a live thread is accepted with an empty allocation list", + test_authenticate(&module_a, object, TXM_THREAD_OBJECT), (ULONG) TX_TRUE); + + _tx_thread_created_count = ((ULONG) 0); + test_expect("and refused once the created list is empty too", + test_authenticate(&module_a, object, TXM_THREAD_OBJECT), (ULONG) TX_FALSE); + + /**********************************************************************/ + /* The size-based check kept for dispatchers outside this repository. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + kind = &test_object_kinds[3]; + object = test_allocate_and_create(&module_a, kind); + + test_expect("the size-based check accepts an exact allocation of that size", + (ULONG) _txm_module_manager_param_check_object_for_use(&module_a, (ALIGN_TYPE) object, + kind -> test_kind_size), + (ULONG) TX_TRUE); + + test_expect("and refuses an allocation of a different size", + (ULONG) _txm_module_manager_param_check_object_for_use(&module_a, (ALIGN_TYPE) object, + kind -> test_kind_size + ((ULONG) 4)), + (ULONG) TX_FALSE); + + /* The interior address the reported attack is built on is what this check has + to refuse, and it does, which is what makes hardening it worthwhile even + though it cannot be given a type. */ + interior_offsets = ((ULONG) 0); + accepted_interiors = ((ULONG) 0); + + for (offset = (ULONG) sizeof(ULONG); offset < kind -> test_kind_size; offset = offset + ((ULONG) sizeof(ULONG))) + { + shifted = (VOID *) (((UCHAR *) object) + offset); + + saved = test_plant(object, offset, kind -> test_kind_id); + + interior_offsets++; + + if (_txm_module_manager_param_check_object_for_use(&module_a, (ALIGN_TYPE) shifted, + kind -> test_kind_size) == TX_TRUE) + { + accepted_interiors++; + } + + (VOID) test_plant(object, offset, saved); + } + + test_expect("the size-based check was offered every aligned interior offset", + (ULONG) (interior_offsets > ((ULONG) 0)), (ULONG) TX_TRUE); + test_expect("and accepted none of them", + accepted_interiors, ((ULONG) 0)); + + /**********************************************************************/ + /* The disclosed sink: resetting a thread that is not a module thread. */ + /**********************************************************************/ + + test_reset(&module_a, &module_b); + + /* A thread the application created carries no module instance. Reset reads + the shell entry function out of that instance to rebuild the thread's + stack, so a null one would be followed in privileged mode. Such a thread is + authentic and can be found by name, and one that has run to completion + passes reset's own state test, so refusing it has to be reset's own job. */ + _tx_thread_current_ptr = &test_host_thread; + + object = test_allocate(&module_a, (ULONG) sizeof(TX_THREAD)); + test_object_create(object, TXM_THREAD_OBJECT); + + ((TX_THREAD *) object) -> tx_thread_state = TX_COMPLETED; + ((TX_THREAD *) object) -> tx_thread_module_instance_ptr = TX_NULL; + + test_stack_builds = ((ULONG) 0); + status = _txm_module_manager_thread_reset((TX_THREAD *) object); + + test_expect("resetting a completed thread with no module instance is refused", + (ULONG) status, (ULONG) TX_NOT_DONE); + test_expect("...without building a stack frame", test_stack_builds, ((ULONG) 0)); + test_expect("...and without moving it out of the completed state", + (ULONG) ((TX_THREAD *) object) -> tx_thread_state, (ULONG) TX_COMPLETED); + + /* A module thread in the same state is reset, so the guard has not broken the + operation it protects. */ + ((TX_THREAD *) object) -> tx_thread_module_instance_ptr = &module_a; + module_a.txm_module_instance_shell_entry_function = TX_NULL; + + test_stack_builds = ((ULONG) 0); + status = _txm_module_manager_thread_reset((TX_THREAD *) object); + + test_expect("a completed module thread is reset", (ULONG) status, (ULONG) TX_SUCCESS); + test_expect("...building its stack frame once", test_stack_builds, ((ULONG) 1)); + test_expect("...and leaving it suspended", + (ULONG) ((TX_THREAD *) object) -> tx_thread_state, (ULONG) TX_SUSPENDED); + + _tx_thread_current_ptr = TX_NULL; + + /**********************************************************************/ + /* What the whole run has to have held. */ + /**********************************************************************/ + + /**********************************************************************/ + /* Why a size cannot stand in for a type. */ + /**********************************************************************/ + + /* Authentication establishes an object's type from the kernel's created list + for that type, rather than from the size of its control block, because a size + does not identify one. Distinct control blocks share a size: on a 32-bit + target a byte pool, a mutex and a timer are all the same size as each other, + and a block pool is the same size as an event flags group. + + This is asserted rather than left as a remark so that the reasoning is checked + against the structures rather than remembered. It holds while any two distinct + types collide, so adding a field to one of them does not make it fail; it + fails only if every type becomes distinguishable by size, which is when the + remark above would need revisiting. */ + test_expect("distinct object types share a control block size", + (ULONG) ((sizeof(TX_BYTE_POOL) == sizeof(TX_MUTEX)) || + (sizeof(TX_BYTE_POOL) == sizeof(TX_TIMER)) || + (sizeof(TX_MUTEX) == sizeof(TX_TIMER)) || + (sizeof(TX_BLOCK_POOL) == sizeof(TX_EVENT_FLAGS_GROUP))), + (ULONG) TX_TRUE); + + test_expect("every case ran", (ULONG) (test_checks > ((ULONG) 100)), (ULONG) TX_TRUE); + + if (test_failures == 0U) + { + printf("SUCCESS! %lu expectations\n", (unsigned long) test_checks); + return(0); + } + + printf("ERROR: %u expectation(s) failed of %lu\n", test_failures, (unsigned long) test_checks); + return(1); +}