v0.7.17 - Fixed critical race condition in CLOSE message handler causing segfault during subscription storms

This commit is contained in:
Your Name
2025-10-15 09:10:18 -04:00
parent e833dcefd4
commit b041654611
3 changed files with 28 additions and 14 deletions

View File

@@ -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;
}

View File

@@ -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");