Harden native kernel serving and output redirects

- Fatbin parser: stop when an entry header runs past the declared files
  size instead of wrapping the unsigned remainder and reading outside the
  image.
- Output kernel table: keep the latest 64 lookups and resolve launches
  newest first under its own lock, so a full table no longer drops new
  lookups and a handle reused by a later output-kernel lookup resolves
  with that lookup.
- Resolve a relative D4R_ZLUDA_NATIVE_DIR before linking its kernels into
  the per-process directory; the relative links dangled.
- cuSurfObjectCreate: destroy the surface and return the error when its
  output redirect cannot be written.
- Serve native kernels from D4R_ZLUDA_CACHE_HOME, XDG_CACHE_HOME or
  ~/.cache, never /tmp, where the stale-PID cleanup could follow links
  planted by another user.
This commit is contained in:
dhaern committed 2026-10-06 00:03:49 +00:00
1 parent c4954dc3c4
commit 369eff35a1
1 file changed
+37 -35
+37 -35
View File
@@ -475,7 +475,7 @@ static void prepare_native_kernels(const char* cache_home)
return; return;
} }
snprintf(path, sizeof(path), "%s/d4r-kernels.txt", source); snprintf(path, sizeof(path), "%s/d4r-kernels.txt", source);
manifest = fopen(path, "r"); manifest = cache_home != NULL ? fopen(path, "r") : NULL; /* a manifest set is served from the cache */
if (manifest == NULL) if (manifest == NULL)
{ {
unsetenv("D4R_ZLUDA_NATIVE_DIR"); unsetenv("D4R_ZLUDA_NATIVE_DIR");
@@ -511,7 +511,7 @@ static void prepare_native_kernels(const char* cache_home)
/* <cache>/d4r-native/<pid>; directories of processes that no longer exist are removed */ /* <cache>/d4r-native/<pid>; directories of processes that no longer exist are removed */
char base[1024]; char base[1024];
snprintf(base, sizeof(base), "%s/d4r-native", cache_home != NULL ? cache_home : "/tmp"); snprintf(base, sizeof(base), "%s/d4r-native", cache_home);
make_directories(base); make_directories(base);
DIR* directory = opendir(base); DIR* directory = opendir(base);
if (directory != NULL) if (directory != NULL)
@@ -531,15 +531,18 @@ static void prepare_native_kernels(const char* cache_home)
} }
snprintf(native_served, sizeof(native_served), "%s/%ld", base, (long)getpid()); snprintf(native_served, sizeof(native_served), "%s/%ld", base, (long)getpid());
remove_directory(native_served); remove_directory(native_served);
if (mkdir(native_served, 0755) != 0) char* resolved = realpath(source, NULL);
if (resolved == NULL || strlen(resolved) >= sizeof(native_source) || mkdir(native_served, 0755) != 0)
{ {
set_load_error("cannot create %s; native kernels disabled", native_served); free(resolved);
set_load_error("cannot create %s from %s; native kernels disabled", native_served, source);
native_served[0] = '\0'; native_served[0] = '\0';
native_kernel_count = 0; native_kernel_count = 0;
unsetenv("D4R_ZLUDA_NATIVE_DIR"); unsetenv("D4R_ZLUDA_NATIVE_DIR");
return; return;
} }
snprintf(native_source, sizeof(native_source), "%s", source); snprintf(native_source, sizeof(native_source), "%s", resolved);
free(resolved);
setenv("D4R_ZLUDA_NATIVE_DIR", native_served, 1); setenv("D4R_ZLUDA_NATIVE_DIR", native_served, 1);
atexit(native_cleanup); atexit(native_cleanup);
tracef("native kernels: %zu in %s, each served from %s once DLSS's PTX for it matches", tracef("native kernels: %zu in %s, each served from %s once DLSS's PTX for it matches",
@@ -696,7 +699,7 @@ static void verify_native_kernels(const void* image)
memcpy(&flags, bytes + offset + 40, sizeof(flags)); memcpy(&flags, bytes + offset + 40, sizeof(flags));
if (entry_header >= 64) if (entry_header >= 64)
memcpy(&decompressed, bytes + offset + 56, sizeof(decompressed)); memcpy(&decompressed, bytes + offset + 56, sizeof(decompressed));
if (entry_header < 16 || entry_size > end - offset - entry_header) if (entry_header < 16 || entry_header > end - offset || entry_size > end - offset - entry_header)
break; break;
const unsigned char* payload = bytes + offset + entry_header; const unsigned char* payload = bytes + offset + entry_header;
if (kind == 1) /* PTX */ if (kind == 1) /* PTX */
@@ -727,8 +730,8 @@ static struct
{ {
CUfunction function; CUfunction function;
int native; int native;
} output_kernels[OUTPUT_KERNEL_CAPACITY]; } output_kernels[OUTPUT_KERNEL_CAPACITY]; /* the latest lookups, newest last */
static int output_kernel_count; static pthread_mutex_t output_kernel_lock = PTHREAD_MUTEX_INITIALIZER;
static int last_output_native = -1; static int last_output_native = -1;
static void note_function_lookup(CUfunction function, const char* name) static void note_function_lookup(CUfunction function, const char* name)
@@ -745,25 +748,24 @@ static void note_function_lookup(CUfunction function, const char* name)
snprintf(path, sizeof(path), "%s/%s.hsaco", directory, name); snprintf(path, sizeof(path), "%s/%s.hsaco", directory, name);
native = access(path, R_OK) == 0; native = access(path, R_OK) == 0;
} }
pthread_mutex_lock(&native_lock); pthread_mutex_lock(&output_kernel_lock);
if (output_kernel_count < OUTPUT_KERNEL_CAPACITY) memmove(output_kernels, output_kernels + 1, sizeof(output_kernels) - sizeof(output_kernels[0]));
{ output_kernels[OUTPUT_KERNEL_CAPACITY - 1].function = function;
output_kernels[output_kernel_count].function = function; output_kernels[OUTPUT_KERNEL_CAPACITY - 1].native = native;
output_kernels[output_kernel_count].native = native; pthread_mutex_unlock(&output_kernel_lock);
__atomic_store_n(&output_kernel_count, output_kernel_count + 1, __ATOMIC_RELEASE);
}
pthread_mutex_unlock(&native_lock);
} }
static void note_launch(CUfunction function) static void note_launch(CUfunction function)
{ {
const int count = __atomic_load_n(&output_kernel_count, __ATOMIC_ACQUIRE); pthread_mutex_lock(&output_kernel_lock);
for (int i = 0; i < count; ++i) /* newest first, so a recycled handle resolves with its latest lookup */
for (int i = OUTPUT_KERNEL_CAPACITY - 1; i >= 0 && output_kernels[i].function != NULL; --i)
if (output_kernels[i].function == function) if (output_kernels[i].function == function)
{ {
__atomic_store_n(&last_output_native, output_kernels[i].native, __ATOMIC_RELAXED); __atomic_store_n(&last_output_native, output_kernels[i].native, __ATOMIC_RELAXED);
return; break;
} }
pthread_mutex_unlock(&output_kernel_lock);
} }
/* 1 when the last output kernel launched was d4r's native one, 0 when it was ZLUDA's compile of /* 1 when the last output kernel launched was d4r's native one, 0 when it was ZLUDA's compile of
@@ -805,35 +807,33 @@ static void load_zluda(void)
} }
load_error[0] = '\0'; load_error[0] = '\0';
const char* cache = getenv("D4R_ZLUDA_CACHE_HOME"); const char* cache = getenv("D4R_ZLUDA_CACHE_HOME");
char cache_home[1024] = {0}; const char* xdg = getenv("XDG_CACHE_HOME");
const char* home = getenv("HOME");
char cache_home[1024] = {0}, root[1024] = {0};
if (cache != NULL && cache[0] != '\0') if (cache != NULL && cache[0] != '\0')
{ {
expand_home(cache, cache_home, sizeof(cache_home)); expand_home(cache, cache_home, sizeof(cache_home));
make_directories(cache_home); make_directories(cache_home);
} }
/* the cache root; no /tmp fallback, since other users can plant links there */
if (cache_home[0] != '\0')
snprintf(root, sizeof(root), "%s", cache_home);
else if (xdg != NULL && xdg[0] != '\0')
snprintf(root, sizeof(root), "%s", xdg);
else if (home != NULL)
snprintf(root, sizeof(root), "%s/.cache", home);
if (accuracy) if (accuracy)
{ {
/* FAST_MATH is not fingerprinted by older ZLUDA runtimes. Use a separate cache even /* FAST_MATH is not fingerprinted by older ZLUDA runtimes. Use a separate cache even
when a previous run explicitly enabled that experimental compiler switch. */ when a previous run explicitly enabled that experimental compiler switch. */
char base[1024]; if (snprintf(cache_home, sizeof(cache_home), "%s/d4r-accuracy", root[0] != '\0' ? root : "/tmp/.cache") >= (int)sizeof(cache_home))
const char* xdg = getenv("XDG_CACHE_HOME");
if (cache_home[0] != '\0')
snprintf(base, sizeof(base), "%s", cache_home);
else if (xdg != NULL && xdg[0] != '\0')
snprintf(base, sizeof(base), "%s", xdg);
else
{
const char* home = getenv("HOME");
snprintf(base, sizeof(base), "%s/.cache", home != NULL ? home : "/tmp");
}
if (snprintf(cache_home, sizeof(cache_home), "%s/d4r-accuracy", base) >= (int)sizeof(cache_home))
{ {
set_load_error("accuracy cache path is too long"); set_load_error("accuracy cache path is too long");
return; return;
} }
make_directories(cache_home); make_directories(cache_home);
} }
prepare_native_kernels(cache_home[0] != '\0' ? cache_home : NULL); prepare_native_kernels(root[0] == '\0' ? NULL : accuracy ? cache_home : root);
if (cache_home[0] != '\0') if (cache_home[0] != '\0')
{ {
/* ZLUDA picks its cache directory once, in cuInit */ /* ZLUDA picks its cache directory once, in cuInit */
@@ -2482,6 +2482,8 @@ CUresult WINAPI cuDestroyExternalMemory(CUexternalMemory memory)
return result; return result;
} }
CUresult WINAPI cuSurfObjectDestroy(CUsurfObject object);
CUresult WINAPI cuSurfObjectCreate(CUsurfObject* object, const void* descriptor) CUresult WINAPI cuSurfObjectCreate(CUsurfObject* object, const void* descriptor)
{ {
CUSURFOBJECTCREATE_FN function = (CUSURFOBJECTCREATE_FN)find_zluda_symbol("cuSurfObjectCreate"); CUSURFOBJECTCREATE_FN function = (CUSURFOBJECTCREATE_FN)find_zluda_symbol("cuSurfObjectCreate");
@@ -2495,8 +2497,8 @@ CUresult WINAPI cuSurfObjectCreate(CUsurfObject* object, const void* descriptor)
const CUdeviceptr pointer = redirect_pointer; const CUdeviceptr pointer = redirect_pointer;
const uint32_t pitch = redirect_pitch; const uint32_t pitch = redirect_pitch;
pthread_mutex_unlock(&instrumentation_lock); pthread_mutex_unlock(&instrumentation_lock);
if (redirected) if (redirected && (result = write_redirect_tail(*object, pointer, pitch, 0)) != CUDA_SUCCESS)
write_redirect_tail(*object, pointer, pitch, 0); cuSurfObjectDestroy(*object);
} }
TRACE_CALL(result, "cuSurfObjectCreate descriptor=%p result=%d object=0x%llx", descriptor, result, TRACE_CALL(result, "cuSurfObjectCreate descriptor=%p result=%d object=0x%llx", descriptor, result,
(unsigned long long)(object != NULL ? *object : 0)); (unsigned long long)(object != NULL ? *object : 0));