I've been using the Oracle bind table functionality in DBD::Oracle, and have
discovered that if you reuse a cursor and change the size of the bound array,
then you run into memory problems. Tracing this into the DBD::Oracle code, it
looks like dbdimp.c only allocates the storage buffer the first time it enters
the binding functions (based on the value of phs->array_numstruct). However,
the array size reported to Oracle is the size of the physical perl storage
array. This means on subsequent calls with a different array size can have the
allocated memory and stored memory sizes out of sync resulting in memory
corruption.
I could not find a reason (other than a marginal performance gain) for maintain
the check preventing reallocation of the storage array, so I'm submitting a
patch to remove this.
This patch was built against DBD::Oracle v1.27
Let me know if you have any questions/comments,
Rod.
--- DBD-Oracle-1.27/dbdimp.c 2010-12-14 16:41:56.000000000 -0800
+++ DBD-Oracle-1.27.new/dbdimp.c 2011-02-18 15:24:18.000000000 -0800
@@ -1625,6 +1625,7 @@
int flag_data_is_utf8=0;
int need_allocate_rows;
int buflen;
+ int numarrayentries;
if( ( ! SvROK(phs->sv) ) || (SvTYPE(SvRV(phs->sv))!=SVt_PVAV)
) { /* Allow only array binds */
croak("dbd_rebind_ph_varchar2_table(): bad bind variable. ARRAY
reference required, but got %s for '%s'.",
neatsvpv(phs->sv,0), phs->name);
@@ -1638,9 +1639,12 @@
/* If no number of entries to bind specified,
* set phs->array_numstruct to the scalar(@array) bound.
*/
- if( phs->array_numstruct <= 0 ){
+ /* This used to check to see if phs->array_numstruct was nonzero, but
+ this doesn't allow us to resize storage if the array sizes change
+ on subsequent calls. This potentially results in a buffer overflow:
+ rodcham 20110218 */
/* av_len() returns last array index, or -1 is array is empty */
- int numarrayentries=av_len( arr );
+ numarrayentries=av_len( arr );
if( numarrayentries >= 0 ){
phs->array_numstruct = numarrayentries+1;
if (trace_level >= 2 || dbd_verbose >= 3 ){
@@ -1648,7 +1652,6 @@
phs->array_numstruct);
}
}
- }
/* Fix charset */
csform = phs->csform;
if (trace_level >= 2 || dbd_verbose >= 3 ){
@@ -2011,6 +2014,7 @@
AV *arr;
int need_allocate_rows;
int buflen;
+ int numarrayentries;
/*int flag_data_is_utf8=0;*/
if( ( ! SvROK(phs->sv) ) || (SvTYPE(SvRV(phs->sv))!=SVt_PVAV)
) { /* Allow only array binds */
@@ -2037,17 +2041,19 @@
/* If no number of entries to bind specified,
* set phs->array_numstruct to the scalar(@array) bound.
*/
- if( phs->array_numstruct <= 0 ){
-/* av_len() returns last array index, or -1 is array is empty */
- int numarrayentries=av_len( arr );
- if( numarrayentries >= 0 ){
- phs->array_numstruct =
numarrayentries+1;
- if (trace_level >= 2 ||
dbd_verbose >= 3 ){
-
PerlIO_printf(DBILOGFP, "dbd_rebind_ph_number_table(): array_numstruct=%d
(calculated) \n",
-
phs->array_numstruct);
- }
- }
- }
+ /* This used to check to see if phs->array_numstruct was nonzero, but
+ this doesn't allow us to resize storage if the array sizes change
+ on subsequent calls. This potentially results in a buffer overflow:
+ rodcham 20110218 */
+ /* av_len() returns last array index, or -1 is array is empty */
+ numarrayentries=av_len( arr );
+ if( numarrayentries >= 0 ){
+ phs->array_numstruct = numarrayentries+1;
+ if (trace_level >= 2 || dbd_verbose >= 3 ){
+ PerlIO_printf(DBILOGFP, "dbd_rebind_ph_number_table():
array_numstruct=%d (calculated) \n",
+ phs->array_numstruct);
+ }
+ }
/* Calculate each bound structure maxlen.
* maxlen(int) = sizeof(int);
* maxlen(double) = sizeof(double);