Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.2
perl
perl-rehash.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File perl-rehash.diff of Package perl
From 57629630785036482da04228e9bf767b3dac66b6 Mon Sep 17 00:00:00 2001 From: Yves Orton <demerphq@gmail.com> Date: Tue, 12 Feb 2013 10:53:05 +0100 Subject: [PATCH] Prevent premature hsplit() calls, and only trigger REHASH after hsplit() Triggering a hsplit due to long chain length allows an attacker to create a carefully chosen set of keys which can cause the hash to use 2 * (2**32) * sizeof(void *) bytes ram. AKA a DOS via memory exhaustion. Doing so also takes non trivial time. Eliminating this check, and only inspecting chain length after a normal hsplit() (triggered when keys>buckets) prevents the attack entirely, and makes such attacks relatively benign. (cherry picked from commit f1220d61455253b170e81427c9d0357831ca0fac) --- ./ext/Hash/Util/FieldHash/t/10_hash.t.orig 2010-12-30 02:07:16.000000000 +0000 +++ ./ext/Hash/Util/FieldHash/t/10_hash.t 2013-02-26 15:54:36.000000000 +0000 @@ -38,15 +38,29 @@ use constant START => "a"; # some initial hash data fieldhash my %h2; -%h2 = map {$_ => 1} 'a'..'cc'; +my $counter= "a"; +$h2{$counter++}++ while $counter ne 'cd'; ok (!Internals::HvREHASH(%h2), "starting with pre-populated non-pathological hash (rehash flag if off)"); my @keys = get_keys(\%h2); +my $buckets= buckets(\%h2); $h2{$_}++ for @keys; +$h2{$counter++}++ while buckets(\%h2) == $buckets; # force a split ok (Internals::HvREHASH(%h2), - scalar(@keys) . " colliding into the same bucket keys are triggering rehash"); + scalar(@keys) . " colliding into the same bucket keys are triggering rehash after split"); + +# returns the number of buckets in a hash +sub buckets { + my $hr = shift; + my $keys_buckets= scalar(%$hr); + if ($keys_buckets=~m!/([0-9]+)\z!) { + return 0+$1; + } else { + return 8; + } +} sub get_keys { my $hr = shift; --- ./t/op/hash.t.orig 2007-12-18 10:47:08.000000000 +0000 +++ ./t/op/hash.t 2013-02-26 15:35:41.000000000 +0000 @@ -39,22 +39,36 @@ use constant THRESHOLD => 14; use constant START => "a"; # some initial hash data -my %h2 = map {$_ => 1} 'a'..'cc'; +my %h2; +my $counter= "a"; +$h2{$counter++}++ while $counter ne 'cd'; ok (!Internals::HvREHASH(%h2), "starting with pre-populated non-pathological hash (rehash flag if off)"); my @keys = get_keys(\%h2); +my $buckets= buckets(\%h2); $h2{$_}++ for @keys; +$h2{$counter++}++ while buckets(\%h2) == $buckets; # force a split ok (Internals::HvREHASH(%h2), - scalar(@keys) . " colliding into the same bucket keys are triggering rehash"); + scalar(@keys) . " colliding into the same bucket keys are triggering rehash after split"); + +# returns the number of buckets in a hash +sub buckets { + my $hr = shift; + my $keys_buckets= scalar(%$hr); + if ($keys_buckets=~m!/([0-9]+)\z!) { + return 0+$1; + } else { + return 8; + } +} sub get_keys { my $hr = shift; # 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 %$hr; --- hv.c.orig 2013-03-15 10:02:44.969542617 +0100 +++ hv.c 2013-03-15 10:37:23.853123998 +0100 @@ -35,7 +35,8 @@ #define PERL_HASH_INTERNAL_ACCESS #include "perl.h" -#define HV_MAX_LENGTH_BEFORE_SPLIT 14 +#define HV_MAX_LENGTH_BEFORE_REHASH 14 +#define SHOULD_DO_HSPLIT(xhv) ((xhv)->xhv_keys > (IV)xhv->xhv_max) /* HvTOTALKEYS(hv) > HvMAX(hv) */ static const char S_strtab_error[] = "Cannot modify shared string table in hv_%s"; @@ -818,23 +819,8 @@ xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ if (!counter) { /* initial entry? */ xhv->xhv_fill++; /* HvFILL(hv)++ */ - } else if (xhv->xhv_keys > (IV)xhv->xhv_max) { + } else if ( SHOULD_DO_HSPLIT(xhv) ) { hsplit(hv); - } else if(!HvREHASH(hv)) { - U32 n_links = 1; - - while ((counter = HeNEXT(counter))) - n_links++; - - if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) { - /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit - bucket splits on a rehashed hash, as we're not going to - split it again, and if someone is lucky (evil) enough to - get all the keys in one list they could exhaust our memory - as we repeatedly double the number of buckets on every - entry. Linear search feels a less worse thing to do. */ - hsplit(hv); - } } } @@ -1180,7 +1166,7 @@ /* Pick your policy for "hashing isn't working" here: */ - if (longest_chain <= HV_MAX_LENGTH_BEFORE_SPLIT /* split worked? */ + if (longest_chain <= HV_MAX_LENGTH_BEFORE_REHASH /* split worked? */ || HvREHASH(hv)) { return; } @@ -2550,9 +2536,8 @@ xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */ if (!next) { /* initial entry? */ - xhv->xhv_fill++; /* HvFILL(hv)++ */ - } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) { - hsplit(PL_strtab); + } else if ( SHOULD_DO_HSPLIT(xhv) ) { + hsplit(PL_strtab); } }
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor