summaryrefslogtreecommitdiff
path: root/hv.c
diff options
context:
space:
mode:
authorAaron Crane <arc@cpan.org>2017-06-01 14:42:22 +0200
committerYves Orton <demerphq@gmail.com>2017-06-01 17:17:34 +0200
commitb02f36453d1392e2b0bd62fdde2b286fb60bd5bc (patch)
tree511763b834e942d731ea762309284dccbd5450d0 /hv.c
parent1cf41740f284a4e05bbefc5b15c5ffd9c254aa78 (diff)
downloadperl-b02f36453d1392e2b0bd62fdde2b286fb60bd5bc.tar.gz
RT #127742: Hash keys are limited to 2 GB - throw an exception if hash keys are too long
We currently require hash keys to be less than 2**31 bytes long. But (a) nothing actually tries to enforce that, and (b) if a Perl program tries to create a hash with such a key (using a 64-bit system), we miscalculate the size of a memory block, yielding a panic: $ ./perl -e '+{ "x" x 2**31, undef }' panic: malloc, size=18446744071562068026 at -e line 1. Instead, check for this situation, and croak with an appropriate (new) diagnostic in the unlikely event that it occurs. This also involves changing the type of an argument to a public API function: Perl_share_hek() previously took the key's length as an I32, but that makes it impossible to detect over-long keys, so it must be SSize_t instead. From Yves: We also inject the length test into the PERL_HASH() macro, so that where the macro is used *before* calling into any of the hv functions we can avoid hashing a very long string only to throw an exception that it is too long. Might as well fail fast.
Diffstat (limited to 'hv.c')
-rw-r--r--hv.c10
1 files changed, 7 insertions, 3 deletions
diff --git a/hv.c b/hv.c
index 3bd62c6f9d..8acf33a4b2 100644
--- a/hv.c
+++ b/hv.c
@@ -2966,7 +2966,7 @@ S_unshare_hek_or_pvn(pTHX_ const HEK *hek, const char *str, I32 len, U32 hash)
* len and hash must both be valid for str.
*/
HEK *
-Perl_share_hek(pTHX_ const char *str, I32 len, U32 hash)
+Perl_share_hek(pTHX_ const char *str, SSize_t len, U32 hash)
{
bool is_utf8 = FALSE;
int flags = 0;
@@ -2998,7 +2998,7 @@ Perl_share_hek(pTHX_ const char *str, I32 len, U32 hash)
}
STATIC HEK *
-S_share_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
+S_share_hek_flags(pTHX_ const char *str, STRLEN len, U32 hash, int flags)
{
HE *entry;
const int flags_masked = flags & HVhek_MASK;
@@ -3007,6 +3007,10 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
PERL_ARGS_ASSERT_SHARE_HEK_FLAGS;
+ if (UNLIKELY(len > (STRLEN) I32_MAX)) {
+ Perl_croak_nocontext("Sorry, hash keys must be smaller than 2**31 bytes");
+ }
+
/* what follows is the moral equivalent of:
if (!(Svp = hv_fetch(PL_strtab, str, len, FALSE)))
@@ -3021,7 +3025,7 @@ S_share_hek_flags(pTHX_ const char *str, I32 len, U32 hash, int flags)
for (;entry; entry = HeNEXT(entry)) {
if (HeHASH(entry) != hash) /* strings can't be equal */
continue;
- if (HeKLEN(entry) != len)
+ if (HeKLEN(entry) != (SSize_t) len)
continue;
if (HeKEY(entry) != str && memNE(HeKEY(entry),str,len)) /* is this it? */
continue;