diff --git a/include/mujoco/mjsan.h b/include/mujoco/mjsan.h index 880dc88b..dcacfd9f 100644 --- a/include/mujoco/mjsan.h +++ b/include/mujoco/mjsan.h @@ -26,8 +26,15 @@ #endif #endif +// Define mjUSEASAN if ADDRESS_SANITIZER is active and we are not on MSVC. +// MSVC asan (/fsanitize=address defines ADDRESS_SANITIZER via EnableASAN) cannot use the GCC/clang +// attribute+asm instrumentation below, so we exclude it. +#if defined(ADDRESS_SANITIZER) && !defined(_MSC_VER) + #define mjUSEASAN +#endif + // Include asan interface header, or provide stubs for poison/unpoison macros when not using asan. -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #include #elif defined(_MSC_VER) #define ASAN_POISON_MEMORY_REGION(addr, size) @@ -42,7 +49,7 @@ // into mark/free into the same function, this instrumentation requires that the compiler retains // separate mark/free calls for each original callee. The memory-clobbered asm blocks act as a // barrier to prevent mark/free calls from being combined under optimization. -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #ifdef __cplusplus extern "C" { #endif @@ -64,6 +71,6 @@ static inline void mj_freeStack(mjData* d) __attribute__((always_inline)) { #ifdef __cplusplus } #endif // __cplusplus -#endif // ADDRESS_SANITIZER +#endif // mjUSEASAN #endif // MUJOCO_INCLUDE_MJSAN_H_ diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index 20ee0a97..54fa025a 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -267,7 +267,7 @@ MJAPI void mj_resetDataDebug(const mjModel* m, mjData* d, unsigned char debug_va // Reset data. If 0 <= key < nkey, set fields from specified keyframe. MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key); -#ifndef ADDRESS_SANITIZER // Stack management functions declared in mjsan.h if ASAN is active. +#ifndef mjUSEASAN // Stack management functions declared in mjsan.h if ASAN is active. // Mark a new frame on the mjData stack. MJAPI void mj_markStack(mjData* d); diff --git a/src/engine/engine_collision_driver.c b/src/engine/engine_collision_driver.c index 295c0aaa..178b4b20 100644 --- a/src/engine/engine_collision_driver.c +++ b/src/engine/engine_collision_driver.c @@ -178,7 +178,7 @@ static inline mjtNum getGap(const mjModel* m, int g1, int g2, int ipair) { // move arena pointer back to the end of the contact array static inline void resetArena(mjData* d) { d->parena = d->ncon * sizeof(mjContact); -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION( (char*)d->arena + d->parena, d->narena - d->pstack - d->parena); #endif diff --git a/src/engine/engine_core_constraint.c b/src/engine/engine_core_constraint.c index 24ec73a5..5627acf0 100644 --- a/src/engine/engine_core_constraint.c +++ b/src/engine/engine_core_constraint.c @@ -137,7 +137,7 @@ static int arenaAllocEfc(const mjModel* m, mjData* d) { d->parena = d->ncon * sizeof(mjContact); // poison remaining memory -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION( (char*)d->arena + d->parena, d->narena - d->pstack - d->parena); #endif @@ -390,7 +390,7 @@ static int mj_vertBodyWeight(const mjModel* m, const mjData* d, int f, int* v, int mj_addContact(const mjModel* m, mjData* d, const mjContact* con) { // move arena pointer back to the end of the existing contact array and invalidate efc_ arrays d->parena = d->ncon * sizeof(mjContact); -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION( (char*)d->arena + d->parena, d->narena - d->pstack - d->parena); #endif diff --git a/src/engine/engine_crossplatform.cc b/src/engine/engine_crossplatform.cc index c30b3c0b..5250da00 100644 --- a/src/engine/engine_crossplatform.cc +++ b/src/engine/engine_crossplatform.cc @@ -43,7 +43,7 @@ void CheckRosetta() { } // namespace #endif // defined(__APPLE__) && defined(__AVX__) -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #include #include diff --git a/src/engine/engine_crossplatform.h b/src/engine/engine_crossplatform.h index 5f6b1b5d..cb69fe85 100644 --- a/src/engine/engine_crossplatform.h +++ b/src/engine/engine_crossplatform.h @@ -75,6 +75,10 @@ #endif #endif +#if defined(ADDRESS_SANITIZER) && !defined(_MSC_VER) + #define mjUSEASAN +#endif + // Atomics helper for size_t. #if defined(_MSC_VER) && !defined(__clang__) #include @@ -103,7 +107,7 @@ extern "C" { #endif -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN int mj__comparePcFuncName(void* pc1, void* pc2); const char* mj__getPcDebugInfo(void* pc); #endif diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index fe006699..99d9d768 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -38,7 +38,7 @@ #include "engine/engine_util_errmem.h" #include "engine/engine_util_misc.h" -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #include #include #endif @@ -1037,7 +1037,7 @@ void mj_initPlugin(const mjModel* m, mjData* d) { // free mjData memory without destroying the struct static void freeDataBuffers(mjData* d) { -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN // raise an error if there's a dangling stack frame mj_freeStack(d); #endif @@ -1315,7 +1315,7 @@ static void _resetData(const mjModel* m, mjData* d, unsigned char debug_value) { d->parena = 0; // poison the entire arena+stack memory region when built with asan -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION(d->arena, d->narena); #endif @@ -1371,7 +1371,7 @@ static void _resetData(const mjModel* m, mjData* d, unsigned char debug_value) { //------------------------------ clear buffer, set defaults // fill buffer with debug_value (normally 0) -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN { #define X(type, name, nr, nc) memset(d->name, (int)debug_value, sizeof(type)*(m->nr)*(nc)); MJDATA_POINTERS diff --git a/src/engine/engine_island.c b/src/engine/engine_island.c index 499b9e32..6cdaf845 100644 --- a/src/engine/engine_island.c +++ b/src/engine/engine_island.c @@ -45,7 +45,7 @@ static void clearIsland(mjData* d, size_t parena) { d->parena = parena; // poison remaining memory -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION( (char*)d->arena + d->parena, d->narena - d->pstack - d->parena); #endif diff --git a/src/engine/engine_memory.c b/src/engine/engine_memory.c index d0c044e0..d4f91741 100644 --- a/src/engine/engine_memory.c +++ b/src/engine/engine_memory.c @@ -28,7 +28,7 @@ #include "engine/engine_util_errmem.h" -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #include #include #endif @@ -42,7 +42,7 @@ #endif // add red zone padding when built with asan, to detect out-of-bound accesses -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN #define mjREDZONE 32 #else #define mjREDZONE 0 @@ -82,7 +82,7 @@ static inline mjStackInfo get_stack_info_from_data(const mjData* d) { } -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN // get stack usage from red-zone (under ASAN) static size_t stack_usage_redzone(const mjStackInfo* stack_info) { size_t usage = 0; @@ -117,7 +117,7 @@ void* mj_arenaAllocByte(mjData* d, size_t bytes, size_t alignment) { size_t stack_usage = d->pstack; // under ASAN, get stack usage from red zone -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN mjStackInfo stack_info = get_stack_info_from_data(d); stack_usage = stack_usage_redzone(&stack_info); #endif @@ -127,7 +127,7 @@ void* mj_arenaAllocByte(mjData* d, size_t bytes, size_t alignment) { d->parena += padding + bytes; d->maxuse_arena = mjMAX(d->maxuse_arena, stack_usage + d->parena); -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_UNPOISON_MEMORY_REGION(result, bytes); #endif @@ -179,7 +179,7 @@ static inline void* stackallocinternal(mjData* d, mjStackInfo* stack_info, size_ stack_required_bytes, d->nefc, d->ncon); } -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN usage = current_alloc_usage + stack_usage_redzone(stack_info); // store new stack usage in the red zone @@ -250,7 +250,7 @@ static inline void* stackalloc(mjData* d, size_t size, size_t alignment, // mjStackInfo mark stack frame, inline so ASAN errors point to correct code unit -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN __attribute__((always_inline)) #endif static inline void markstackinternal(mjData* d, mjStackInfo* stack_info) { @@ -259,7 +259,7 @@ static inline void markstackinternal(mjData* d, mjStackInfo* stack_info) { (mjStackFrame*) stackallocinternal(d, stack_info, sizeof(mjStackFrame), _Alignof(mjStackFrame), NULL, 0); s->pbase = stack_info->stack_base; s->pstack = top_old; -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN // store the program counter to the caller so that we can compare against mj_freeStack later s->pc = __sanitizer_return_address(); #endif @@ -268,7 +268,7 @@ static inline void markstackinternal(mjData* d, mjStackInfo* stack_info) { // mjData mark stack frame -#ifndef ADDRESS_SANITIZER +#ifndef mjUSEASAN void mj_markStack(mjData* d) #else void mj__markStack(mjData* d) @@ -286,7 +286,7 @@ void mj__markStack(mjData* d) } -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN __attribute__((always_inline)) #endif static inline void freestackinternal(mjStackInfo* stack_info) { @@ -295,7 +295,7 @@ static inline void freestackinternal(mjStackInfo* stack_info) { } mjStackFrame* s = (mjStackFrame*) stack_info->stack_base; -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN // raise an error if caller function name doesn't match the most recent caller of mj_markStack if (!mj__comparePcFuncName(s->pc, __sanitizer_return_address())) { mjERROR("mj_markStack %s has no corresponding mj_freeStack (detected %s)", @@ -310,14 +310,14 @@ static inline void freestackinternal(mjStackInfo* stack_info) { stack_info->top = s->pstack; // if running under asan, poison the newly freed memory region -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN ASAN_POISON_MEMORY_REGION((char*)old_top, stack_info->top - old_top); #endif } // mjData free stack frame -#ifndef ADDRESS_SANITIZER +#ifndef mjUSEASAN void mj_freeStack(mjData* d) #else void mj__freeStack(mjData* d) diff --git a/src/engine/engine_memory.h b/src/engine/engine_memory.h index f7969eb4..22c61d38 100644 --- a/src/engine/engine_memory.h +++ b/src/engine/engine_memory.h @@ -34,7 +34,7 @@ extern "C" { // mjData arena allocate MJAPI void* mj_arenaAllocByte(mjData* d, size_t bytes, size_t alignment); -#ifndef ADDRESS_SANITIZER +#ifndef mjUSEASAN // mjData mark stack frame MJAPI void mj_markStack(mjData* d); diff --git a/test/engine/engine_io_test.cc b/test/engine/engine_io_test.cc index bffb77e0..57da12c5 100644 --- a/test/engine/engine_io_test.cc +++ b/test/engine/engine_io_test.cc @@ -812,7 +812,7 @@ TEST_F(EngineIoTest, TestStackShardingForThreads) { } } -#ifdef ADDRESS_SANITIZER +#ifdef mjUSEASAN void MarkFreeStack(mjData* d, bool free) { mj_markStack(d); if (free) { diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 3941cc5f..3f5ddeef 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -65,6 +65,7 @@ public const int mjMAXMATERIAL = 1000; public const bool THIRD_PARTY_MUJOCO_MJRFILAMENT_H_ = true; public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJSAN_H_ = true; public const bool ADDRESS_SANITIZER = true; +public const bool mjUSEASAN = true; public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJSPEC_H_ = true; public const bool THIRD_PARTY_MUJOCO_MJSPECMACRO_H_ = true; public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJTYPE_H_ = true;