@@ -42,73 +42,91 @@ static ERL_NIF_TERM ATOM_EMPTY;
4242 * A thread-safe registry of valid channel pointers. Used to validate
4343 * capsule pointers without dereferencing them, preventing crashes from
4444 * forged PyCapsules.
45+ *
46+ * Uses a dynamic array that grows as needed. Registration never fails.
4547 * ============================================================================ */
4648
47- #define CHANNEL_REGISTRY_SIZE 1024 /* Must be power of 2 */
48- #define CHANNEL_REGISTRY_MASK (CHANNEL_REGISTRY_SIZE - 1)
49+ #define CHANNEL_REGISTRY_INITIAL_SIZE 64
50+
51+ static void * * g_channel_registry = NULL ;
52+ static size_t g_channel_registry_count = 0 ;
53+ static size_t g_channel_registry_capacity = 0 ;
54+ static pthread_mutex_t g_channel_registry_lock = PTHREAD_MUTEX_INITIALIZER ;
55+
56+ /* Initialize the registry (called once, lazily) */
57+ static bool channel_registry_init_locked (void ) {
58+ if (g_channel_registry != NULL ) return true;
59+
60+ g_channel_registry = enif_alloc (CHANNEL_REGISTRY_INITIAL_SIZE * sizeof (void * ));
61+ if (g_channel_registry == NULL ) return false;
4962
50- static void * g_channel_registry [CHANNEL_REGISTRY_SIZE ];
51- static pthread_rwlock_t g_channel_registry_lock = PTHREAD_RWLOCK_INITIALIZER ;
63+ g_channel_registry_capacity = CHANNEL_REGISTRY_INITIAL_SIZE ;
64+ g_channel_registry_count = 0 ;
65+ return true;
66+ }
5267
53- /* Hash a pointer to a registry index */
54- static inline size_t channel_registry_hash (void * ptr ) {
55- uintptr_t p = (uintptr_t )ptr ;
56- /* Mix bits for better distribution */
57- p ^= p >> 16 ;
58- p *= 0x85ebca6b ;
59- p ^= p >> 13 ;
60- return p & CHANNEL_REGISTRY_MASK ;
68+ /* Grow the registry (called when full) */
69+ static bool channel_registry_grow_locked (void ) {
70+ size_t new_capacity = g_channel_registry_capacity * 2 ;
71+ void * * new_registry = enif_alloc (new_capacity * sizeof (void * ));
72+ if (new_registry == NULL ) return false;
73+
74+ memcpy (new_registry , g_channel_registry , g_channel_registry_count * sizeof (void * ));
75+ enif_free (g_channel_registry );
76+ g_channel_registry = new_registry ;
77+ g_channel_registry_capacity = new_capacity ;
78+ return true;
6179}
6280
6381/* Register a channel pointer (called from channel_alloc) */
64- static void channel_registry_add (void * ptr ) {
65- pthread_rwlock_wrlock (& g_channel_registry_lock );
66- size_t idx = channel_registry_hash (ptr );
67- for (size_t i = 0 ; i < CHANNEL_REGISTRY_SIZE ; i ++ ) {
68- size_t slot = (idx + i ) & CHANNEL_REGISTRY_MASK ;
69- if (g_channel_registry [slot ] == NULL ) {
70- g_channel_registry [slot ] = ptr ;
71- break ;
82+ static bool channel_registry_add (void * ptr ) {
83+ pthread_mutex_lock (& g_channel_registry_lock );
84+
85+ /* Initialize on first use */
86+ if (g_channel_registry == NULL && !channel_registry_init_locked ()) {
87+ pthread_mutex_unlock (& g_channel_registry_lock );
88+ return false;
89+ }
90+
91+ /* Grow if full */
92+ if (g_channel_registry_count >= g_channel_registry_capacity ) {
93+ if (!channel_registry_grow_locked ()) {
94+ pthread_mutex_unlock (& g_channel_registry_lock );
95+ return false;
7296 }
7397 }
74- pthread_rwlock_unlock (& g_channel_registry_lock );
98+
99+ g_channel_registry [g_channel_registry_count ++ ] = ptr ;
100+ pthread_mutex_unlock (& g_channel_registry_lock );
101+ return true;
75102}
76103
77104/* Unregister a channel pointer (called from destructor) */
78105static void channel_registry_remove (void * ptr ) {
79- pthread_rwlock_wrlock (& g_channel_registry_lock );
80- size_t idx = channel_registry_hash (ptr );
81- for (size_t i = 0 ; i < CHANNEL_REGISTRY_SIZE ; i ++ ) {
82- size_t slot = (idx + i ) & CHANNEL_REGISTRY_MASK ;
83- if (g_channel_registry [slot ] == ptr ) {
84- g_channel_registry [slot ] = NULL ;
106+ pthread_mutex_lock (& g_channel_registry_lock );
107+ for (size_t i = 0 ; i < g_channel_registry_count ; i ++ ) {
108+ if (g_channel_registry [i ] == ptr ) {
109+ /* Move last element to this slot (order doesn't matter) */
110+ g_channel_registry [i ] = g_channel_registry [-- g_channel_registry_count ];
85111 break ;
86112 }
87- if (g_channel_registry [slot ] == NULL ) {
88- break ; /* Not found */
89- }
90113 }
91- pthread_rwlock_unlock (& g_channel_registry_lock );
114+ pthread_mutex_unlock (& g_channel_registry_lock );
92115}
93116
94117/* Validate a pointer without dereferencing (public API) */
95118bool channel_validate (void * ptr ) {
96119 if (ptr == NULL ) return false;
97120
98- pthread_rwlock_rdlock (& g_channel_registry_lock );
121+ pthread_mutex_lock (& g_channel_registry_lock );
99122 bool found = false;
100- size_t idx = channel_registry_hash (ptr );
101- for (size_t i = 0 ; i < CHANNEL_REGISTRY_SIZE ; i ++ ) {
102- size_t slot = (idx + i ) & CHANNEL_REGISTRY_MASK ;
103- if (g_channel_registry [slot ] == ptr ) {
123+ for (size_t i = 0 ; i < g_channel_registry_count ; i ++ ) {
124+ if (g_channel_registry [i ] == ptr ) {
104125 found = true;
105126 break ;
106127 }
107- if (g_channel_registry [slot ] == NULL ) {
108- break ; /* Not found */
109- }
110128 }
111- pthread_rwlock_unlock (& g_channel_registry_lock );
129+ pthread_mutex_unlock (& g_channel_registry_lock );
112130 return found ;
113131}
114132
@@ -172,8 +190,13 @@ py_channel_t *channel_alloc(size_t max_size) {
172190 return NULL ;
173191 }
174192
175- /* Register in the pointer registry for validation */
176- channel_registry_add (channel );
193+ /* Register in the pointer registry for validation - fail if registration fails */
194+ if (!channel_registry_add (channel )) {
195+ pthread_mutex_destroy (& channel -> mutex );
196+ enif_ioq_destroy (channel -> queue );
197+ enif_release_resource (channel );
198+ return NULL ;
199+ }
177200
178201 return channel ;
179202}
0 commit comments