Subject: | Segfault from setErrorFromDiagRecInfo |
Hello,
I found a new bug causing segfaults when the DB2 query returns no rows.
Using DBI trace, I can see I'm also getting a "silent" DB2 error during
prepare() of my SQL statement. May be this issue is triggered by a
combination of these two, I don't know.
This is the segfault itself:
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/i386/i486/strlen.S:69
69 ../sysdeps/i386/i486/strlen.S: No such file or directory.
in ../sysdeps/i386/i486/strlen.S
Current language: auto
The current source language is "auto; currently asm".
(gdb) bt
#0 strlen () at ../sysdeps/i386/i486/strlen.S:69
#1 0xb7cab776 in set_err_char (h=0x8354918, imp_xxh=0x84397f8,
err_c=0xb7c76e47 "", err_i=100, errstr=0x0, state=0xbfffec3e "00000",
method=0x0) at DBI.xs:604
#2 0xb7c6c4ea in setErrorFromDiagRecInfo (perlHandle=<value optimized
Show quoted text
out>, handleType=<value optimized out>, handle=65537, err=0xb7c76e47 "")
at dbdimp.c:112
#3 0xb7c6c693 in diagnoseError (perlHandle=0x8354918, handleType=9376,
handle=65537, rc=100, what=0xb7c76dd8 "Fetch Failed") at dbdimp.c:54
#4 0xb7c72ee7 in db2_st_fetch (sth=0x8354918, imp_sth=0x84397f8) at
dbdimp.c:2539
#5 0xb7c67cc7 in XS_DBD__DB2__st_fetchrow_arrayref (my_perl=0x8188008,
cv=0x8291950) at DB2.xs:399
#6 0xb7cb9299 in XS_DBI_dispatch (my_perl=0x8188008, cv=0x8326040) at
DBI.xs:3292
#7 0x080b12c0 in Perl_pp_entersub ()
#8 0x080af688 in Perl_runops_standard ()
#9 0x080adbb2 in perl_run ()
#10 0x08063ffd in main ()
And this is what is reported in DBI trace log:
DBI::db=HASH(0x8ad1960) trace level set to 0x0/1 (DBI @ 0x0/0) in
DBI 1.609-ithread (pid 5042)
DB2: 2009-11-20-06.00.00 - 2009-11-21-06.00.00
!! ERROR: '-99999' '[IBM][CLI Driver] CLI0137E Information type out
of range. SQLSTATE=HY096' (err#1)
<- prepare_cached('
SELECT
count(RPT.ACC_BODYHIST.KANR) count
FROM
RPT.ACC_BODYHIST
WHERE
RPT.ACC_BODYHIST.STATUS0 = ?
AND RPT.ACC_BODYHIST.MDATUMZEIT >= TIMESTAMP( CAST(? AS TIMESTAMP) )
AND RPT.ACC_BODYHIST.MDATUMZEIT < TIMESTAMP( CAST(? AS TIMESTAMP) )
GROUP BY
RPT.ACC_BODYHIST.STATUS0;
')= DBI::st=HASH(0x8ad1c90) at count line 145
!! ERROR: '-99999' CLEARED by call to execute method
<- execute('Z100', '2009-11-20-06.00.00', ...)= -1 at count line 159
Segmentation fault
The weird thing is that the prepare is actually OK and subsequent
SELECTs with actual result rows are OK. I only trigger the segfault when
there are no rows returned. I couldn't find much about CLI0137E and
whatever I do with my statement, it doesn't go away.
I have just installed a new DB2 administration client version, but I
haven't tried this perl app with previous version. This client is the
latest version available in the IBM Software Access Catalog and is much
newer than my previous one:
DB21085I Instance "dave" uses "32" bits and DB2 code release "SQL08010"
with
level identifier "01010106".
Informational tokens are "DB2 v8.1.0.0", "s021023", "", and FixPak "0".
Product is installed at "/opt/IBM/db2/V8.1".
DBI is 1.609-1 (Ubuntu)
DBD::DB2 is 1.76 (CPAN)
Anyway, I found the issue and fixed it. I'm attaching a patch for
dbdimp.c. You can see that setErrorFromDiagRecInfo() initializes var
"message" with NULL. In an obvious case, this value is not updated and
is passed to DBI's set_err_char() as "errstr", which is then duplicated
and strlen()'d without any checking.
I think DBI should sanitize its parameters, but DBD::DB2 also shouldn't
pass NULLs around unnecessarily. :) After initializing message = "", the
segfault disappears and my code is working again.
I'd be very much interested in what "CLI0137E Information type out of
range. SQLSTATE=HY096" means and why the statement works fine despite
it. Here's the same DBD::DB2 call sequence, but with the highest
DB2_TRACE. You won't get segfault this way, because when
DBIc_TRACE_LEVEL >= 3, the switch statement in setErrorFromDiagRecInfo()
updates "message" itself:
DBI::db=HASH(0x94f9600) trace level set to 0x0/5 (DBI @ 0x0/0) in
DBI 1.609-ithread (pid 10458)
DB2: 2009-11-20-06.00.00 - 2009-11-21-06.00.00
-> prepare for DBD::DB2::db (DBI::db=HASH(0x94f9690)~0x94f9600 '
SELECT
count(RPT.ACC_BODYHIST.KANR) count
FROM
RPT.ACC_BODYHIST
WHERE
RPT.ACC_BODYHIST.STATUS0 = ?
AND RPT.ACC_BODYHIST.MDATUMZEIT >= TIMESTAMP( CAST(? AS TIMESTAMP) )
AND RPT.ACC_BODYHIST.MDATUMZEIT < TIMESTAMP( CAST(? AS TIMESTAMP) )
GROUP BY
RPT.ACC_BODYHIST.STATUS0;
') thr#932d008
New 'DBI::st' (for DBD::DB2::st, parent=DBI::db=HASH(0x94f9600),
id=undef)
dbih_setup_handle(DBI::st=HASH(0x94f9930)=>DBI::st=HASH(0x94f98d0),
DBD::DB2::st, 9477790, Null!)
dbih_make_com(DBI::db=HASH(0x94f9600), 93542f8, DBD::DB2::st, 160,
0) thr#932d008
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), Err,
DBI::db=HASH(0x94f9600)) SCALAR(0x94776b0) (already defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), State,
DBI::db=HASH(0x94f9600)) SCALAR(0x9477730) (already defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), Errstr,
DBI::db=HASH(0x94f9600)) SCALAR(0x94776f0) (already defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), TraceLevel,
DBI::db=HASH(0x94f9600)) 5 (already defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), FetchHashKeyName,
DBI::db=HASH(0x94f9600)) 'NAME' (already defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), HandleSetErr,
DBI::db=HASH(0x94f9600)) undef (not defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), HandleError,
DBI::db=HASH(0x94f9600)) undef (not defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), ReadOnly,
DBI::db=HASH(0x94f9600)) undef (not defined)
dbih_setup_attrib(DBI::st=HASH(0x94f98d0), Profile,
DBI::db=HASH(0x94f9600)) undef (not defined)
dbd_st_prepare'd sql f65537
SELECT
count(RPT.ACC_BODYHIST.KANR) count
FROM
RPT.ACC_BODYHIST
WHERE
RPT.ACC_BODYHIST.STATUS0 = ?
AND RPT.ACC_BODYHIST.MDATUMZEIT >= TIMESTAMP( CAST(? AS TIMESTAMP) )
AND RPT.ACC_BODYHIST.MDATUMZEIT < TIMESTAMP( CAST(? AS TIMESTAMP) )
GROUP BY
RPT.ACC_BODYHIST.STATUS0;
statement =
SELECT
count(RPT.ACC_BODYHIST.KANR) count
FROM
RPT.ACC_BODYHIST
WHERE
RPT.ACC_BODYHIST.STATUS0 = ?
AND RPT.ACC_BODYHIST.MDATUMZEIT >= TIMESTAMP( CAST(? AS TIMESTAMP) )
AND RPT.ACC_BODYHIST.MDATUMZEIT < TIMESTAMP( CAST(? AS TIMESTAMP) )
GROUP BY
RPT.ACC_BODYHIST.STATUS0;
imp_sth->statement=
SELECT
count(RPT.ACC_BODYHIST.KANR) count
FROM
RPT.ACC_BODYHIST
WHERE
RPT.ACC_BODYHIST.STATUS0 = :p1
AND RPT.ACC_BODYHIST.MDATUMZEIT >= TIMESTAMP( CAST(:p2 AS TIMESTAMP) )
AND RPT.ACC_BODYHIST.MDATUMZEIT < TIMESTAMP( CAST(:p3 AS TIMESTAMP) )
GROUP BY
RPT.ACC_BODYHIST.STATUS0;
scanned 3 distinct placeholders
fbh 0: 'COUNT' , type 4, 11, dsize 10, p0 s154487488
out: ftype 1, indp 0, bufl 45, rlen 45
!! ERROR: '-99999' '[IBM][CLI Driver] CLI0137E Information type out
of range. SQLSTATE=HY096' (err#0)
<- prepare= DBI::st=HASH(0x94f9930) at ./count line 147 via at
./count line 36
!! ERROR: '-99999' CLEARED by call to execute method
-> execute for DBD::DB2::st (DBI::st=HASH(0x94f9930)~0x94f98d0
'Z100' '2009-11-20-06.00.00' '2009-11-21-06.00.00') thr#932d008
bind :p1 <== 'Z100' (attribs: <no attribs>)
bind :p1: db2_param_type=1, db2_c_type=1, db2_type=1, PRECISION=0,
SCALE=0, Maxlen=0, Described
bind :p2 <== '2009-11-20-06.00.00' (attribs: <no attribs>)
bind :p2: db2_param_type=1, db2_c_type=1, db2_type=93, PRECISION=0,
SCALE=6, Maxlen=0, Described
bind :p3 <== '2009-11-21-06.00.00' (attribs: <no attribs>)
bind :p3: db2_param_type=1, db2_c_type=1, db2_type=93, PRECISION=0,
SCALE=6, Maxlen=0, Described
<- execute= -1 at ./count line 161 via at ./count line 36
-> fetchrow_arrayref for DBD::DB2::st
(DBI::st=HASH(0x94f9930)~0x94f98d0) thr#932d008
!! info: '' 'SQL_NO_DATA returned from Diagnostic Information. There
might be no diagnostic records for this handle. Please see Infocentre
for more details!!' (err#2)
<- fetchrow_arrayref= undef row-1 at ./count line 163 via at
./count line 36
!! info: '' CLEARED by call to finish method
-> finish for DBD::DB2::st (DBI::st=HASH(0x94f9930)~0x94f98d0)
thr#932d008
<- finish= 1 at ./count line 165 via at ./count line 36
-> DESTROY for DBD::DB2::st (DBI::st=HASH(0x94f98d0)~INNER) thr#932d008
<- DESTROY= undef at ./count line 36 via at ./count line 36
-> trace for DBD::DB2::db (DBI::db=HASH(0x94f9690)~0x94f9600 0)
thr#932d008
<- trace= 5 at ./count line 37
I don't know what could have changed in DBI or IBM DB2, but such an
error would have been fixed long time ago. Empty result is a frequent
occurrence, this must be something new. Please, keep me up-to-date and
let me know if you need something or discover something.
David
Subject: | dbd-db2-1.76-segv.patch |
diff -ru DBD-DB2-1.76/dbdimp.c DBD-DB2-1.76-EAbIPE/dbdimp.c
--- DBD-DB2-1.76/dbdimp.c 2009-11-28 10:07:57.000000000 +0100
+++ DBD-DB2-1.76-EAbIPE/dbdimp.c 2010-01-08 21:42:34.594365191 +0100
@@ -93,6 +93,7 @@
message = msgBuffer;
} else {
err = "";
+ message = "";
sqlcode = returnCode;
strcpy( (char*)sqlstate, "00000" );
if(DBIc_TRACE_LEVEL(imp_xxh) >= 3){