Skip to content

feature 'class' appears to leak opslabs #20812

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
leonerd opened this issue Feb 15, 2023 · 13 comments · Fixed by #21457
Closed

feature 'class' appears to leak opslabs #20812

leonerd opened this issue Feb 15, 2023 · 13 comments · Fixed by #21457

Comments

@leonerd
Copy link
Contributor

leonerd commented Feb 15, 2023

The first merge of feature 'class' turned out to have many memory leaks. Most of them are fixed by #20809.

With that in place most of the more basic tests no longer leak, but any field definition that includes a :param attribute or a = EXPR defaulting expression still does. In both cases the leak is visible in valgrind as:

==3139978== 16,136 (552 direct, 15,584 indirect) bytes in 1 blocks are definitely lost in loss record 6 of 6
==3139978==    at 0x48407B4: malloc (vg_replace_malloc.c:381)
==3139978==    by 0x153171: S_new_slab (op.c:254)
==3139978==    by 0x1535DA: Perl_Slab_Alloc (op.c:346)
==3139978==    by 0x16D01B: Perl_newSVOP (op.c:7572)
==3139978==    by 0x225D01: S_pending_ident (toke.c:9967)
==3139978==    by 0x22380B: Perl_yylex (toke.c:9592)
==3139978==    by 0x23DF02: Perl_yyparse (perly.c:357)
==3139978==    by 0x19E9DF: S_parse_body (perl.c:2614)
==3139978==    by 0x19C54F: perl_parse (perl.c:1923)
==3139978==    by 0x14E285: main (perlmain.c:106)

which suggests it's because an op gets allocated somewhere and isn't released again. But so far I haven't been able to find it.

@tonycoz
Copy link
Contributor

tonycoz commented Feb 16, 2023

I see a leak from LSAN even without an initializer. In the following I've rebuilt perl to add class to the default feature set to reduce noise:

$ LSAN_OPTIONS=report_objects=1 PERL_DESTRUCT_LEVEL=2 ./perl -DS -Ilib -e 'class Foo { field $x; }'
class is experimental at -e line 1.
allocated new op slab sz 0x40, 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000e78, slab 616000000c80, head slab 616000000c80 at -e line 1.
freeing    op at 616000000e78, slab 616000000c80, head slab 616000000c80 at -e line 1.
field is experimental at -e line 1.
realloced  op at 616000000e78, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000e48, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000de8, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000da8, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocated new op slab sz 0x40, 616000000f80, head slab 616000000f80 at -e line 1.
allocating op at 616000001170, slab 616000000f80, head slab 616000000f80 at -e line 1.
allocating op at 616000001130, slab 616000000f80, head slab 616000000f80 at -e line 1.
allocating op at 6160000010f0, slab 616000000f80, head slab 616000000f80 at -e line 1.
allocating op at 6160000010b8, slab 616000000f80, head slab 616000000f80 at -e line 1.
allocating op at 616000000d50, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000d10, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocating op at 616000000cb0, slab 616000000c80, head slab 616000000c80 at -e line 1.
allocated new op slab sz 0x80, 619000001480, head slab 616000000c80 at -e line 1.
allocating op at 619000001870, slab 619000001480, head slab 616000000c80 at -e line 1.
allocating op at 619000001840, slab 619000001480, head slab 616000000c80 at -e line 1.

EXECUTING...

freeing    op at 619000001840, slab 619000001480, head slab 616000000c80.
freeing    op at 616000000cb0, slab 616000000c80, head slab 616000000c80.
freeing    op at 616000000d50, slab 616000000c80, head slab 616000000c80.
freeing    op at 616000000de8, slab 616000000c80, head slab 616000000c80.
freeing    op at 616000000e48, slab 616000000c80, head slab 616000000c80.
freeing    op at 616000000da8, slab 616000000c80, head slab 616000000c80.
freeing    op at 616000000d10, slab 616000000c80, head slab 616000000c80.
freeing    op at 619000001870, slab 619000001480, head slab 616000000c80.
freeing    op at 616000001170, slab 616000000f80, head slab 616000000f80 during global destruction.
freeing    op at 616000001130, slab 616000000f80, head slab 616000000f80 during global destruction.
freeing    op at 6160000010f0, slab 616000000f80, head slab 616000000f80 during global destruction.
freeing    op at 6160000010b8, slab 616000000f80, head slab 616000000f80 during global destruction.
freeing slab 616000000f80 during global destruction.

