diff --git a/Dockerfile.alpine-musl b/Dockerfile.alpine-musl index 7626c5d..541b14a 100644 --- a/Dockerfile.alpine-musl +++ b/Dockerfile.alpine-musl @@ -89,9 +89,9 @@ RUN cd nostr_core_lib && \ COPY src/ /build/src/ COPY Makefile /build/Makefile -# Build c-relay with full static linking (only rebuilds when src/ changes) +# Build c-relay with full static linking and debug symbols (only rebuilds when src/ changes) # Disable fortification to avoid __*_chk symbols that don't exist in MUSL -RUN gcc -static -O2 -Wall -Wextra -std=c99 \ +RUN gcc -static -g -O0 -DDEBUG -Wall -Wextra -std=c99 \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ -I. -Inostr_core_lib -Inostr_core_lib/nostr_core \ -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \ @@ -103,8 +103,8 @@ RUN gcc -static -O2 -Wall -Wextra -std=c99 \ -lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \ -lcurl -lz -lpthread -lm -ldl -# Strip binary to reduce size -RUN strip /build/c_relay_static +# DO NOT strip - we need debug symbols for debugging +# RUN strip /build/c_relay_static # Verify it's truly static RUN echo "=== Binary Information ===" && \ diff --git a/src/dm_admin.c b/src/dm_admin.c index e2dcf5c..bf0de7e 100644 --- a/src/dm_admin.c +++ b/src/dm_admin.c @@ -1086,19 +1086,30 @@ int send_nip17_response(const char* sender_pubkey, const char* response_content, } } - // Store the gift wrap in database + // Broadcast FIRST before storing (broadcasting needs the event intact) + // Make a copy for broadcasting to avoid use-after-free issues + cJSON* gift_wrap_copy = cJSON_Duplicate(gift_wraps[0], 1); + if (!gift_wrap_copy) { + cJSON_Delete(gift_wraps[0]); + strncpy(error_message, "NIP-17: Failed to duplicate gift wrap for broadcast", error_size - 1); + return -1; + } + + // Broadcast the copy to active subscriptions + broadcast_event_to_subscriptions(gift_wrap_copy); + + // Store the original in database int store_result = store_event(gift_wraps[0]); + // Clean up both copies + cJSON_Delete(gift_wrap_copy); + cJSON_Delete(gift_wraps[0]); + if (store_result != 0) { - cJSON_Delete(gift_wraps[0]); strncpy(error_message, "NIP-17: Failed to store response gift wrap", error_size - 1); return -1; } - // Broadcast the response event to active subscriptions - broadcast_event_to_subscriptions(gift_wraps[0]); - - cJSON_Delete(gift_wraps[0]); return 0; } diff --git a/src/websockets.c b/src/websockets.c index 8e97193..638e2a3 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -895,10 +895,9 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso return 0; } - // Remove from global manager - remove_subscription_from_manager(subscription_id, wsi); - - // Remove from session list if present + // CRITICAL FIX: Remove from session list FIRST (while holding lock) + // to prevent race condition where global manager frees the subscription + // while we're still iterating through the session list if (pss) { pthread_mutex_lock(&pss->session_lock); @@ -916,6 +915,10 @@ static int nostr_relay_callback(struct lws *wsi, enum lws_callback_reasons reaso pthread_mutex_unlock(&pss->session_lock); } + // Remove from global manager AFTER removing from session list + // This prevents use-after-free when iterating session subscriptions + remove_subscription_from_manager(subscription_id, wsi); + // Subscription closed } else { send_notice_message(wsi, "error: missing or invalid subscription ID in CLOSE");