Gentoo Archives: gentoo-commits

From: "Torsten Veller (tove)" <tove@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in dev-lang/perl/files: perl-5.12.5-rehash-5.12.5.patch
Date: Mon, 04 Mar 2013 19:51:20
Message-Id: 20130304195115.AFC0020081@flycatcher.gentoo.org
1 tove 13/03/04 19:51:15
2
3 Added: perl-5.12.5-rehash-5.12.5.patch
4 Log:
5 Bump. Fixes CVE-2013-1667 and adds subslots.
6
7 (Portage version: 2.2.0_alpha164/cvs/Linux x86_64, signed Manifest commit with key 0x5772769F4E046AEC)
8
9 Revision Changes Path
10 1.1 dev-lang/perl/files/perl-5.12.5-rehash-5.12.5.patch
11
12 file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/dev-lang/perl/files/perl-5.12.5-rehash-5.12.5.patch?rev=1.1&view=markup
13 plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/dev-lang/perl/files/perl-5.12.5-rehash-5.12.5.patch?rev=1.1&content-type=text/plain
14
15 Index: perl-5.12.5-rehash-5.12.5.patch
16 ===================================================================
17 From f2a571dae7d70f7e3b59022834d8003ecd2df884 Mon Sep 17 00:00:00 2001
18 From: Yves Orton <demerphq@×××××.com>
19 Date: Tue, 12 Feb 2013 10:53:05 +0100
20 Subject: [PATCH] Prevent premature hsplit() calls, and only trigger REHASH
21 after hsplit()
22
23 Triggering a hsplit due to long chain length allows an attacker
24 to create a carefully chosen set of keys which can cause the hash
25 to use 2 * (2**32) * sizeof(void *) bytes ram. AKA a DOS via memory
26 exhaustion. Doing so also takes non trivial time.
27
28 Eliminating this check, and only inspecting chain length after a
29 normal hsplit() (triggered when keys>buckets) prevents the attack
30 entirely, and makes such attacks relatively benign.
31
32 (cherry picked from commit f1220d61455253b170e81427c9d0357831ca0fac)
33 ---
34 ext/Hash-Util-FieldHash/t/10_hash.t | 18 ++++++++++++++++--
35 hv.c | 26 ++++++--------------------
36 t/op/hash.t | 20 +++++++++++++++++---
37 3 files changed, 39 insertions(+), 25 deletions(-)
38
39 diff --git a/ext/Hash-Util-FieldHash/t/10_hash.t b/ext/Hash-Util-FieldHash/t/10_hash.t
40 index 2cfb4e8..d58f053 100644
41 --- a/ext/Hash-Util-FieldHash/t/10_hash.t
42 +++ b/ext/Hash-Util-FieldHash/t/10_hash.t
43 @@ -38,15 +38,29 @@ use constant START => "a";
44
45 # some initial hash data
46 fieldhash my %h2;
47 -%h2 = map {$_ => 1} 'a'..'cc';
48 +my $counter= "a";
49 +$h2{$counter++}++ while $counter ne 'cd';
50
51 ok (!Internals::HvREHASH(%h2),
52 "starting with pre-populated non-pathological hash (rehash flag if off)");
53
54 my @keys = get_keys(\%h2);
55 +my $buckets= buckets(\%h2);
56 $h2{$_}++ for @keys;
57 +$h2{$counter++}++ while buckets(\%h2) == $buckets; # force a split
58 ok (Internals::HvREHASH(%h2),
59 - scalar(@keys) . " colliding into the same bucket keys are triggering rehash");
60 + scalar(@keys) . " colliding into the same bucket keys are triggering rehash after split");
61 +
62 +# returns the number of buckets in a hash
63 +sub buckets {
64 + my $hr = shift;
65 + my $keys_buckets= scalar(%$hr);
66 + if ($keys_buckets=~m!/([0-9]+)\z!) {
67 + return 0+$1;
68 + } else {
69 + return 8;
70 + }
71 +}
72
73 sub get_keys {
74 my $hr = shift;
75 diff --git a/hv.c b/hv.c
76 index 89c6456..8659678 100644
77 --- a/hv.c
78 +++ b/hv.c
79 @@ -35,7 +35,8 @@ holds the key and hash value.
80 #define PERL_HASH_INTERNAL_ACCESS
81 #include "perl.h"
82
83 -#define HV_MAX_LENGTH_BEFORE_SPLIT 14
84 +#define HV_MAX_LENGTH_BEFORE_REHASH 14
85 +#define SHOULD_DO_HSPLIT(xhv) ((xhv)->xhv_keys > (xhv)->xhv_max) /* HvTOTALKEYS(hv) > HvMAX(hv) */
86
87 static const char S_strtab_error[]
88 = "Cannot modify shared string table in hv_%s";
89 @@ -818,23 +819,8 @@ Perl_hv_common(pTHX_ HV *hv, SV *keysv, const char *key, STRLEN klen,
90 xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
91 if (!counter) { /* initial entry? */
92 xhv->xhv_fill++; /* HvFILL(hv)++ */
93 - } else if (xhv->xhv_keys > (IV)xhv->xhv_max) {
94 + } else if ( SHOULD_DO_HSPLIT(xhv) ) {
95 hsplit(hv);
96 - } else if(!HvREHASH(hv)) {
97 - U32 n_links = 1;
98 -
99 - while ((counter = HeNEXT(counter)))
100 - n_links++;
101 -
102 - if (n_links > HV_MAX_LENGTH_BEFORE_SPLIT) {
103 - /* Use only the old HvKEYS(hv) > HvMAX(hv) condition to limit
104 - bucket splits on a rehashed hash, as we're not going to
105 - split it again, and if someone is lucky (evil) enough to
106 - get all the keys in one list they could exhaust our memory
107 - as we repeatedly double the number of buckets on every
108 - entry. Linear search feels a less worse thing to do. */
109 - hsplit(hv);
110 - }
111 }
112 }
113
114 @@ -1180,7 +1166,7 @@ S_hsplit(pTHX_ HV *hv)
115
116
117 /* Pick your policy for "hashing isn't working" here: */
118 - if (longest_chain <= HV_MAX_LENGTH_BEFORE_SPLIT /* split worked? */
119 + if (longest_chain <= HV_MAX_LENGTH_BEFORE_REHASH /* split worked? */
120 || HvREHASH(hv)) {
121 return;
122 }
123 @@ -2551,8 +2537,8 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, register U32 hash, int flags)
124 xhv->xhv_keys++; /* HvTOTALKEYS(hv)++ */
125 if (!next) { /* initial entry? */
126 xhv->xhv_fill++; /* HvFILL(hv)++ */
127 - } else if (xhv->xhv_keys > (IV)xhv->xhv_max /* HvKEYS(hv) > HvMAX(hv) */) {
128 - hsplit(PL_strtab);
129 + } else if ( SHOULD_DO_HSPLIT(xhv) ) {
130 + hsplit(PL_strtab);
131 }
132 }
133
134 diff --git a/t/op/hash.t b/t/op/hash.t
135 index 9bde518..45eb782 100644
136 --- a/t/op/hash.t
137 +++ b/t/op/hash.t
138 @@ -39,22 +39,36 @@ use constant THRESHOLD => 14;
139 use constant START => "a";
140
141 # some initial hash data
142 -my %h2 = map {$_ => 1} 'a'..'cc';
143 +my %h2;
144 +my $counter= "a";
145 +$h2{$counter++}++ while $counter ne 'cd';
146
147 ok (!Internals::HvREHASH(%h2),
148 "starting with pre-populated non-pathological hash (rehash flag if off)");
149
150 my @keys = get_keys(\%h2);
151 +my $buckets= buckets(\%h2);
152 $h2{$_}++ for @keys;
153 +$h2{$counter++}++ while buckets(\%h2) == $buckets; # force a split
154 ok (Internals::HvREHASH(%h2),
155 - scalar(@keys) . " colliding into the same bucket keys are triggering rehash");
156 + scalar(@keys) . " colliding into the same bucket keys are triggering rehash after split");
157 +
158 +# returns the number of buckets in a hash
159 +sub buckets {
160 + my $hr = shift;
161 + my $keys_buckets= scalar(%$hr);
162 + if ($keys_buckets=~m!/([0-9]+)\z!) {
163 + return 0+$1;
164 + } else {
165 + return 8;
166 + }
167 +}
168
169 sub get_keys {
170 my $hr = shift;
171
172 # the minimum of bits required to mount the attack on a hash
173 my $min_bits = log(THRESHOLD)/log(2);
174 -
175 # if the hash has already been populated with a significant amount
176 # of entries the number of mask bits can be higher
177 my $keys = scalar keys %$hr;
178 --
179 1.8.1.3