-
Notifications
You must be signed in to change notification settings - Fork 77
Description
DBD::mysql version
4.050
MySQL client version
8.0.39
Server version
8.0.39-0ubuntu0.20.04.1
Operating system version
Ubuntu 20.04.6 LTS
What happened?
Started work in an older codebase. I tried using DBI::Log
module in a test script, and got a segfault.
$ perl test.pl
-- Wed Oct 15 13:36:21 2024
-- /home/pyrrhlin/repo/path/DB.pm 160
SET NAMES 'utf8'
Segmentation fault (core dumped)
As you can see, the first query, using $dbh->do()
is fine. The second query is an $sth->execute(42)
on a SELECT
statement with one placeholder, an integer id field, that causes the segfault. (Note: nowhere did we use $sth->bind_param
.)
I used perl debugger to find the offending statement, and it's this one:
%values = %{$sth->{ParamValues}};
So this module uses the statement handle attribute ParamValues
to find out what the bound parameter values were. This is a sometimes supported interface from DBI. It's expected to return a hashref, where
keys of the hash are the 'names' of the placeholders, typically integers starting at 1. Returns undef if not supported by the driver
However, it should be noted that:
Some drivers cannot provide valid values for some or all of these attributes until after
$sth->execute
has been successfully called. Typically the attribute will beundef
in these situations
So, it's clear to me that DBI::Log
should not be trying to read this data before execution, because for some drivers (including obviously mysql), the data won't be there yet. Regardless, if the attribute value was undef
, as DBI docs suggest, the line causing the segfault would not have been executed. To have entered the if
block, the attribute must have had some true value.
In the debugger again, I find what it is:
DB<2> c
-- Wed Oct 16 15:17:50 2024
-- /home/pyrrhlin/repo/path/DB.pm 160
SET NAMES 'utf8'
DBI::Log::pre_query(/home/pyrrhlin/repo/path/DBI/Log.pm:197):
197: if ($sth && $sth->{ParamValues}) {
DB<2> print 7 if $sth && $sth->{ParamValues}
7
DB<3> print ref $sth->{ParamValues}
HASH
DB<4> x $sth->{ParamValues}
0 HASH(0x559dc82a05f8)
0 => undef
DB<5> q
So the ParamValues
attribute had the true value { 0 => undef }
.
However, that data structure was not a "pure-perl" clean hashref, as dereferencing it in list context caused the segfault. Alternative code to copy the hash such as this did not trigger a segfault:
my $h = $sth->{ParamValues}; $values{$_} = $h->{$_} for keys %$h`
Now, reading statement handle docs for DBD::mysql
there is no mention of ParamValues
attribute, which raises some question whether it is supported at all. The docs do confirm however that:
most attributes are valid only after a successful
execute
. Anundef
value will returned otherwise
Testing shows that after execution, the attribute is populated with e.g., { 0 => 42 }
, which is almost useful, but non-compliant with the DBI docs (which required the first argument to be under key 1
).
So we have these problems:
-
under no circumstance should accessing the parameter value, regardless of context, cause a segfault.
-
before execution, if we can't provide the values yet, the attribute value should be
undef
. -
after execution, it should probably be using integer keys starting with 1, e.g.
{ 1 => 42 }
Other information
Testing performed with system perl on Ubuntu 20.04 LTS :
This is perl 5, version 30, subversion 0 (v5.30.0) built for x86_64-linux-gnu-thread-multi
(with 60 registered patches, see perl -V for more detail)
Apologies I did not have time to try to replicate the failure with a newer version. If I get more time I will try to do that.
Issue #353 also suggests trouble with the xs building the data structures tied behind the statement handle attributes. Perhaps related.