Skip to content

Commit ab080e4

Browse files
committed
sql: clear clnt->thd before handing the client back
clnt->thd outlives the sqlthdstate it points at, which is alloca'd on the pool thread's stack. Clear it before signal_clnt_as_done(). Hardening: the common path is already covered by done_cb_evbuffer() marking clnt->done. Signed-off-by: Mark Hannum <mhannum@bloomberg.net>
1 parent f9aab01 commit ab080e4

4 files changed

Lines changed: 35 additions & 3 deletions

File tree

db/reqlog.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1992,7 +1992,10 @@ void reqlog_long_running_clnt(struct sqlclntstate *clnt)
19921992
{
19931993
int have_fingerprint = 0;
19941994
char fp[FINGERPRINTSZ] = {0};
1995-
if (clnt->done || !clnt->thd || !clnt->sql || !clnt->thd->logger) return;
1995+
/* single read: worker clears clnt->thd concurrently */
1996+
struct sqlthdstate *thd = clnt->thd;
1997+
if (clnt->done || !thd || !clnt->sql || !thd->logger)
1998+
return;
19961999

19972000
if (can_consume(clnt) == 1) {
19982001
return; /* Do not log consumers */

db/sqlglue.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10268,7 +10268,11 @@ int recover_deadlock_flags(bdb_state_type *bdb_state, struct sqlclntstate *clnt,
1026810268
clnt->recover_deadlock_thd = pthread_self();
1026910269
comdb2_cheapstack_char_array(clnt->recover_deadlock_stack, RECOVER_DEADLOCK_MAX_STACK);
1027010270
#endif
10271-
recover_deadlock_sc_cleanup(clnt->thd->sqlthd);
10271+
/* use TLS thd (as recover_deadlock_flags_int does): clnt->thd may
10272+
* already be cleared when called from the post-done flush path */
10273+
struct sql_thread *sqlthd = pthread_getspecific(query_info_key);
10274+
if (sqlthd)
10275+
recover_deadlock_sc_cleanup(sqlthd);
1027210276
assert(bdb_lockref() == 0);
1027310277
} else {
1027410278
assert(bdb_lockref() > 0);

db/sqlinterfaces.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ void rcache_destroy(void);
194194
void sql_reset_sqlthread(struct sql_thread *thd);
195195
int blockproc2sql_error(int rc, const char *func, int line);
196196
static int test_no_btcursors(struct sqlthdstate *thd);
197+
static void clnt_detach_thd(struct sqlclntstate *clnt, struct sql_thread *sqlthd);
197198
static void sql_thread_describe(void *obj, FILE *out);
198199
static char *get_query_cost_as_string(struct sql_thread *, struct sqlclntstate *);
199200
void handle_sql_intrans_unrecoverable_error(struct sqlclntstate *clnt);
@@ -4626,6 +4627,7 @@ static void sqlengine_work_lua_thread(void *thddata, void *work)
46264627
osql_log_time_done(clnt);
46274628

46284629
debug_close_clnt(clnt);
4630+
clnt_detach_thd(clnt, thd->sqlthd);
46294631
signal_clnt_as_done(clnt);
46304632

46314633
thrman_setid(thrman_self(), "[done]");
@@ -4858,6 +4860,22 @@ static int can_execute_sql_query_now(
48584860
return 1;
48594861
}
48604862

4863+
/* clear before signal: signal hands clnt back to the event thread.
4864+
* nested replay call: outer frame still owns thd.
4865+
* only call this while we still own clnt (ie not after a redispatch) */
4866+
static void clnt_detach_thd(struct sqlclntstate *clnt, struct sql_thread *sqlthd)
4867+
{
4868+
if (clnt->osql.in_replay_nested)
4869+
return;
4870+
Pthread_mutex_lock(&gbl_sql_lock);
4871+
sqlthd->clnt = NULL;
4872+
Pthread_mutex_unlock(&gbl_sql_lock);
4873+
/* sql_lk: watchdog reads clnt->thd under it */
4874+
Pthread_mutex_lock(&clnt->sql_lk);
4875+
clnt->thd = NULL; /* thd is about to go away */
4876+
Pthread_mutex_unlock(&clnt->sql_lk);
4877+
}
4878+
48614879
void sqlengine_work_appsock(struct sqlthdstate *thd, struct sqlclntstate *clnt)
48624880
{
48634881
struct sql_thread *sqlthd = thd->sqlthd;
@@ -4891,7 +4909,10 @@ void sqlengine_work_appsock(struct sqlthdstate *thd, struct sqlclntstate *clnt)
48914909
if (srs_tran_replay(clnt) == RC_INTERNAL_RETRY) {
48924910
/* Another iteration was scheduled on a new worker.
48934911
* That worker now owns the clnt; do NOT signal_clnt_as_done
4894-
* here or it would race with the new worker's enqueue. */
4912+
* here or it would race with the new worker's enqueue.
4913+
* Do NOT clnt_detach_thd() either: enqueue_sql_query() already
4914+
* cleared clnt->thd before dispatching, and the new owner may
4915+
* have finished and freed clnt by now. */
48954916
thrman_setid(thrman_self(), "[done]");
48964917
return;
48974918
}
@@ -4908,6 +4929,7 @@ void sqlengine_work_appsock(struct sqlthdstate *thd, struct sqlclntstate *clnt)
49084929
clnt->osql.timings.query_finished = osql_log_time();
49094930
osql_log_time_done(clnt);
49104931
clnt_change_state(clnt, CONNECTION_IDLE);
4932+
clnt_detach_thd(clnt, sqlthd);
49114933
signal_clnt_as_done(clnt);
49124934
return;
49134935
}
@@ -4960,6 +4982,7 @@ void sqlengine_work_appsock(struct sqlthdstate *thd, struct sqlclntstate *clnt)
49604982
osql_log_time_done(clnt);
49614983
clnt_change_state(clnt, CONNECTION_IDLE);
49624984
debug_close_clnt(clnt);
4985+
clnt_detach_thd(clnt, sqlthd);
49634986
signal_clnt_as_done(clnt);
49644987

49654988
thrman_setid(thrman_self(), "[done]");

db/sqloffload.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,9 @@ int osql_clean_sqlclntstate(struct sqlclntstate *clnt)
641641
abort();
642642
}
643643

644+
int in_replay_nested = osql->in_replay_nested; /* call-stack state, not txn state */
644645
bzero(osql, sizeof(*osql));
646+
osql->in_replay_nested = in_replay_nested;
645647
listc_init(&osql->shadtbls, offsetof(struct shad_tbl, linkv));
646648

647649
sql_set_sqlengine_state(clnt, __FILE__, __LINE__, SQLENG_NORMAL_PROCESS);

0 commit comments

Comments
 (0)