When an op is allocated, PL_compcv is checked to see whether it can
hold an op slab if it does not hold one already. If PL_compcv is not
usable, for whatever reason, it falls back to malloc.
Since the new slab allocator was added in commit 8be227a, newFOROP has
been assuming, probably correctly, that its listop which it needs to
enlarge to a loopop was allocated by slab.
Since newFOROP is an API function, we should err on the safe side and
check first whether the op is slab-allocated, falling back to realloc
if it is not.
To trigger this potential bug, one has to set things up such that
there is a usable pad available, but no usable PL_compcv. I said
‘probably correctly’ above because this situation is highly unlikely
and probably indicative of bugs elsewhere. (But we should still err
on the side of safety.)
#endif
+bool
+test_newFOROP_without_slab()
+CODE:
+ {
+ const I32 floor = start_subparse(0,0);
+ CV * const cv = PL_compcv;
+ /* The slab allocator does not like CvROOT being set. */
+ CvROOT(PL_compcv) = (OP *)1;
+ op_free(newFOROP(0, 0, newOP(OP_PUSHMARK, 0), 0, 0));
+ CvROOT(PL_compcv) = NULL;
+ SvREFCNT_dec(PL_compcv);
+ LEAVE_SCOPE(floor);
+ /* If we have not crashed yet, then the test passes. */
+ RETVAL = TRUE;
+ }
+OUTPUT:
+ RETVAL
+
+
MODULE = XS::APItest PACKAGE = XS::APItest::AUTOLOADtest
int
*hint_fetch = *hint_fetch = \&XS::APItest::Hash::refcounted_he_fetch;
require '../../t/op/caller.pl';
+
+ok test_newFOROP_without_slab(),
+ 'no assertion failures when allocating FOROP without slab';
* for our $x () sets OPpOUR_INTRO */
loop->op_private = (U8)iterpflags;
#ifndef PL_OP_SLAB_ALLOC
- if (DIFF(loop, OpSLOT(loop)->opslot_next)
+ if (loop->op_slabbed
+ && DIFF(loop, OpSLOT(loop)->opslot_next)
< SIZE_TO_PSIZE(sizeof(LOOP)))
#endif
{
S_op_destroy(aTHX_ (OP*)loop);
loop = tmp;
}
+#ifndef PL_OP_SLAB_ALLOC
+ else if (!loop->op_slabbed)
+ loop = (LOOP*)PerlMemShared_realloc(loop, sizeof(LOOP));
+#endif
loop->op_targ = padoff;
wop = newWHILEOP(flags, 1, loop, newOP(OP_ITER, 0), block, cont, 0);
if (madsv)