Make PDO connection construction single-shot#22874
Conversation
980028e to
4b9e4d9
Compare
Constructing a PDO handle over an already constructed one re-ran connection setup; for a persistent connection the second pass freed the pemalloc'd dbh with efree() and corrupted the heap. Besides a plain second __construct(), the same sink was reachable by reentering during a uri: DSN stream open, by retrying a persistent construct that failed after swapping in the persistent handle, and by a subclass destructor reentering while a failed persistent connect() unwinds. Track construction with an is_constructing flag, set as soon as the handle exists for both __construct() and connect(), carried onto the persistent handle and cleared only on success; construction is rejected once the driver is attached or a construction is in progress. A construct that fails now leaves the handle unusable instead of allowing a retry.
4b9e4d9 to
b55dcee
Compare
|
Why disallow it? Why not just fix the heap corruption? |
|
I can't think of a legitimate use for re-running |
|
I looked at this PR again, and maybe you are right, but I still don't quite follow your fix. Wouldn't something like this work better? |
|
The I ran your patch against the PR's test: the two reconstruct cases throw as expected, but the three reentrant ones aren't rejected. The
|
Constructing a PDO over an already constructed handle re-runs connection setup and, for a persistent connection, frees the pemalloc'd dbh with efree(), corrupting the heap. The same sink is also reachable by reentering __construct() from a uri: DSN stream wrapper or a failed connect()'s destructor, and by retrying a persistent construct that failed after swapping in the handle. This makes construction single-shot: an is_constructing flag (an ABI-neutral bit taken from _reserved_flags, sizeof(pdo_dbh_t) unchanged) is set as soon as the handle exists for both __construct() and connect(), and construction is rejected once the driver is attached or one is already in progress. Behavior change: a construct that fails now leaves the handle unusable instead of allowing a retry.