Fix crash on status change during reconnect

With soft reconnects it is now possible for bitlbee to request status
change in the middle of reconnect causing a write to uninitialized ssl
socket and consequent segfault.
Postpone setting status until we are in a WS_READY state.
This commit is contained in:
Artem Savkov 2018-04-10 21:59:58 +02:00
parent 5a92f69aef
commit 4fc5649367
2 changed files with 52 additions and 21 deletions

View File

@ -23,6 +23,14 @@
#include "discord-util.h"
#include "discord.h"
#define DISCORD_STATUS_TIMEOUT 500
typedef struct {
discord_data *dd;
gboolean idle;
gchar *msg;
} status_data;
static gchar *discord_ws_mask(guchar key[4], const char *pload,
guint64 psize)
{
@ -340,27 +348,21 @@ int discord_ws_init(struct im_connection *ic, discord_data *dd)
return 0;
}
static void discord_ws_remove_event(gint *event)
{
if (*event > 0) {
b_event_remove(*event);
*event = 0;
}
}
void discord_ws_cleanup(discord_data *dd)
{
if (dd->keepalive_loop_id > 0) {
b_event_remove(dd->keepalive_loop_id);
dd->keepalive_loop_id = 0;
}
if (dd->heartbeat_timeout_id > 0) {
b_event_remove(dd->heartbeat_timeout_id);
dd->heartbeat_timeout_id = 0;
}
if (dd->wsid > 0) {
b_event_remove(dd->wsid);
dd->wsid = 0;
}
if (dd->inpa > 0) {
b_event_remove(dd->inpa);
dd->inpa = 0;
}
discord_ws_remove_event(&dd->keepalive_loop_id);
discord_ws_remove_event(&dd->heartbeat_timeout_id);
discord_ws_remove_event(&dd->status_timeout_id);
discord_ws_remove_event(&dd->wsid);
discord_ws_remove_event(&dd->inpa);
if (dd->ssl != NULL) {
ssl_disconnect(dd->ssl);
@ -368,11 +370,39 @@ void discord_ws_cleanup(discord_data *dd)
}
}
static gboolean discord_ws_status_postponed(status_data *sd, gint fd,
b_input_condition cond)
{
if (sd->dd->state != WS_READY) {
return TRUE;
}
discord_ws_set_status(sd->dd, sd->idle, sd->msg);
g_free(sd->msg);
g_free(sd);
sd->dd->status_timeout_id = 0;
return FALSE;
}
void discord_ws_set_status(discord_data *dd, gboolean idle, gchar *message)
{
GString *buf = g_string_new("");
gchar *msg = NULL;
if (dd->state != WS_READY) {
if (dd->status_timeout_id == 0) {
status_data *sdata = g_new0(status_data, 1);
sdata->dd = dd;
sdata->idle = idle;
sdata->msg = g_strdup(message);
dd->status_timeout_id = b_timeout_add(DISCORD_STATUS_TIMEOUT,
(b_event_handler)discord_ws_status_postponed, sdata);
}
return;
}
if (message != NULL) {
msg = discord_escape_string(message);
}

View File

@ -68,10 +68,11 @@ typedef struct _discord_data {
gint keepalive_interval;
gint keepalive_loop_id;
gint heartbeat_timeout_id;
gint status_timeout_id;
void *ssl;
int sslfd;
int inpa;
guint wsid;
gint inpa;
gint wsid;
guint64 seq;
guint pending_sync;
GSList *pending_reqs;