On Tue, 20 Aug 2002, Tim Bunce wrote:
> On Tue, Aug 20, 2002 at 07:18:23PM +1000, Bradley Baetz wrote:
> Ah. I think ExampleP predates _get_fbav. I'll mandate that all
> drivers must use _get_fbav (either in C or perl) to get the row
> buffer. So the right approach here is to use $sth->_get_fbav()
> and then deal with whatever knock-on effects appear.
I can't work out how to use this from the perl code to get the correct
results. Whatever I try, I can't seem to either get the tatint tests to
pass, or not regress other tests I'm sure I'm missing something obvious...
However, _get_fbav has the comment that the TAINT; "affects sv_setsv()'s
called within same perl statement".
In the perl code, I'm not in the same statement, and there aren't any
sv_setsv's (since the entire hash isn't tainted, only (possibly) values
within it) - is that the problem?
I can't find docs on most of the XS taint stuff you do, so I'm not sure if
I'm reading this correctly.
> > If I reenable that part of the code, then the taint tests pass, but
> > selectall_hashref fails at the ->bind_col in DBI.pm because $index is
> > tainted; this is presumably why that was disabled.
>
> Um, not sure what that's about off-hand. Can you repost once you've
> switched ExampleP over to using $sth->_get_fbav()?
If I could do that, then I can leave that code disabled, so its OK.
Anyway, I've attached a patch (w/o docs, for the moment) which fails the
tests for when the output stuff is meant to be tainted. I also decided to
name the attributes TaintIn and TaintOut rather than TaintInput and
TaintOutput, for no real reason except personal preference.
Let me know what you think.
Bradley
diff -ur DBI-1.30/DBI.xs DBI/DBI.xs
--- DBI-1.30/DBI.xs Fri Jul 19 00:23:51 2002
+++ DBI/DBI.xs Tue Aug 20 16:20:05 2002
@@ -932,8 +932,9 @@
if (DBIc_is(imp_xxh, DBIcf_BegunWork)) sv_catpv(flags,"BegunWork ");
if (DBIc_is(imp_xxh, DBIcf_LongTruncOk)) sv_catpv(flags,"LongTruncOk ");
if (DBIc_is(imp_xxh, DBIcf_MultiThread)) sv_catpv(flags,"MultiThread ");
- if (DBIc_is(imp_xxh, DBIcf_Taint)) sv_catpv(flags,"Taint ");
+ if (DBIc_is(imp_xxh, DBIcf_TaintOut)) sv_catpv(flags,"TaintOut ");
if (DBIc_is(imp_xxh, DBIcf_Profile)) sv_catpv(flags,"Profile ");
+ if (DBIc_is(imp_xxh, DBIcf_TaintIn)) sv_catpv(flags,"TaintIn ");
PerlIO_printf(DBILOGFP,"%s FLAGS 0x%lx: %s\n", pad, (long)DBIc_FLAGS(imp_xxh),
SvPV(flags,lna));
PerlIO_printf(DBILOGFP,"%s PARENT %s\n", pad,
neatsvpv((SV*)DBIc_PARENT_H(imp_xxh),0));
PerlIO_printf(DBILOGFP,"%s KIDS %ld (%ld Active)\n", pad,
@@ -1109,7 +1110,7 @@
if ( (av = DBIc_FIELDS_AV(imp_sth)) == Nullav)
av = dbih_setup_fbav(imp_sth);
- if (DBIc_is(imp_sth, DBIcf_Taint)) {
+ if (DBIc_is(imp_sth, DBIcf_TaintOut)) {
dTHR;
TAINT; /* affects sv_setsv()'s called within same perl statement */
}
@@ -1321,7 +1322,14 @@
}
}
else if (strEQ(key, "Taint")) {
- DBIc_set(imp_xxh,DBIcf_Taint, on);
+ /* 'Taint' is a shortcut for both in and out mode */
+ DBIc_set(imp_xxh,DBIcf_TaintIn|DBIcf_TaintOut, on);
+ }
+ else if (strEQ(key, "TaintIn")) {
+ DBIc_set(imp_xxh,DBIcf_TaintIn, on);
+ }
+ else if (strEQ(key, "TaintOut")) {
+ DBIc_set(imp_xxh,DBIcf_TaintOut, on);
}
else if (htype<=DBIt_DB && keylen==10 && strEQ(key, "CachedKids")) {
D_imp_dbh(h); /* XXX also for drh */
@@ -1536,7 +1544,14 @@
valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_MultiThread));
}
else if (keylen==5 && strEQ(key, "Taint")) {
- valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_Taint));
+ valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_TaintIn) &&
+ DBIc_has(imp_xxh,DBIcf_TaintOut));
+ }
+ else if (keylen==7 && strEQ(key, "TaintIn")) {
+ valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_TaintIn));
+ }
+ else if (keylen==8 && strEQ(key, "TaintOut")) {
+ valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_TaintOut));
}
else if (htype<=DBIt_DB && keylen==10 && strEQ(key, "AutoCommit")) {
valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_AutoCommit));
@@ -2144,7 +2159,7 @@
}
if (tainting && items > 1 /* method call has args */
- && DBIc_is(imp_xxh, DBIcf_Taint) /* taint checks requested */
+ && DBIc_is(imp_xxh, DBIcf_TaintIn) /* taint checks requested */
&& !(ima && ima->flags & IMA_NO_TAINT_IN)
) {
for(i=1; i < items; ++i) {
@@ -2337,7 +2352,7 @@
}
PerlIO_printf(logfp,"%c%c <- %s",
(call_depth > 1) ? '0'+call_depth-1 : ' ',
- (DBIc_is(imp_xxh, DBIcf_Taint)) ? 'T' : ' ',
+ (DBIc_is(imp_xxh, DBIcf_TaintIn|DBIcf_TaintOut)) ? 'T' : ' ',
meth_name);
if (debug==1 && items>=2) { /* make level 1 more useful */
/* we only have the first two parameters available here */
@@ -2401,7 +2416,7 @@
}
if (tainting
- && DBIc_is(imp_xxh, DBIcf_Taint) /* taint checks requested */
+ && DBIc_is(imp_xxh, DBIcf_TaintOut) /* taint checks requested */
/* XXX this would taint *everything* being returned from *any* */
/* method that doesn't have IMA_NO_TAINT_OUT set. */
/* DISABLED: just tainting fetched data in get_fbav seems ok */
Only in DBI: DBI.xs~
diff -ur DBI-1.30/DBIXS.h DBI/DBIXS.h
--- DBI-1.30/DBIXS.h Mon Jul 15 21:19:04 2002
+++ DBI/DBIXS.h Tue Aug 20 18:52:14 2002
@@ -227,11 +227,12 @@
#define DBIcf_AutoCommit 0x000200 /* dbh only. used by drivers */
#define DBIcf_LongTruncOk 0x000400 /* truncation to LongReadLen is okay */
#define DBIcf_MultiThread 0x000800 /* allow multiple threads to enter */
-#define DBIcf_Taint 0x001000 /* taint fetched data */
+#define DBIcf_TaintIn 0x001000 /* taint fetched data */
#define DBIcf_ShowErrorStatement 0x002000 /* include Statement in error */
#define DBIcf_BegunWork 0x004000 /* between begin_work & commit/rollback */
#define DBIcf_HandleError 0x008000 /* has coderef in HandleError attribute */
#define DBIcf_Profile 0x010000 /* profile activity on this handle */
+#define DBIcf_TaintOut 0x020000 /* Check inputs for taintedness */
#define DBIcf_INHERITMASK /* what NOT to pass on to children */ \
(U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY \
Only in DBI: DBIXS.h~
Only in DBI: Makefile.old
diff -ur DBI-1.30/lib/DBD/ExampleP.pm DBI/lib/DBD/ExampleP.pm
--- DBI-1.30/lib/DBD/ExampleP.pm Mon Jul 15 21:19:20 2002
+++ DBI/lib/DBD/ExampleP.pm Wed Aug 21 11:09:14 2002
@@ -326,6 +326,8 @@
$sth->finish;
return;
}
+ # untaint $f so that we can use this for DBI taint tests
+ ($f) = ($f =~ m/^(.*)$/);
my $file = $haveFileSpec
? File::Spec->catfile($dir, $f) : "$dir/$f";
# put in all the data fields
@@ -334,7 +336,7 @@
# return just what fields the query asks for
my @new = @s{ @{$sth->{NAME}} };
-
+
return $sth->_set_fbav(\@new);
}
*fetchrow_arrayref = \&fetch;
Only in DBI/lib/DBD: ExampleP.pm~
diff -ur DBI-1.30/lib/DBI/PurePerl.pm DBI/lib/DBI/PurePerl.pm
--- DBI-1.30/lib/DBI/PurePerl.pm Mon Jul 15 21:19:22 2002
+++ DBI/lib/DBI/PurePerl.pm Wed Aug 21 11:04:18 2002
@@ -134,6 +134,8 @@
ShowErrorStatement
Statement
Taint
+ TaintIn
+ TaintOut
TraceLevel
Version
Warn
@@ -492,6 +494,9 @@
$i++;
}
}
+ if ($key eq "Taint") {
+ return ($h->{'TaintIn'} && $h->{'TaintOut'});
+ }
my $v = $h->{$key};
if (!defined $v && !exists $h->{$key}) {
local $^W; # hide undef warnings
@@ -507,6 +512,13 @@
unless $value == -900 || $value == -901;
$value = ($value == -901);
}
+ elsif (($key eq 'Taint' || $key eq 'TaintIn' || $key eq 'TaintOut')) {
+ Carp::croak(sprintf "Can't set %s->{%s}: Taint mode not supported by
+DBI::PurePerl",$h,$key);
+ }
+ elsif ($key eq 'Taint') {
+ $h->{'TaintIn'} = $h->{'TaintOut'} = $value;
+ return 1;
+ }
elsif (!$is_valid_attribute{$key} && $key =~ /^[A-Z]/ && !exists $h->{$key}) {
Carp::croak(sprintf "Can't set %s->{%s}: unrecognised attribute or invalid
value %s",
$h,$key,$value);
@@ -752,6 +764,8 @@
InactiveDestroy
Kids
Taint
+ TaintIn
+ TaintOut
TraceLevel
(and probably others)
Only in DBI/lib/DBI: PurePerl.pm~
diff -ur DBI-1.30/t/10examp.t DBI/t/10examp.t
--- DBI-1.30/t/10examp.t Mon Jul 15 21:19:08 2002
+++ DBI/t/10examp.t Wed Aug 21 11:18:27 2002
@@ -35,11 +35,13 @@
DBI->trace(3,$trace_file); # enable trace before first driver load
my $r;
-my $dbh = DBI->connect('dbi:ExampleP(AutoCommit=>1 ,Taint = 1):', undef, undef);
+my $dbh = DBI->connect('dbi:ExampleP(AutoCommit=>1):', undef, undef);
die "Unable to connect to ExampleP driver: $DBI::errstr" unless $dbh;
ok(0, $dbh);
ok(0, ref $dbh);
+$dbh->{Taint} = 1 unless $DBI::PurePerl;
+
if (0) {
DBI->trace(9,undef);
warn DBI::dump_handle($dbh,"dump_handle",1);
@@ -67,7 +69,7 @@
#$dbh->trace(2);
$dbh->{AutoCommit} = 1;
$dbh->{PrintError} = 0;
-ok(0, $dbh->{Taint} == 1);
+ok(0, $dbh->{Taint} == 1) unless $DBI::PurePerl && ok(0,1);
ok(0, $dbh->{AutoCommit} == 1);
ok(0, $dbh->{PrintError} == 0);
#$dbh->trace(0); die;
@@ -158,6 +160,38 @@
ok(0, "@{[sort keys %{$csr_b->{NAME_uc_hash}}]}" eq "MODE NAME SIZE");
ok(0, "@{[sort values %{$csr_b->{NAME_uc_hash}}]}" eq "0 1 2");
+if ($DBI::PurePerl) {
+ warn " Taint mode switching tests skipped\n";
+ ok(0,1) foreach (1..15);
+} else {
+ # Check Taint* attribute switching
+
+ #$dbh->{'Taint'} = 1; # set in connect
+ ok(0, $dbh->{'Taint'});
+ ok(0, $dbh->{'TaintIn'} == 1);
+ ok(0, $dbh->{'TaintOut'} == 1);
+
+ $dbh->{'TaintOut'} = 0;
+ ok(0, $dbh->{'Taint'} == 0);
+ ok(0, $dbh->{'TaintIn'} == 1);
+ ok(0, $dbh->{'TaintOut'} == 0);
+
+ $dbh->{'Taint'} = 0;
+ ok(0, $dbh->{'Taint'} == 0);
+ ok(0, $dbh->{'TaintIn'} == 0);
+ ok(0, $dbh->{'TaintOut'} == 0);
+
+ $dbh->{'TaintIn'} = 1;
+ ok(0, $dbh->{'Taint'} == 0);
+ ok(0, $dbh->{'TaintIn'} == 1);
+ ok(0, $dbh->{'TaintOut'} == 0);
+
+ $dbh->{'TaintOut'} = 1;
+ ok(0, $dbh->{'Taint'} == 1);
+ ok(0, $dbh->{'TaintIn'} == 1);
+ ok(0, $dbh->{'TaintOut'} == 1);
+}
+
# get a dir always readable on all platforms
my $dir = getcwd() || cwd();
$dir = VMS::Filespec::unixify($dir) if $^O eq 'VMS';
@@ -173,7 +207,7 @@
#$csr_a->trace(2);
ok(0, $csr_a->bind_columns(undef, \($col0, $col1, $col2)) );
ok(0, $csr_a->execute( $dir ));
-ok(0, $csr_a->{Taint} = 1);
+ok(0, $csr_a->{Taint} = 1) unless $DBI::PurePerl && ok(0,1);
@row_a = $csr_a->fetchrow_array;
ok(0, @row_a);
@@ -189,29 +223,81 @@
my $foo;
return ! eval { ($foo=join('',@_)), kill 0; 1; };
}
-if (is_tainted($^X)) {
+if (is_tainted($^X) && !$DBI::PurePerl) {
print "Taint attribute test enabled\n";
- ok(0, is_tainted($row_a[0]) );
- ok(0, is_tainted($row_a[1]) );
- ok(0, is_tainted($row_a[2]) );
- # check simple method call values
- ok(0, 1);
- # check simple attribute values
- ok(0, 1); # is_tainted($dbh->{AutoCommit}) );
- # check nested attribute values (where a ref is returned)
+ $dbh->{'Taint'} = 0;
+ my $st;
+ eval { $st = $dbh->prepare($std_sql); };
+ ok(0, ref $st);
+
+ ok(0, $st->{'Taint'} == 0);
+
+ ok(0, $st->execute( $dir ));
+
+ my @row = $st->fetchrow_array;
+ ok(0, @row);
+
+ ok(0, !is_tainted($row[0]));
+ ok(0, !is_tainted($row[1]));
+ ok(0, !is_tainted($row[2]));
+
+ $st->{'TaintIn'} = 1;
+
+ @row = $st->fetchrow_array;
+ ok(0, @row);
+
+ ok(0, !is_tainted($row[0]));
+ ok(0, !is_tainted($row[1]));
+ ok(0, !is_tainted($row[2]));
+
+ $st->{'TaintOut'} = 1;
+
+ @row = $st->fetchrow_array;
+ ok(0, @row);
+
+ ok(0, is_tainted($row[0]));
+ ok(0, is_tainted($row[1]));
+ ok(0, is_tainted($row[2]));
+
+ $st->finish;
+
+ # check simple method call values
+ #ok(0, 1);
+ # check simple attribute values
+ #ok(0, 1); # is_tainted($dbh->{AutoCommit}) );
+ # check nested attribute values (where a ref is returned)
#ok(0, is_tainted($csr_a->{NAME}->[0]) );
- # check checking for tainted values
- eval { $dbh->prepare($^X); 1; };
- ok(0, $@ =~ /Insecure dependency/, $@);
- eval { $csr_a->execute($^X); 1; };
- ok(0, $@ =~ /Insecure dependency/, $@);
+ # check checking for tainted values
+
+ $dbh->{'Taint'} = $csr_a->{'Taint'} = 1;
+ eval { $dbh->prepare($^X); 1; };
+ ok(0, $@ =~ /Insecure dependency/, $@);
+ eval { $csr_a->execute($^X); 1; };
+ ok(0, $@ =~ /Insecure dependency/, $@);
undef $@;
+
+ $dbh->{'TaintIn'} = $csr_a->{'TaintIn'} = 0;
+
+ eval { $dbh->prepare($^X); 1; };
+ ok(0, !$@);
+ eval { $csr_a->execute($^X); 1; };
+ ok(0, !$@);
+
+ # Reset taint status to what it was before this block, so that
+ # tests later in the file don't get confused
+ $dbh->{'Taint'} = $csr_a->{'Taint'} = 1;
}
else {
warn " Taint attribute tests skipped\n";
- ok(0,1) foreach (1..7);
+ ok(0,1) foreach (1..19);
+}
+
+unless ($DBI::PurePerl) {
+ $csr_a->{Taint} = 0;
+ ok(0, $csr_a->{Taint} == 0);
+} else {
+ ok(0, 1);
}
-$csr_a->{Taint} = 0;
ok(0, $csr_b->bind_param(1, $dir));
ok(0, $csr_b->execute());
@@ -574,4 +660,4 @@
}
ok(0, (%tables == 0));
-BEGIN { $tests = 215; }
+BEGIN { $tests = 243; }
Only in DBI/t: 10examp.t~
Only in DBI/t: zz_01basics_pp.t
Only in DBI/t: zz_02dbidrv_pp.t
Only in DBI/t: zz_03hleak_pp.t
Only in DBI/t: zz_04mods_pp.t
Only in DBI/t: zz_05thrclone_pp.t
Only in DBI/t: zz_10examp_pp.t
Only in DBI/t: zz_15array_pp.t
Only in DBI/t: zz_20meta_pp.t
Only in DBI/t: zz_30subclass_pp.t
Only in DBI/t: zz_40profile_pp.t
Only in DBI/t: zz_60preparse_pp.t
Only in DBI/t: zz_70shell_pp.t
Only in DBI/t: zz_80proxy_pp.t