summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorNicholas Clark <nick@ccl4.org>2021-07-26 07:32:46 +0000
committerNicholas Clark <nick@ccl4.org>2021-09-16 07:06:57 +0000
commitf15a20bce3aaf0956e466389530be470aa2cabfe (patch)
tree23ccb66f9917a7856ea7a3ef3b10f00623ba3b2c /ext
parentd5a0a5dd15db7407246717e8c3e8891b9ba7c53c (diff)
downloadperl-f15a20bce3aaf0956e466389530be470aa2cabfe.tar.gz
Avoid a use-after-free deleting 8-bit keys from stashes
This code path only affects symbol tables, and can't be reached by regular Perl code. It is only reachable using the XS API to delete a key from a stash where the key was in the 8-bit range but passed in UTF-8 encoded. This has been in the code since it was added in Oct 2010 by commit 35759254f69c7bfa: Rename stashes when they move around Also there is no need to call SvPV() on keysv in S_hv_delete_common() as its caller has always already done this. This entire code is not KISS.
Diffstat (limited to 'ext')
-rw-r--r--ext/XS-APItest/t/hash.t19
1 files changed, 19 insertions, 0 deletions
diff --git a/ext/XS-APItest/t/hash.t b/ext/XS-APItest/t/hash.t
index 29d5803584..5d3357e888 100644
--- a/ext/XS-APItest/t/hash.t
+++ b/ext/XS-APItest/t/hash.t
@@ -325,6 +325,25 @@ pass("hv_store works on the hint hash");
is(sv_magic_mycopy_count(\%h), 4);
}
+{
+ # There are two API variants - hv_delete and hv_delete_ent. The Perl
+ # interpreter exclusively uses hv_delete_ent. Only XS code uses hv_delete.
+ # Hence the problem case could only be triggered by XS code called on
+ # symbol tables, and with particular non-ASCII keys:
+
+ # Deleting a key with WASUTF from a stash used to trigger a use-after free:
+ my $key = "\xFF\x{100}";
+ chop $key;
+ ++$main::{$key};
+ is(XS::APItest::Hash::delete(\%main::, $key), 1,
+ "hv_delete doesn't trigger a use-after free");
+
+ # Perl code has always used this API, which never had the problem:
+ ++$main::{$key};
+ is(XS::APItest::Hash::delete_ent(\%main::, $key), 1,
+ "hv_delete_ent never triggered a use-after free, but test it anyway");
+}
+
done_testing;
exit;