Nosferatu - A Symphony of Horror

Nosferatu - A Symphony of Horror

Perl 5 VM - A Symphony of Horror

Perl 5 VM - A Symphony of Horror

Example code - sum

my $sum = 0;
while ( my $_ = <> ) {
    $sum += $_;
}
say $sum;

Perl is a forest

Our Microscope

A tree

$ perl -MO=Concise bin/sum
m  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 44 sum:5) v:%,*,&,{,$ ->3
5     <2> sassign vKS/2 ->6
3        <$> const(IV 0) s ->4
4        <0> padsv[$sum:44,48] sRM*/LVINTRO ->5
6     <;> nextstate(main 47 sum:6) v:%,*,&,$ ->7
h     <2> leaveloop vK/2 ->i
7        <{> enterloop(next->b last->h redo->8) v ->c
-        <1> null vK/1 ->h
g           <|> and(other->8) vK/1 ->h
f              <1> defined sK/1 ->g
-                 <1> null sKS/2 ->f
c                    <0> padsv[$_:45,47] sRM*/LVINTRO ->d
e                    <1> readline[t3] sKS/1 ->f
d                       <$> gv(*ARGV) s ->e
-              <@> lineseq vK ->-

Execution order

perl -MO=Concise,-exec bin/sum
1  <0> enter
2  <;> nextstate(main 44 sum:5) v:%,*,&,{,$
3  <$> const(IV 0) s
4  <0> padsv[$sum:44,48] sRM*/LVINTRO
5  <2> sassign vKS/2
6  <;> nextstate(main 47 sum:6) v:%,*,&,$
7  <{> enterloop(next->b last->h redo->8) v
c  <0> padsv[$_:45,47] sRM*/LVINTRO
d  <$> gv(*ARGV) s
e  <1> readline[t3] sKS/1
f  <1> defined sK/1
g  <|> and(other->8) vK/1
8      <0> padsv[$sum:44,48] sRM
9      <0> padsv[$_:45,47] s
a      <2> add[t4] vKS/2
b      <0> unstack v
goto c

Ignoring lots of stuff, tree

perl -MO=Concise bin/sum | bin/idealized-optree
leave
    enter
    nextstate(main sum:5)

    sassign
        const 0 
        padsv $sum       
    nextstate(main sum:6)

Ignoring lots of stuff, exec

perl -MO=Concise,exec bin/sum | bin/hand-waving
enter
nextstate(main sum:5)

const 0
padsv $sum
sassign
nextstate(main sum:6)

leave

Statement + smidge, tree

$sum = 0
simplified tree

Statement + smidge, tree

  • ↓ First
  • ⚫ Execute
  • → Sibling
simplified tree

Statement + smidge, exec

 
simplified tree

Opcodes

  • Oodles of opcodes
  • Handful of classes
B::OP classes

Scalar assignment, gutsy

perl -MO=Debug bin/sum
BINOP (0x81facc8)
    next         0x…
    sibling      0x…
    ppaddr       PL_ppaddr[OP_SASSIGN]
    type         36
    opt          1
    flags        69
    private      2
    first        0x…

Scalar assignment, exec

sketch of execution order for "$sum = 0"

Stuff on the stack

simplified sketch of "$sum = 0"

Runloop

o = …->start
while ( o ) {
    o = o->ppaddr();
}

Stacks

From Gisle Aas' PerlGuts Illustrated

local()

  1. pushes onto scope stack
  2. pops off scope stack
    • reverts saved pointers
    • --(ref count for temporaries)
execution order for "$sum = 0"

overloading

sketch of a disconnected "add" opcode

Pads - Lexicals & Temporaries

Names

PADLIST[0][ op->TARG ]

  1. NULL
  2. "$_"
  3. "$sum"

Pads - Lexicals & Temporaries

Values

PADLIST[1..][ op->TARG ]

  1. $_
  2. $sum

Pads - Lexicals & Temporaries

VCG diagram of a PADLIST

Lexical Pragmas

package eldritch;
use pragma -base;
$H^{eldritch} = 1
nextstate->cop_hints_hash = \ %^H

Everyone loses a sanity point

Anyone still in the room loses a sanity point

B

my $padsv = main_root()
    ->first
    ->sibling
    ->first
    ->sibling;

B

walkoptree(
    main_root,
    'my_method'
);

sub B::OP::my_method {
    …

B::Utils

walkallops_filtered(
    …,
    sub {
        opgrep( {
            name => 'sassign',
            first => {
                name => 'const',
                iv => 0,
                sibling => {
                    name => 'padsv'
                },
            },
        } );
    }
);

B::XPath

//sassign/const[@iv=0]/sibling::padsv

B::Lint

MAGIC_DIAMOND: {
    next
        if not(
            $check{magic_diamond}
            and parents->[0]->name eq 'readline'
            and $op->gv_harder->NAME eq 'ARGV'
        );

    warning 'Use of <>';
}

B::Lint::Plugin::*

Add your own warnings

B::Lint->register_plugin( __PACKAGE__ => [ 'good_taste' ];

sub match {
    my ( $op, $checks_href ) = @_;
    if ( $checks_href->{good_taste} ) {
        …
    }
}

Hook builtins

PL_ppaddr[OP_EVAL]

  • Clobber it!
  • Localize it!

 overload::eval

Wraps PL_ppaddr[OP_EVAL]

 overload::eval

use overload::eval 'hook';
eval 'milk?';
sub hook { say "Got @_" }

Deobfuscating perl

eval rot13( 'fnl "Hryyb!"' )
perl -Moverload::eval=-p obfu.pl

UNIVERSAL::ref

Edits the past too

Good for proxies and lying liar objects

fix( $_ ) for B::Utils::all_roots

sub fix ($o) {
        …
	$o->ppaddr( NEW ) if $o->type == OP_REF;
	
        fix $o->first
        fix $o->sibling
}

Hookable runloop

Runops::Trace @ Work

  • 1e7 inputs
  • 1e5 code paths
  • 200 tests for full coverage

--SAN

Everyone loses another sanity point

B::Generate

my $pushmark = B::OP->new( pushmark => 2 );

my $die = B::LISTOP->new(
    die => 5,
    $pushmark,
    $gvsv );
$die->targ( 1 );
$die->private( 1 );

my $or_root = B::LOGOP->new(
    or => 2,
    $op,
    $die )

B::OptreeDiff

optree_diff( \&foo, \&bar )
- /leavesub/lineseq/nextstate*print
+ /leavesub/lineseq/nextstate*null
+                                 .op_flags = 4
+                                 .op_private = 1
+                                 .op_targ = 0
+ /leavesub/lineseq/nextstate*null/or
+                                    .op_flags = 4
+                                    .op_other = 0
+                                    .op_private = 1
+                                    .op_targ = 0
+ /leavesub/lineseq/nextstate*null/or/print

B::Simple (from svn only)

See #moose

$tree = Seq(
    Apply(
        Op( '=' ),
        Const( 0 ),
        Var( '$sum' )
    )
);

Roll against your sanity

Roll against your sanity to see if you lose another point

Optimizations

Learn moar? - IRC

Learn moar? - Books

Learn moar? - Articles

Code

Modules not previously mentioned

.