diff options
author | Father Chrysostomos <sprout@cpan.org> | 2013-10-28 21:59:14 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2013-12-21 18:09:54 -0800 |
commit | c1cec775e9019cc8ae244d4db239a7ea5c0b343e (patch) | |
tree | f2c2393343552b8a6e74bce1257e8770413d1839 /op.c | |
parent | bb02e0c10a57ab28b9ec6ca218c7aa6aac53a90c (diff) | |
download | perl-c1cec775e9019cc8ae244d4db239a7ea5c0b343e.tar.gz |
[perl #119801] Stop @DB::dbline modifications from crashing
The cop address for each breakable line was being stored in the IVX
slot of ${"_<$file"}[$line]. This value itself, writable from Perl
space, was being used as the address of the op to be flagged, whenever
a breakpoint was set.
This meant writing to ${"_<$file"}[$line] and assigning a number (like
42) would cause perl to use 42 as an op address, and crash when trying
to flag the op.
Furthermore, since the array holding the lines could outlive the ops,
setting a breakpoint on the op could write to freed memory or to an
unrelated op (even a different type), potentially changing the beha-
viour of unrelated code.
This commit solves those pitfalls by moving breakpoints into a global
breakpoint bitfield. Dbstate ops now have an extra field on the end
holding a sequence number, representing which bit holds the breakpoint
for that op.
Diffstat (limited to 'op.c')
-rw-r--r-- | op.c | 22 |
1 files changed, 19 insertions, 3 deletions
@@ -5922,12 +5922,28 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o) flags &= ~SVf_UTF8; - NewOp(1101, cop, 1, COP); if (PERLDB_LINE && CopLINE(PL_curcop) && PL_curstash != PL_debstash) { + size_t sz, seq; + NewOp(1101, *(struct dbop **)&cop, 1, struct dbop); cop->op_type = OP_DBSTATE; cop->op_ppaddr = PL_ppaddr[ OP_DBSTATE ]; + OP_REFCNT_LOCK; + sz = PL_breakpointseq+8/8; + if (!PL_breakpoints) { + PL_breakpoints = (U8 *)PerlMemShared_malloc(sz); + PL_breakpointslen = sz; + } + else if (PL_breakpointslen < sz) { + PL_breakpoints = + (U8 *)PerlMemShared_realloc(PL_breakpoints,sz); + PL_breakpointslen = sz; + } + seq = ((struct dbop *)cop)->dbop_seq = PL_breakpointseq++; + PL_breakpoints[seq/8] &= ~(U8)(1 << seq%8); + OP_REFCNT_UNLOCK; } else { + NewOp(1101, cop, 1, COP); cop->op_type = OP_NEXTSTATE; cop->op_ppaddr = PL_ppaddr[ OP_NEXTSTATE ]; } @@ -5972,13 +5988,13 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o) CopSTASH_set(cop, PL_curstash); if (cop->op_type == OP_DBSTATE) { - /* this line can have a breakpoint - store the cop in IV */ + /* this line can have a breakpoint - store the dbop seq in IV */ AV *av = CopFILEAVx(PL_curcop); if (av) { SV * const * const svp = av_fetch(av, CopLINE(cop), FALSE); if (svp && *svp != &PL_sv_undef ) { (void)SvIOK_on(*svp); - SvIV_set(*svp, PTR2IV(cop)); + SvUV_set(*svp, ((struct dbop *)cop)->dbop_seq); } } } |