mirror of
https://github.com/yattee/yattee.git
synced 2026-10-23 13:12:53 +00:00
Give each thread its own libsmbclient talloc stackframe
One build 270 crash log aborts in talloc (talloc_abort_unknown_value) inside libsmbclient name resolution while a media-source download opens a file through SMBBridge, with FFmpeg reading an smb:// file on another thread at the same time. libsmbclient keeps its talloc stackframe behind a table of thread functions, and none was installed (this build does not export smbc_thread_posix()), so every thread shared one stackframe and the two users freed each other's allocations. Install a pthread implementation of smb_thread_functions at load time, before any libsmbclient use, including FFmpeg's. The struct layout matches what talloc_stack and smb_threads in the bundled library read (get_tls at +0x30, lock_mutex at +0x10).
This commit is contained in:
@@ -17,6 +17,113 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// MARK: - libsmbclient thread support
|
||||
//
|
||||
// libsmbclient keeps its talloc stackframe and a few global locks behind a
|
||||
// table of thread functions. Without one installed (smbc_thread_posix() is not
|
||||
// exported by this build), every thread shares a single stackframe, so two
|
||||
// threads using libsmbclient at once - a download through this bridge and
|
||||
// FFmpeg playing an smb:// file - free each other's allocations and abort in
|
||||
// talloc. The table mirrors `struct smb_thread_functions` from Samba's
|
||||
// lib/util/smb_threads.h; this is the SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION.
|
||||
|
||||
struct smb_thread_functions {
|
||||
int (*create_mutex)(const char *lockname, void **pplock, const char *location);
|
||||
void (*destroy_mutex)(void *plock, const char *location);
|
||||
int (*lock_mutex)(void *plock, int lock_type, const char *location);
|
||||
int (*create_tls)(const char *keyname, void **ppkey, const char *location);
|
||||
void (*destroy_tls)(void **ppkey, const char *location);
|
||||
int (*set_tls)(void *pkey, const void *pval, const char *location);
|
||||
void *(*get_tls)(void *pkey, const char *location);
|
||||
};
|
||||
|
||||
extern int smb_thread_set_functions(const struct smb_thread_functions *tf);
|
||||
|
||||
#define SMB_THREAD_UNLOCK 2
|
||||
|
||||
static pthread_mutex_t smb_create_tls_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int smb_create_mutex_pthread(const char *lockname, void **pplock, const char *location) {
|
||||
pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
||||
if (!mutex) return ENOMEM;
|
||||
pthread_mutex_init(mutex, NULL);
|
||||
*pplock = mutex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smb_destroy_mutex_pthread(void *plock, const char *location) {
|
||||
pthread_mutex_destroy((pthread_mutex_t *)plock);
|
||||
free(plock);
|
||||
}
|
||||
|
||||
static int smb_lock_pthread(void *plock, int lock_type, const char *location) {
|
||||
if (lock_type == SMB_THREAD_UNLOCK) {
|
||||
return pthread_mutex_unlock((pthread_mutex_t *)plock);
|
||||
}
|
||||
return pthread_mutex_lock((pthread_mutex_t *)plock);
|
||||
}
|
||||
|
||||
static int smb_create_tls_pthread(const char *keyname, void **ppkey, const char *location) {
|
||||
pthread_mutex_lock(&smb_create_tls_mutex);
|
||||
if (*ppkey) {
|
||||
pthread_mutex_unlock(&smb_create_tls_mutex);
|
||||
return 0;
|
||||
}
|
||||
pthread_key_t *key = (pthread_key_t *)malloc(sizeof(pthread_key_t));
|
||||
if (!key) {
|
||||
pthread_mutex_unlock(&smb_create_tls_mutex);
|
||||
return ENOMEM;
|
||||
}
|
||||
int ret = pthread_key_create(key, NULL);
|
||||
if (ret) {
|
||||
free(key);
|
||||
pthread_mutex_unlock(&smb_create_tls_mutex);
|
||||
return ret;
|
||||
}
|
||||
*ppkey = key;
|
||||
pthread_mutex_unlock(&smb_create_tls_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smb_destroy_tls_pthread(void **ppkey, const char *location) {
|
||||
pthread_mutex_lock(&smb_create_tls_mutex);
|
||||
if (*ppkey) {
|
||||
pthread_key_delete(*(pthread_key_t *)*ppkey);
|
||||
free(*ppkey);
|
||||
*ppkey = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(&smb_create_tls_mutex);
|
||||
}
|
||||
|
||||
static int smb_set_tls_pthread(void *pkey, const void *pval, const char *location) {
|
||||
if (!pkey) return -1;
|
||||
return pthread_setspecific(*(pthread_key_t *)pkey, pval) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
// talloc reads its stackframe before the key exists (the key is created on
|
||||
// the first stackframe), so a missing key reads as "no value yet".
|
||||
static void *smb_get_tls_pthread(void *pkey, const char *location) {
|
||||
if (!pkey) return NULL;
|
||||
return pthread_getspecific(*(pthread_key_t *)pkey);
|
||||
}
|
||||
|
||||
static const struct smb_thread_functions smb_pthread_functions = {
|
||||
smb_create_mutex_pthread,
|
||||
smb_destroy_mutex_pthread,
|
||||
smb_lock_pthread,
|
||||
smb_create_tls_pthread,
|
||||
smb_destroy_tls_pthread,
|
||||
smb_set_tls_pthread,
|
||||
smb_get_tls_pthread,
|
||||
};
|
||||
|
||||
// Installed at load time: the stackframe must become thread-local before any
|
||||
// libsmbclient use, including FFmpeg's, which this bridge does not see.
|
||||
__attribute__((constructor))
|
||||
static void smb_install_thread_functions(void) {
|
||||
smb_thread_set_functions(&smb_pthread_functions);
|
||||
}
|
||||
|
||||
// Context wrapper that bundles SMBCCTX with auth data
|
||||
struct SMBContextWrapper {
|
||||
SMBCCTX *ctx; // Isolated libsmbclient context
|
||||
|
||||
Reference in New Issue
Block a user