=================================================================
==992111==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 1064 byte(s) in 1 object(s) allocated from:
    #0 0x7f74a9c58e8f in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x561d0de5c71f in S_new_slab /home/tony/dev/perl/git/perl6/op.c:254
    #2 0x561d0de5db99 in Perl_Slab_Alloc /home/tony/dev/perl/git/perl6/op.c:403
    #3 0x561d0de88008 in Perl_newLISTOP /home/tony/dev/perl/git/perl6/op.c:5560
    #4 0x561d0de87490 in Perl_op_prepend_elem /home/tony/dev/perl/git/perl6/op.c:5396
    #5 0x561d0de9c196 in Perl_newSTATEOP /home/tony/dev/perl/git/perl6/op.c:8509
    #6 0x561d0e0880e8 in Perl_yyparse /home/tony/dev/perl/git/perl6/perly.y:324
    #7 0x561d0df039ee in S_parse_body /home/tony/dev/perl/git/perl6/perl.c:2614
    #8 0x561d0defee9e in perl_parse /home/tony/dev/perl/git/perl6/perl.c:1923
    #9 0x561d0de51621 in main /home/tony/dev/perl/git/perl6/perlmain.c:106
    #10 0x7f74a9891d09 in __libc_start_main ../csu/libc-start.c:308

Objects leaked above:
0x619000001480 (1064 bytes)
Indirect leak of 552 byte(s) in 1 object(s) allocated from:
    #0 0x7f74a9c58e8f in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x561d0de5c71f in S_new_slab /home/tony/dev/perl/git/perl6/op.c:254
    #2 0x561d0de5d072 in Perl_Slab_Alloc /home/tony/dev/perl/git/perl6/op.c:346
    #3 0x561d0de950b2 in Perl_newSVOP /home/tony/dev/perl/git/perl6/op.c:7572
    #4 0x561d0dfb2a3d in S_force_word /home/tony/dev/perl/git/perl6/toke.c:2286
    #5 0x561d0e01e5a2 in yyl_word_or_keyword /home/tony/dev/perl/git/perl6/toke.c:7884
    #6 0x561d0e03ae0d in yyl_keylookup /home/tony/dev/perl/git/perl6/toke.c:8979
    #7 0x561d0e0431fe in yyl_try /home/tony/dev/perl/git/perl6/toke.c:9492
    #8 0x561d0e047f38 in Perl_yylex /home/tony/dev/perl/git/perl6/toke.c:9794
    #9 0x561d0e086415 in Perl_yyparse /home/tony/dev/perl/git/perl6/perly.c:357
    #10 0x561d0df039ee in S_parse_body /home/tony/dev/perl/git/perl6/perl.c:2614
    #11 0x561d0defee9e in perl_parse /home/tony/dev/perl/git/perl6/perl.c:1923
    #12 0x561d0de51621 in main /home/tony/dev/perl/git/perl6/perlmain.c:106
    #13 0x7f74a9891d09 in __libc_start_main ../csu/libc-start.c:308

Objects leaked above:
0x616000000c80 (552 bytes)

Indirect leak of 56 byte(s) in 1 object(s) allocated from:
    #0 0x7f74a9c591f8 in __interceptor_realloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:164
    #1 0x561d0de5cb9d in S_link_freed_op /home/tony/dev/perl/git/perl6/op.c:295
    #2 0x561d0de5e38b in Perl_Slab_Free /home/tony/dev/perl/git/perl6/op.c:507
    #3 0x561d0de618e7 in Perl_op_free /home/tony/dev/perl/git/perl6/op.c:991
    #4 0x561d0def607f in perl_destruct /home/tony/dev/perl/git/perl6/perl.c:910
    #5 0x561d0de51837 in main /home/tony/dev/perl/git/perl6/perlmain.c:139
    #6 0x7f74a9891d09 in __libc_start_main ../csu/libc-start.c:308

