stas 2003/11/03 01:29:50
Modified: src/modules/perl mod_perl.c modperl_perl_includes.h
modperl_util.c
. Changes
Added: t/perl hash_attack.t
t/response/TestPerl hash_attack.pm
Log:
Adjust the source to properly work with 5.8.2's new algorithm of dynamic
re-hashing of hashes on hash collision attack. Add a test that mounts such
an attack so we can verify that we can survive this rehashing.
Revision Changes Path
1.202 +7 -2 modperl-2.0/src/modules/perl/mod_perl.c
Index: mod_perl.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/mod_perl.c,v
retrieving revision 1.201
retrieving revision 1.202
diff -u -u -r1.201 -r1.202
--- mod_perl.c 23 Oct 2003 05:57:46 -0000 1.201
+++ mod_perl.c 3 Nov 2003 09:29:49 -0000 1.202
@@ -68,8 +68,13 @@
{
#ifdef MP_NEED_HASH_SEED_FIXUP
if (MP_init_hash_seed_set) {
- PL_hash_seed_set = TRUE;
- PL_hash_seed = MP_init_hash_seed;
+#if PERL_REVISION == 5 && PERL_VERSION == 8 && PERL_SUBVERSION == 1
+ PL_hash_seed = MP_init_hash_seed;
+ PL_hash_seed_set = MP_init_hash_seed_set;
+#else
+ PL_rehash_seed = MP_init_hash_seed;
+ PL_rehash_seed_set = MP_init_hash_seed_set;
+#endif
}
#endif
}
1.18 +4 -0 modperl-2.0/src/modules/perl/modperl_perl_includes.h
Index: modperl_perl_includes.h
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_perl_includes.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -u -r1.17 -r1.18
--- modperl_perl_includes.h 25 Nov 2002 01:31:00 -0000 1.17
+++ modperl_perl_includes.h 3 Nov 2003 09:29:49 -0000 1.18
@@ -42,6 +42,10 @@
# endif
#endif
+/* needed starting from 5.8.2 to access the PERL_HASH_INTERNAL macro
+ * in hv.h. we use it in modperl_util.c */
+#define PERL_HASH_INTERNAL_ACCESS
+
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
1.56 +6 -0 modperl-2.0/src/modules/perl/modperl_util.c
Index: modperl_util.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_util.c,v
retrieving revision 1.55
retrieving revision 1.56
diff -u -u -r1.55 -r1.56
--- modperl_util.c 9 Oct 2003 23:56:52 -0000 1.55
+++ modperl_util.c 3 Nov 2003 09:29:49 -0000 1.56
@@ -473,6 +473,12 @@
return 0;
}
+#ifdef HvREHASH
+ if (HvREHASH(hv)) {
+ PERL_HASH_INTERNAL(hash, key, klen);
+ }
+ else
+#endif
if (!hash) {
PERL_HASH(hash, key, klen);
}
1.1 modperl-2.0/t/perl/hash_attack.t
Index: hash_attack.t
===================================================================
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestUtil;
use Apache::TestTrace;
use Apache::TestRequest 'GET_BODY_ASSERT';
plan tests => 1,
have { "relevant only for perl 5.8.2 and higher" => ($] >= 5.008002) };
my $expected = "ok";
my $received = GET_BODY_ASSERT "/TestPerl__hash_attack";
ok($expected eq $received);
1.1 modperl-2.0/t/response/TestPerl/hash_attack.pm
Index: hash_attack.pm
===================================================================
package TestPerl::hash_attack;
# if the rehashing of the keys in the stash happens due to the hash attack,
# mod_perl must not fail to find the previously cached stash entry (response
# and fixup handlers in this test). Moreover it must not fail to find
# that entry on the subsequent requests.
#
# the hash attack is detected when HV_MAX_LENGTH_BEFORE_SPLIT keys
# find themselves in the same hash bucket, in which case starting from
# 5.8.2 the hash will rehash all its keys using a random hash seed
# (PL_new_hash_seed, set in mod_perl or via PERL_HASH_SEED environment
# variable)
#
# Prior to the attack condition hashes use the PL_hash_seed, which is
# always 0.
#
# only in 5.8.1 hashes always use a non-zero PL_hash_seed (unless set
# to 0 via PERL_HASH_SEED environment variable or compiled without
# -DUSE_HASH_SEED or -DUSE_HASH_SEED_EXPLICIT
use strict;
use warnings FATAL => 'all';
use Apache::TestTrace;
use Apache::Const -compile => 'OK';
use Math::BigInt;
use constant MASK_U32 => 2**32;
use constant HASH_SEED => 0; # 5.8.2: always zero before the rehashing
use constant THRESHOLD => 14; #define HV_MAX_LENGTH_BEFORE_SPLIT
use constant START => "a";
# create conditions which will trigger a rehash on the current stash
# (__PACKAGE__::). Relevant for perl 5.8.2 and higher.
sub init {
my $r = shift;
no strict 'refs';
my @attack_keys = attack(\%{__PACKAGE__ . "::"}) if $] >= 5.008002;
return Apache::DECLINED;
}
sub fixup { return Apache::OK; }
sub handler {
my $r = shift;
$r->print("ok");
return Apache::OK;
}
sub attack {
my $stash = shift;
#require Hash::Util; # avail since 5.8.0
debug "starting attack (it may take a long time!)";
my @keys;
# the minimum of bits required to mount the attack on a hash
my $min_bits = log(THRESHOLD)/log(2);
# if the hash has already been populated with a significant amount
# of entries the number of mask bits can be higher
my $keys = scalar keys %$stash;
my $bits = $keys ? log($keys)/log(2) : 0;
$bits = $min_bits if $min_bits > $bits;
$bits = int($bits) < $bits ? int($bits) + 1 : int($bits);
# need to add 2 bits to cover the internal split cases
$bits += 2;
my $mask = 2**$bits-1;
debug "mask: $mask ($bits)";
my $s = START;
my $c = 0;
# get 2 keys on top of the THRESHOLD
my $h;
while (@keys < THRESHOLD+2) {
next if exists $stash->{$s};
$h = hash($s);
next unless ($h & $mask) == 0;
$c++;
$stash->{$s}++;
debug sprintf "%2d: %5s, %10s, %s", $c, $s, $h, scalar(%$stash);
push @keys, $s;
debug "The hash collision attack has been successful"
if Internals::HvREHASH(%$stash);
} continue {
$s++;
}
# this verifies that the attack was mounted successfully. If
# HvREHASH is on it is. Otherwise the sequence wasn't successful.
die "Failed to mount the hash collision attack"
unless Internals::HvREHASH(%$stash);
debug "ending attack";
return @keys;
}
# trying to provide the fastest equivalent of C macro's PERL_HASH in
# Perl - the main complication is that the C macro uses U32 integer
# (unsigned int), which we can't do it Perl (it can do I32, with 'use
# integer'). So we outsmart Perl and take modules 2*32 after each
# calculation, emulating overflows that happen in C.
sub hash {
my $s = shift;
my @c = split //, $s;
my $u = 0;
for (@c) {
$u += ord; $u %= MASK_U32;
$u += $u << 10; $u %= MASK_U32;
$u ^= $u >> 6; $u %= MASK_U32;
}
$u += $u << 3; $u %= MASK_U32;
$u ^= $u >> 11; $u %= MASK_U32;
$u += $u << 15; $u %= MASK_U32;
$u;
}
1;
__END__
PerlModule TestPerl::hash_attack
PerlInitHandler TestPerl::hash_attack::init
# call twice to verify an access to the same hash value after the rehash
PerlFixupHandler TestPerl::hash_attack::fixup TestPerl::hash_attack::fixup
1.243 +7 -0 modperl-2.0/Changes
Index: Changes
===================================================================
RCS file: /home/cvs/modperl-2.0/Changes,v
retrieving revision 1.242
retrieving revision 1.243
diff -u -u -r1.242 -r1.243
--- Changes 20 Oct 2003 19:58:56 -0000 1.242
+++ Changes 3 Nov 2003 09:29:50 -0000 1.243
@@ -12,6 +12,13 @@
=item 1.99_11-dev -
+Adjust the source to properly work with 5.8.2's new algorithm of
+dynamic re-hashing of hashes on hash collision attack. [Nicholas Clark
+<[EMAIL PROTECTED]>, Stas]. Add a test that mounts such an attack so we
+can verify that we can survive this rehashing. [Scott A Crosby
+<[EMAIL PROTECTED]>, Nicholas Clark <[EMAIL PROTECTED]>, Tels
+<[EMAIL PROTECTED]>, Mark Jason Dominus <[EMAIL PROTECTED]>, Stas]
+
Standardize the Apache::PerlSections package name to it's plural form for
clarity and so that the pod gets glued in it's proper place.
[Philippe M. Chiasson <[EMAIL PROTECTED]>]