Skip to content

Remove GVL unlocking at all functions which process data modifiable in a second thread#723

Open
larskanis wants to merge 1 commit into
masterfrom
DFVULN-801-2
Open

Remove GVL unlocking at all functions which process data modifiable in a second thread#723
larskanis wants to merge 1 commit into
masterfrom
DFVULN-801-2

Conversation

@larskanis

@larskanis larskanis commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

This removes possible VM crashs when data to be sent is modified/cleared in a second thread.
It works by keeping the GVL lock for libpq functions that don't immediately process all the data and don't make a copy of it.
These are the PQsend*, PQexec* and some related functions.

Since pg-1.3 all the blocking functions or states are avoided by using the non-blocking API of libpq.
Therefore holding the GVL somewhat longer shouldn't matter that much.

Having some libpq function with and without unlocked GVL, results in rb_thread_call_with_gvl() sometimes needed and sometimes not to process callbacks.
Therefore ruby_thread_has_gvl_p() is used to check if it's needed on ruby<4.0.
In ruby-4.0+ rb_thread_call_with_gvl() doesn't care about whether GVL is already locked or not, so that it can be called in both cases.

Fixes #721

@larskanis larskanis changed the title Dfvuln 801 2 Remove GLV unlocking at all functions which process data modifiable in a second thread Jun 8, 2026
@larskanis larskanis force-pushed the DFVULN-801-2 branch 6 times, most recently from 2facd7a to b9a2e30 Compare June 8, 2026 08:31
…n a second thread

This removes possible VM crashs when data to be sent is modified/cleared in a second thread.
It works by keeping the GVL lock for libpq functions that don't immediately process all the data and don't make a copy of it.
These are the `PQsend*`, `PQexec*` and some related functions.

Since pg-1.3 all the blocking functions or states are avoided by using the non-blocking API of libpq.
Therefore holding the GVL somewhat longer shouldn't matter that much.

Having some libpq function with and without unlocked GVL, results in `rb_thread_call_with_gvl()` sometimes needed and sometimes not to process callbacks.
Therefore `ruby_thread_has_gvl_p()` is used to check if it's needed on ruby<4.0.
In ruby-4.0+ `rb_thread_call_with_gvl()` doesn't care about whether GVL is already locked or not, so that it can be called in both cases.

Fixes #721

@cbandy cbandy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 PR title says GLV instead of GVL

Comment thread ext/gvl_wrappers.h


/*
* Definitions of blocking functions and their parameters

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to explain how one decides which functions to include/exclude from this list.

Comment thread ext/gvl_wrappers.h
Comment on lines -259 to -266
function(PQsendQuery, GVL_TYPE_NONVOID, int, const char *, query) \
function(PQsendQueryParams, GVL_TYPE_NONVOID, int, int, resultFormat) \
function(PQsendPrepare, GVL_TYPE_NONVOID, int, const Oid *, paramTypes) \
function(PQsendQueryPrepared, GVL_TYPE_NONVOID, int, int, resultFormat) \
function(PQsendDescribePrepared, GVL_TYPE_NONVOID, int, const char *, stmt) \
function(PQsendDescribePortal, GVL_TYPE_NONVOID, int, const char *, portal) \
function(PQsendClosePrepared, GVL_TYPE_NONVOID, int, const char *, stmt) \
function(PQsendClosePortal, GVL_TYPE_NONVOID, int, const char *, portal) \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are async, and docs describe them as returns 1 if it was able to dispatch the request, and 0 if not. Is it just a socket write? What can block during these?

@larskanis larskanis changed the title Remove GLV unlocking at all functions which process data modifiable in a second thread Remove GVL unlocking at all functions which process data modifiable in a second thread Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DFVULN-801: Query Parameter Lifetime Bug Causes Heap Use-After-Free

2 participants