Objects leaked above:
0x6060000043a0 (56 bytes)

SUMMARY: AddressSanitizer: 1672 byte(s) leaked in 3 allocation(s).

So the e78 op is freed, then allocated again, and it's the first OP realloced. But it isn't freed after that

$ LSAN_OPTIONS=report_objects=1 PERL_DESTRUCT_LEVEL=2 gdb --args ./perl -DS -Ilib -e 'class Foo { field $x; }'
...
(gdb) b op.c:373
Breakpoint 1 at 0x1cf6d3: file op.c, line 373.
(gdb) r
Starting program: /home/tony/dev/perl/git/perl6/perl -DS -Ilib -e class\ Foo\ \{\ field\ \$x\;\ \}
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
class is experimental at -e line 1.
allocated new op slab sz 0x40, 616000000980, head slab 616000000980 at -e line 1.
allocating op at 616000000b78, slab 616000000980, head slab 616000000980 at -e line 1.
freeing    op at 616000000b78, slab 616000000980, head slab 616000000980 at -e line 1.
field is experimental at -e line 1.
realloced  op at 616000000b78, slab 616000000980, head slab 616000000980 at -e line 1.

Breakpoint 1, Perl_Slab_Alloc (my_perl=0x620000000080, sz=40) at op.c:373
373                 head_slab->opslab_freed[base_index] = o->op_next;
(gdb) bt
#0  Perl_Slab_Alloc (my_perl=0x620000000080, sz=40) at op.c:373
#1  0x000055555574e67f in Perl_newOP (my_perl=0x620000000080, type=13, flags=0)
    at op.c:5614
#2  0x000055555590ef1f in S_pending_ident (my_perl=0x620000000080)
    at toke.c:9897
#3  0x000055555590a138 in Perl_yylex (my_perl=0x620000000080) at toke.c:9592
#4  0x000055555594c416 in Perl_yyparse (my_perl=0x620000000080, gramtype=258)
    at perly.c:357
#5  0x00005555557c99ef in S_parse_body (my_perl=0x620000000080, env=0x0, 
    xsinit=0x555555717864 <xs_init>) at perl.c:2614
#6  0x00005555557c4e9f in perl_parse (my_perl=0x620000000080, 
    xsinit=0x555555717864 <xs_init>, argc=5, argv=0x7fffffffe798, env=0x0)
    at perl.c:1923
#7  0x0000555555717622 in main (argc=5, argv=0x7fffffffe798, 
    env=0x7fffffffe7c8) at perlmain.c:106
(gdb) up 2
#2  0x000055555590ef1f in S_pending_ident (my_perl=0x620000000080)
    at toke.c:9897
9897                    o = newOP(OP_PADANY, 0);
(gdb) p my_perl->Iparser->tokenbuf
$1 = "$x\000ld", '\000' <repeats 250 times>

So it looks like the OP_PAD* op isn't being released. The fieldvar grammar rules fetch the PADNAME for the pad variable but don't seem to do anything with the OP, should they be releasing it?

@leonerd
Copy link
Contributor Author

leonerd commented Feb 16, 2023

So it looks like the OP_PAD* op isn't being released. The fieldvar grammar rules fetch the PADNAME for the pad variable but don't seem to do anything with the OP, should they be releasing it?

Woo. Much thanks @tonycoz - that was indeed the problem. I've now popped the fix in 14f46f2. If that comes up OK I'll squash it into the main #20809 and we can call it done.

@leonerd
Copy link
Contributor Author

leonerd commented Feb 17, 2023

With #20809 merged, most of the leaks are fixed.

There's still one more memory leak left, but that's only apparent if a compiletime error causes a field expression inside a class block to fail its out-of-block controlflow test.

Compare:

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { BEGIN { die "Oopsie" } }'
$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { field $x; BEGIN { die "Oopsie" } }'

