fix locale leak on zero-length strings
authorDavid Mitchell <davem@iabyn.com>
Tue, 16 Apr 2019 14:28:16 +0000 (15:28 +0100)
committerDavid Mitchell <davem@iabyn.com>
Tue, 16 Apr 2019 15:09:38 +0000 (16:09 +0100)
For example the following leaked:

    use locale;
    my $le = "" le "";

When a comparison is done within locale scope, PERL_MAGIC_collxfrm
magic is added to the SV. However, the value set for mg_len is the length
of the collated string, not the malloced size of the buffer. This means
that mg_len can be set to zero, which by convention, means that mg_ptr
shouldn't be freed.

For now, fix by putting special-cased code in S_mg_free_struct. After
5.30.0 is out, I'll probably add a PERL_MAGIC_collxfrm-specific svt_free
vtable method.

The symptoms were that valgrind / Address Sanitizer found fault with
lib/locale.t

mg.c

diff --git a/mg.c b/mg.c
index 320e2d3..afe452f 100644 (file)
--- a/mg.c
+++ b/mg.c
@@ -556,12 +556,18 @@ S_mg_free_struct(pTHX_ SV *sv, MAGIC *mg)
     const MGVTBL* const vtbl = mg->mg_virtual;
     if (vtbl && vtbl->svt_free)
        vtbl->svt_free(aTHX_ sv, mg);
-    if (mg->mg_ptr && mg->mg_type != PERL_MAGIC_regex_global) {
+
+    if (mg->mg_type == PERL_MAGIC_collxfrm && mg->mg_len >= 0)
+        /* collate magic uses string len not buffer len, so
+         * free even with mg_len == 0 */
+        Safefree(mg->mg_ptr);
+    else if (mg->mg_ptr && mg->mg_type != PERL_MAGIC_regex_global) {
        if (mg->mg_len > 0 || mg->mg_type == PERL_MAGIC_utf8)
            Safefree(mg->mg_ptr);
        else if (mg->mg_len == HEf_SVKEY)
            SvREFCNT_dec(MUTABLE_SV(mg->mg_ptr));
     }
+
     if (mg->mg_flags & MGf_REFCOUNTED)
        SvREFCNT_dec(mg->mg_obj);
     Safefree(mg);