[asan] Adjust interception compatibility for AIX (#131870)

Adjust asan interceptor compatbility for AIX. AIX uses dlsym to retrieve
addresses of exported functions. However, some functions in libc.a, such
as memcpy, are not exported, so we currently have a limitation in
retrieving these addresses.

Issue: https://github.com/llvm/llvm-project/issues/138916
This commit is contained in:
Jake Egan 2026-01-02 17:31:26 -05:00 committed by GitHub
parent 3572e62991
commit 5ec319119f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 10 deletions

View File

@ -282,7 +282,12 @@ INTERCEPTOR(int, pthread_create, void* thread, void* attr,
# endif
asanThreadArgRetval().Create(detached, {start_routine, arg}, [&]() -> uptr {
result = REAL(pthread_create)(thread, attr, asan_thread_start, t);
// AIX pthread_t is unsigned int.
# if SANITIZER_AIX
return result ? 0 : *(unsigned*)(thread);
# else
return result ? 0 : *(uptr*)(thread);
# endif
});
}
if (result != 0) {
@ -439,10 +444,12 @@ INTERCEPTOR(int, swapcontext, struct ucontext_t* oucp, struct ucontext_t* ucp) {
# define siglongjmp __siglongjmp14
# endif
# if ASAN_INTERCEPT_LONGJMP
INTERCEPTOR(void, longjmp, void* env, int val) {
__asan_handle_no_return();
REAL(longjmp)(env, val);
}
# endif
# if ASAN_INTERCEPT__LONGJMP
INTERCEPTOR(void, _longjmp, void* env, int val) {
@ -867,7 +874,9 @@ void InitializeAsanInterceptors() {
# endif
// Intercept jump-related functions.
# if ASAN_INTERCEPT_LONGJMP
ASAN_INTERCEPT_FUNC(longjmp);
# endif
# if ASAN_INTERCEPT_SWAPCONTEXT
ASAN_INTERCEPT_FUNC(swapcontext);

View File

@ -31,10 +31,26 @@ void InitializePlatformInterceptors();
// really defined to replace libc functions.
#if !SANITIZER_FUCHSIA
// Sanitizer on AIX is currently unable to retrieve the address
// of the real longjump (or an alternative thereto).
// TODO: Consider intercepting longjmpx on AIX.
# if !SANITIZER_AIX
# define ASAN_INTERCEPT_LONGJMP 1
# else
# define ASAN_INTERCEPT_LONGJMP 0
# endif
// Use macro to describe if specific function should be
// intercepted on a given platform.
# if !SANITIZER_WINDOWS
# define ASAN_INTERCEPT__LONGJMP 1
// Sanitizer on AIX is currently unable to retrieve the address
// of the real _longjump (or an alternative thereto).
// TODO: Consider intercepting _longjmpx on AIX.
# if !SANITIZER_AIX
# define ASAN_INTERCEPT__LONGJMP 1
# else
# define ASAN_INTERCEPT__LONGJMP 0
# endif
# define ASAN_INTERCEPT_INDEX 1
# define ASAN_INTERCEPT_PTHREAD_CREATE 1
# else
@ -56,7 +72,10 @@ void InitializePlatformInterceptors();
# define ASAN_INTERCEPT_SWAPCONTEXT 0
# endif
# if !SANITIZER_WINDOWS
// Sanitizer on AIX is currently unable to retrieve the address
// of the real siglongjump (or an alternative thereto).
// TODO: Consider intercepting sigsetjmpx on AIX.
# if !SANITIZER_WINDOWS && !SANITIZER_AIX
# define ASAN_INTERCEPT_SIGLONGJMP 1
# else
# define ASAN_INTERCEPT_SIGLONGJMP 0
@ -84,7 +103,10 @@ void InitializePlatformInterceptors();
# define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 0
# endif
# if !SANITIZER_WINDOWS
// Clang on AIX neither uses `__cxa_atexit` nor links against a library with
// such.
// TODO: Consider intercepting `atexit` and `unatexit` on AIX.
# if !SANITIZER_WINDOWS && !SANITIZER_AIX
# define ASAN_INTERCEPT___CXA_ATEXIT 1
# else
# define ASAN_INTERCEPT___CXA_ATEXIT 0

View File

@ -15,7 +15,7 @@
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \
SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_HAIKU
SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_HAIKU || SANITIZER_AIX
# include "asan_allocator.h"
# include "asan_interceptors.h"

View File

@ -17,16 +17,61 @@
#if SANITIZER_AIX
# include <dlfcn.h> // for dlsym()
# include <stddef.h> // for size_t
# if SANITIZER_WORDSIZE == 64
# define STRCPY_STR "___strcpy64"
# define MEMCPY_STR "___memcpy64"
# define MEMMOVE_STR "___memmove64"
# else
# define STRCPY_STR "___strcpy"
# define MEMCPY_STR "___memcpy"
# define MEMMOVE_STR "___memmove"
# endif
namespace __interception {
static void *GetFuncAddr(const char *name, uptr wrapper_addr) {
// AIX dlsym can only defect the functions that are exported, so
// on AIX, we can not intercept some basic functions like memcpy.
// These symbols cannot be used for indirect calls.
char* ___strcpy(char*, const char*) __asm__(STRCPY_STR);
char* ___memcpy(char*, const char*, size_t) __asm__(MEMCPY_STR);
char* ___memmove(char*, const char*, size_t) __asm__(MEMMOVE_STR);
static char* real_strcpy_wrapper(char* s1, const char* s2) {
return (char*)___strcpy(s1, s2);
}
static char* real_memcpy_wrapper(char* s1, const char* s2, size_t n) {
return (char*)___memcpy(s1, s2, n);
}
static char* real_memmove_wrapper(char* s1, const char* s2, size_t n) {
return (char*)___memmove(s1, s2, n);
}
static void* GetFuncAddr(const char* name, uptr wrapper_addr) {
// FIXME: if we are going to ship dynamic asan library, we may need to search
// all the loaded modules with RTLD_DEFAULT if RTLD_NEXT failed.
void *addr = dlsym(RTLD_NEXT, name);
// AIX dlsym can only detect functions that are exported, so
// some basic functions like memcpy return null. In this case, we fall back
// to a corresponding internal libc symbol (for example, ___memcpy) if it's
// available and, otherwise, to the internal sanitizer function.
if (!addr) {
if (internal_strcmp(name, "strcpy") == 0)
addr = (void*)real_strcpy_wrapper;
else if (internal_strcmp(name, "strncpy") == 0)
addr = (void*)internal_strncpy;
else if (internal_strcmp(name, "strcat") == 0)
addr = (void*)internal_strcat;
else if (internal_strcmp(name, "strncat") == 0)
addr = (void*)internal_strncat;
else if (internal_strcmp(name, "memcpy") == 0)
addr = (void*)real_memcpy_wrapper;
else if (internal_strcmp(name, "memmove") == 0)
addr = (void*)real_memmove_wrapper;
}
// In case `name' is not loaded, dlsym ends up finding the actual wrapper.
// We don't want to intercept the wrapper and have it point to itself.
if ((uptr)addr == wrapper_addr)

View File

@ -190,6 +190,14 @@ uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
return dstlen + srclen;
}
char* internal_strcat(char* dst, const char* src) {
uptr len = internal_strlen(dst);
uptr i;
for (i = 0; src[i]; i++) dst[len + i] = src[i];
dst[len + i] = 0;
return dst;
}
char *internal_strncat(char *dst, const char *src, uptr n) {
uptr len = internal_strlen(dst);
uptr i;

View File

@ -59,6 +59,7 @@ char *internal_strdup(const char *s);
uptr internal_strlen(const char *s);
uptr internal_strlcat(char *dst, const char *src, uptr maxlen);
char *internal_strncat(char *dst, const char *src, uptr n);
char* internal_strcat(char* dst, const char* src);
int internal_strncmp(const char *s1, const char *s2, uptr n);
uptr internal_strlcpy(char *dst, const char *src, uptr maxlen);
char *internal_strncpy(char *dst, const char *src, uptr n);

View File

@ -167,7 +167,7 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
#define SANITIZER_INTERCEPT_STRLEN SI_NOT_FUCHSIA
#define SANITIZER_INTERCEPT_STRNLEN (SI_NOT_MAC && SI_NOT_FUCHSIA)
#define SANITIZER_INTERCEPT_STRCMP (SI_NOT_FUCHSIA && SI_NOT_AIX)
#define SANITIZER_INTERCEPT_STRCMP SI_NOT_FUCHSIA
#define SANITIZER_INTERCEPT_STRSTR SI_NOT_FUCHSIA
#define SANITIZER_INTERCEPT_STRCASESTR (SI_POSIX && SI_NOT_AIX)
#define SANITIZER_INTERCEPT_STRTOK SI_NOT_FUCHSIA
@ -179,8 +179,8 @@ SANITIZER_WEAK_IMPORT void *aligned_alloc(__sanitizer::usize __alignment,
#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_STRCASECMP SI_POSIX
#define SANITIZER_INTERCEPT_MEMSET 1
#define SANITIZER_INTERCEPT_MEMMOVE SI_NOT_AIX
#define SANITIZER_INTERCEPT_MEMCPY SI_NOT_AIX
#define SANITIZER_INTERCEPT_MEMMOVE 1
#define SANITIZER_INTERCEPT_MEMCPY 1
#define SANITIZER_INTERCEPT_MEMCMP SI_NOT_FUCHSIA
#define SANITIZER_INTERCEPT_BCMP \
SANITIZER_INTERCEPT_MEMCMP && \