neither of which leak, with

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { field $x = last; }'

which does. It only affects programs that are syntactically invalid - so it shouldn't be too high impact. In normal circumstances the entire process is about to terminate anyway, so a memory leak here wouldn't be a problem. It would affect any code that doesn't exit on a compilation failure - such as uses of eval($code) or eval { require ... }, so we should still fix it.

@jkeenan
Copy link
Contributor

jkeenan commented Feb 17, 2023

With #20809 merged, most of the leaks are fixed.

There's still one more memory leak left, but that's only apparent if a compiletime error causes a field expression inside a class block to fail its out-of-block controlflow test.

Compare:

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { BEGIN { die "Oopsie" } }'
$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { field $x; BEGIN { die "Oopsie" } }'

neither of which leak, with

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { field $x = last; }'

When I tried these valgrind invocations as written above, I got a can't locate feature.pm in @INC error. Example:

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -I. -Mfeature=class -E 'class XXX { field $x = last; }'
==2804911== Memcheck, a memory error detector
==2804911== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2804911== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==2804911== Command: ./perl -I. -Mfeature=class -E class\ XXX\ {\ field\ $x\ =\ last;\ }
==2804911== 
Can't locate feature.pm in @INC (you may need to install the feature module) (@INC entries checked: . /usr/local/lib/perl5/site_perl/5.37.9/x86_64-linux /usr/local/lib/perl5/site_perl/5.37.9 /usr/local/lib/perl5/5.37.9/x86_64-linux /usr/local/lib/perl5/5.37.9).
BEGIN failed--compilation aborted.
==2804911== 
==2804911== HEAP SUMMARY:
==2804911==     in use at exit: 3,561 bytes in 1 blocks
==2804911==   total heap usage: 986 allocs, 985 frees, 205,292 bytes allocated
==2804911== 
==2804911== LEAK SUMMARY:
==2804911==    definitely lost: 0 bytes in 0 blocks
==2804911==    indirectly lost: 0 bytes in 0 blocks
==2804911==      possibly lost: 0 bytes in 0 blocks
==2804911==    still reachable: 3,561 bytes in 1 blocks
==2804911==         suppressed: 0 bytes in 0 blocks
==2804911== Reachable blocks (those to which a pointer was found) are not shown.
==2804911== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==2804911== 
==2804911== For lists of detected and suppressed errors, rerun with: -s
==2804911== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

When I substituted -Ilib for -I., this error message went away and I was able to confirm your examples.

@jkeenan
Copy link
Contributor

jkeenan commented Feb 17, 2023

I was able to confirm your results on FreeBSD-12.

$ git describe
v5.37.8-182-g04c0207ebb
$ ./perl -Ilib -V:config_args
config_args='-des -Dusedevel -Duseithreads -Doptimize=-O2 -pipe -fstack-protector -fno-strict-aliasing';

NOT leaking

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -Ilib -Mfeature=class -E 'class XXX { BEGIN { die "Oopsie" } }'

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -Ilib -Mfeature=class -E 'class XXX { field $x; BEGIN { die "Oopsie" } }'

Still leaking

$ PERL_DESTRUCT_LEVEL=2 valgrind --leak-check=full ./perl -Ilib -Mfeature=class -E 'class XXX { field $x = last; }'
==35844== Memcheck, a memory error detector
==35844== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==35844== Using Valgrind-3.20.0.GIT and LibVEX; rerun with -h for copyright info
==35844== Command: ./perl -Ilib -Mfeature=class -E class\ XXX\ {\ field\ $x\ =\ last;\ }
==35844== 
class is experimental at -e line 1.
field is experimental at -e line 1.
Can't "last" out of field initialiser expression at -e line 1.
==35844== 
==35844== HEAP SUMMARY:
==35844==     in use at exit: 3,965 bytes in 6 blocks
==35844==   total heap usage: 2,520 allocs, 2,514 frees, 501,541 bytes allocated
==35844== 
==35844== 576 (552 direct, 24 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 6
==35844==    at 0x4853B34: malloc (in /usr/local/libexec/valgrind/vgpreload_memcheck-amd64-freebsd.so)
==35844==    by 0x3E0974: Perl_Slab_Alloc (in /usr/home/jkeenan/gitwork/perl/perl)
==35844==    by 0x3E9E15: Perl_newOP (in /usr/home/jkeenan/gitwork/perl/perl)
==35844==    by 0x4408F5: Perl_yyparse (in /usr/home/jkeenan/gitwork/perl/perl)
==35844==    by 0x404EED: perl_parse (in /usr/home/jkeenan/gitwork/perl/perl)
==35844==    by 0x3E0808: main (in /usr/home/jkeenan/gitwork/perl/perl)
==35844== 
==35844== LEAK SUMMARY:
==35844==    definitely lost: 552 bytes in 1 blocks
==35844==    indirectly lost: 24 bytes in 1 blocks
==35844==      possibly lost: 0 bytes in 0 blocks
==35844==    still reachable: 3,389 bytes in 4 blocks
==35844==         suppressed: 0 bytes in 0 blocks
==35844== Reachable blocks (those to which a pointer was found) are not shown.
==35844== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==35844== 
==35844== For lists of detected and suppressed errors, rerun with: -s
==35844== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

@tonycoz
Copy link
Contributor

tonycoz commented Feb 19, 2023

The op not being freed appears to be the OP_LAST (found using roughly the same method as above.)

I expect forbid_outofblock_ops() needs to release the op tree if it's going to croak.

I added code to op_free() the OP before the croak in walk_ops_forbid() and that prevented the leak, but that's not a real solution -- the last might be embedded within the OP tree, eg. ./perl -Ilib -e 'class XXX { field $x = foo() ? last : 1; }' still leaks.

@tonycoz
Copy link
Contributor

tonycoz commented Feb 20, 2023

Adding the op_free() breaks other things, SAVEFREEOP() is safer, but the similar errors for defer and finally don't appear to need the free, the last OP in defer { last } at least appears to be freed with the CV.

@iabyn
Copy link
Contributor

iabyn commented Feb 25, 2023 via email

@iabyn
Copy link
Contributor

iabyn commented Mar 6, 2023 via email

@leonerd
Copy link
Contributor Author

leonerd commented Mar 6, 2023

I just want to check that my implicit assumption was correct: that, having helped diagnose the issue, I can now sit back and let someone else (i.e. Paul) do any actual fixing?

Oh indeed. I was on vacation and then post-vacation covid illness, but I'm back now. I'll write a longer reply soon but in summary I think I have this yes.

@tonycoz
Copy link
Contributor

tonycoz commented May 8, 2023

This appears to be the cause of the smoke failures on Fedora such as https://www.nntp.perl.org/group/perl.daily-build.reports/2023/05/msg293351.html:

[tony@fedora t]$ PERL_DESTRUCT_LEVEL=2 ./perl harness lib/croak.t
lib/croak.t .. 6/215 FILE: lib/croak/class ; line 101
PROG: 
use strict;
no warnings 'experimental::class';
use feature 'class';
class XXX {
  field $x = last;
}
EXPECTED:
Can't "last" out of field initialiser expression at - line 5.
EXIT STATUS: != 0
GOT:
Can't "last" out of field initialiser expression at - line 5.

=================================================================
==253147==ERROR: LeakSanitizer: detected memory leaks

Indirect leak of 552 byte(s) in 1 object(s) allocated from:
    #0 0x4d22d2 in __interceptor_malloc (/home/tony/dev/perl/git/perl/perl+0x4d22d2) (BuildId: 82e9218982b346f815ba720c680fba13dfb0f153)
    #1 0x510068 in S_new_slab /home/tony/dev/perl/git/perl/op.c:254:22
    #2 0x50e8ca in Perl_Slab_Alloc /home/tony/dev/perl/git/perl/op.c:346:32
    #3 0x53f609 in Perl_newOP /home/tony/dev/perl/git/perl/op.c:5660:5
    #4 0x850382 in Perl_yyparse /home/tony/dev/perl/git/perl/perly.y:1453:23
    #5 0x60d545 in S_parse_body /home/tony/dev/perl/git/perl/perl.c:2619:9
    #6 0x604882 in perl_parse /home/tony/dev/perl/git/perl/perl.c:1921:9
    #7 0x50df55 in main /home/tony/dev/perl/git/perl/perlmain.c:106:10
    #8 0x7f9ca78e7b49 in __libc_start_call_main (/lib64/libc.so.6+0x27b49) (BuildId: 245240a31888ad5c11bbc55b18e02d87388f59a9)
    #9 0x7f9ca78e7c0a in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x27c0a) (BuildId: 245240a31888ad5c11bbc55b18e02d87388f59a9)
    #10 0x437c04 in _start (/home/tony/dev/perl/git/perl/perl+0x437c04) (BuildId: 82e9218982b346f815ba720c680fba13dfb0f153)

Indirect leak of 24 byte(s) in 1 object(s) allocated from:
    #0 0x4d240c in __interceptor_calloc (/home/tony/dev/perl/git/perl/perl+0x4d240c) (BuildId: 82e9218982b346f815ba720c680fba13dfb0f153)
    #1 0x511207 in S_link_freed_op /home/tony/dev/perl/git/perl/op.c:284:36
    #2 0x511bd8 in Perl_Slab_Free /home/tony/dev/perl/git/perl/op.c:507:5
    #3 0x51380b in Perl_op_free /home/tony/dev/perl/git/perl/op.c:991:9
    #4 0x85cd45 in Perl_cv_undef_flags /home/tony/dev/perl/git/perl/pad.c:337:13
    #5 0x85b3e3 in Perl_cv_undef /home/tony/dev/perl/git/perl/pad.c:298:5
    #6 0xe84de7 in Perl_sv_clear /home/tony/dev/perl/git/perl/sv.c:6720:13
    #7 0xe91c5e in Perl_sv_free2 /home/tony/dev/perl/git/perl/sv.c:7233:9
    #8 0xe4f180 in Perl_SvREFCNT_dec_NN /home/tony/dev/perl/git/perl/./sv_inline.h:715:9
    #9 0xdba127 in do_clean_all /home/tony/dev/perl/git/perl/sv.c:577:5
    #10 0xdb42d9 in S_visit /home/tony/dev/perl/git/perl/sv.c:394:17
    #11 0xdb9f89 in Perl_sv_clean_all /home/tony/dev/perl/git/perl/sv.c:595:15
    #12 0x5fb44d in perl_destruct /home/tony/dev/perl/git/perl/perl.c:1331:12
    #13 0x50e15e in main /home/tony/dev/perl/git/perl/perlmain.c:139:18
    #14 0x7f9ca78e7b49 in __libc_start_call_main (/lib64/libc.so.6+0x27b49) (BuildId: 245240a31888ad5c11bbc55b18e02d87388f59a9)
    #15 0x7f9ca78e7c0a in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x27c0a) (BuildId: 245240a31888ad5c11bbc55b18e02d87388f59a9)
    #16 0x437c04 in _start (/home/tony/dev/perl/git/perl/perl+0x437c04) (BuildId: 82e9218982b346f815ba720c680fba13dfb0f153)

SUMMARY: AddressSanitizer: 576 byte(s) leaked in 2 allocation(s).
EXIT STATUS: 1
lib/croak.t .. 14/215 # Failed test 14 - test from lib/croak/class at line 101 at lib/croak/class line 101
lib/croak.t .. 211/215 #
# Note: 'run_multiple_progs' run has one or more failures
#        you can consider setting the environment variable
#        PERL_TEST_ABORT_FIRST_FAILURE=1 before running the test
#        to stop on the first error.
#
lib/croak.t .. Failed 1/215 subtests 
        (less 3 skipped subtests: 211 okay)

Test Summary Report
-------------------
lib/croak.t (Wstat: 0 Tests: 215 Failed: 1)
  Failed test:  14
Files=1, Tests=215, 11 wallclock secs ( 0.14 usr  0.10 sys +  3.96 cusr  6.55 csys = 10.75 CPU)
Result: FAIL
Finished test run at Mon May  8 11:20:31 2023.
[tony@fedora t]$

iabyn added a commit that referenced this issue May 19, 2023
This test is known to leak: see GH #20812. Skip it for now so that ASAN
smokes don't fail. Start failing again on the next development branch so
that the issue isn't forgotten
@iabyn
Copy link
Contributor

iabyn commented May 19, 2023

PR #21104 which I've just created, skips that leaking test for now. I intend to merge it today unless anyone objects.

jkeenan pushed a commit that referenced this issue May 19, 2023
This test is known to leak: see GH #20812. Skip it for now so that ASAN
smokes don't fail. Start failing again on the next development branch so
that the issue isn't forgotten
pjacklam pushed a commit to pjacklam/perl5 that referenced this issue May 20, 2023
This test is known to leak: see GH Perl#20812. Skip it for now so that ASAN
smokes don't fail. Start failing again on the next development branch so
that the issue isn't forgotten
pjacklam pushed a commit to pjacklam/perl5 that referenced this issue May 20, 2023
This test is known to leak: see GH Perl#20812. Skip it for now so that ASAN
smokes don't fail. Start failing again on the next development branch so
that the issue isn't forgotten
@tonycoz
Copy link
Contributor

tonycoz commented May 23, 2023

If there was anyone else wondering, these specific OPs aren't released because at this point they're not on the parser stack (which is why the similar defer code doesn't leak), nor are they part of the CV's op tree.

Since the CV is no longer marked slabbed only the OPs hold "references" to the slabs, but since ops aren't part of the tree, op_free() doesn't find all of the ops to release, hence not all of the references to the slabs are released and so the slabs leak.

khwilliamson pushed a commit to khwilliamson/perl5 that referenced this issue Jul 10, 2023
This test is known to leak: see GH Perl#20812. Skip it for now so that ASAN
smokes don't fail. Start failing again on the next development branch so
that the issue isn't forgotten
tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 5, 2023
Previously if forbid_outofblock_ops() here threw an error the ops
from defop would leak, including leaking the slab(s) containing
those ops.

The other callers to forbid_outofblock_ops() left the ops being
checked on the parser stack when performing this check, so the
parse stack clean up would release the ops, but the field initaliser
code removes the OP from the parse stack before
class_set_field_defop() so the OP and its children leaked.

To prevent that, populate the defop for the field with the supplied ops
before calling forbid_outofblock_ops(), then as the stack rewinds
class_seal_stash() will check the error count and free the ops.

Fixes Perl#20812
tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 5, 2023
Previously if forbid_outofblock_ops() here threw an error the ops
from defop would leak, including leaking the slab(s) containing
those ops.

The other callers to forbid_outofblock_ops() left the ops being
checked on the parser stack when performing this check, so the
parse stack clean up would release the ops, but the field initaliser
code removes the OP from the parse stack before
class_set_field_defop() so the OP and its children leaked.

To prevent that, populate the defop for the field with the supplied ops
before calling forbid_outofblock_ops(), then as the stack rewinds
class_seal_stash() will check the error count and free the ops.

Fixes Perl#20812
tonycoz added a commit to tonycoz/perl5 that referenced this issue Sep 7, 2023
Previously if forbid_outofblock_ops() here threw an error the ops
from defop would leak, including leaking the slab(s) containing
those ops.

To prevent that, populate the defop for the field with the supplied ops
before calling forbid_outofblock_ops(), then as the save stack rewinds
class_seal_stash() will check the error count and free the ops.

Fixes Perl#20812
tonycoz added a commit that referenced this issue Sep 14, 2023
Previously if forbid_outofblock_ops() here threw an error the ops
from defop would leak, including leaking the slab(s) containing
those ops.

To prevent that, populate the defop for the field with the supplied ops
before calling forbid_outofblock_ops(), then as the save stack rewinds
class_seal_stash() will check the error count and free the ops.

Fixes #20812
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants