code-intelligence.com/cifuzz@v0.40.0/third-party/minijail/tools/bpf.py (about)

     1  #!/usr/bin/env python3
     2  # -*- coding: utf-8 -*-
     3  #
     4  # Copyright (C) 2018 The Android Open Source Project
     5  #
     6  # Licensed under the Apache License, Version 2.0 (the "License");
     7  # you may not use this file except in compliance with the License.
     8  # You may obtain a copy of the License at
     9  #
    10  #      http://www.apache.org/licenses/LICENSE-2.0
    11  #
    12  # Unless required by applicable law or agreed to in writing, software
    13  # distributed under the License is distributed on an "AS IS" BASIS,
    14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  # See the License for the specific language governing permissions and
    16  # limitations under the License.
    17  """Tools to interact with BPF programs."""
    18  
    19  import abc
    20  import collections
    21  import struct
    22  
    23  # This comes from syscall(2). Most architectures only support passing 6 args to
    24  # syscalls, but ARM supports passing 7.
    25  MAX_SYSCALL_ARGUMENTS = 7
    26  
    27  # The following fields were copied from <linux/bpf_common.h>:
    28  
    29  # Instruction classes
    30  BPF_LD = 0x00
    31  BPF_LDX = 0x01
    32  BPF_ST = 0x02
    33  BPF_STX = 0x03
    34  BPF_ALU = 0x04
    35  BPF_JMP = 0x05
    36  BPF_RET = 0x06
    37  BPF_MISC = 0x07
    38  
    39  # LD/LDX fields.
    40  # Size
    41  BPF_W = 0x00
    42  BPF_H = 0x08
    43  BPF_B = 0x10
    44  # Mode
    45  BPF_IMM = 0x00
    46  BPF_ABS = 0x20
    47  BPF_IND = 0x40
    48  BPF_MEM = 0x60
    49  BPF_LEN = 0x80
    50  BPF_MSH = 0xa0
    51  
    52  # JMP fields.
    53  BPF_JA = 0x00
    54  BPF_JEQ = 0x10
    55  BPF_JGT = 0x20
    56  BPF_JGE = 0x30
    57  BPF_JSET = 0x40
    58  
    59  # Source
    60  BPF_K = 0x00
    61  BPF_X = 0x08
    62  
    63  BPF_MAXINSNS = 4096
    64  
    65  # The following fields were copied from <linux/seccomp.h>:
    66  
    67  SECCOMP_RET_KILL_PROCESS = 0x80000000
    68  SECCOMP_RET_KILL_THREAD = 0x00000000
    69  SECCOMP_RET_TRAP = 0x00030000
    70  SECCOMP_RET_ERRNO = 0x00050000
    71  SECCOMP_RET_TRACE = 0x7ff00000
    72  SECCOMP_RET_USER_NOTIF = 0x7fc00000
    73  SECCOMP_RET_LOG = 0x7ffc0000
    74  SECCOMP_RET_ALLOW = 0x7fff0000
    75  
    76  SECCOMP_RET_ACTION_FULL = 0xffff0000
    77  SECCOMP_RET_DATA = 0x0000ffff
    78  
    79  
    80  def arg_offset(arg_index, hi=False):
    81      """Return the BPF_LD|BPF_W|BPF_ABS addressing-friendly register offset."""
    82      offsetof_args = 4 + 4 + 8
    83      arg_width = 8
    84      return offsetof_args + arg_width * arg_index + (arg_width // 2) * hi
    85  
    86  
    87  def simulate(instructions, arch, syscall_number, *args):
    88      """Simulate a BPF program with the given arguments."""
    89      args = ((args + (0, ) *
    90               (MAX_SYSCALL_ARGUMENTS - len(args)))[:MAX_SYSCALL_ARGUMENTS])
    91      input_memory = struct.pack('IIQ' + 'Q' * MAX_SYSCALL_ARGUMENTS,
    92                                 syscall_number, arch, 0, *args)
    93  
    94      register = 0
    95      program_counter = 0
    96      cost = 0
    97      while program_counter < len(instructions):
    98          ins = instructions[program_counter]
    99          program_counter += 1
   100          cost += 1
   101          if ins.code == BPF_LD | BPF_W | BPF_ABS:
   102              register = struct.unpack('I', input_memory[ins.k:ins.k + 4])[0]
   103          elif ins.code == BPF_JMP | BPF_JA | BPF_K:
   104              program_counter += ins.k
   105          elif ins.code == BPF_JMP | BPF_JEQ | BPF_K:
   106              if register == ins.k:
   107                  program_counter += ins.jt
   108              else:
   109                  program_counter += ins.jf
   110          elif ins.code == BPF_JMP | BPF_JGT | BPF_K:
   111              if register > ins.k:
   112                  program_counter += ins.jt
   113              else:
   114                  program_counter += ins.jf
   115          elif ins.code == BPF_JMP | BPF_JGE | BPF_K:
   116              if register >= ins.k:
   117                  program_counter += ins.jt
   118              else:
   119                  program_counter += ins.jf
   120          elif ins.code == BPF_JMP | BPF_JSET | BPF_K:
   121              if register & ins.k != 0:
   122                  program_counter += ins.jt
   123              else:
   124                  program_counter += ins.jf
   125          elif ins.code == BPF_RET:
   126              if ins.k == SECCOMP_RET_KILL_PROCESS:
   127                  return (cost, 'KILL_PROCESS')
   128              if ins.k == SECCOMP_RET_KILL_THREAD:
   129                  return (cost, 'KILL_THREAD')
   130              if ins.k == SECCOMP_RET_TRAP:
   131                  return (cost, 'TRAP')
   132              if (ins.k & SECCOMP_RET_ACTION_FULL) == SECCOMP_RET_ERRNO:
   133                  return (cost, 'ERRNO', ins.k & SECCOMP_RET_DATA)
   134              if ins.k == SECCOMP_RET_TRACE:
   135                  return (cost, 'TRACE')
   136              if ins.k == SECCOMP_RET_USER_NOTIF:
   137                  return (cost, 'USER_NOTIF')
   138              if ins.k == SECCOMP_RET_LOG:
   139                  return (cost, 'LOG')
   140              if ins.k == SECCOMP_RET_ALLOW:
   141                  return (cost, 'ALLOW')
   142              raise Exception('unknown return %#x' % ins.k)
   143          else:
   144              raise Exception('unknown instruction %r' % (ins, ))
   145      raise Exception('out-of-bounds')
   146  
   147  
   148  class SockFilter(
   149          collections.namedtuple('SockFilter', ['code', 'jt', 'jf', 'k'])):
   150      """A representation of struct sock_filter."""
   151  
   152      __slots__ = ()
   153  
   154      def encode(self):
   155          """Return an encoded version of the SockFilter."""
   156          return struct.pack('HBBI', self.code, self.jt, self.jf, self.k)
   157  
   158  
   159  class AbstractBlock(abc.ABC):
   160      """A class that implements the visitor pattern."""
   161  
   162      def __init__(self):
   163          super().__init__()
   164  
   165      @abc.abstractmethod
   166      def accept(self, visitor):
   167          pass
   168  
   169  
   170  class BasicBlock(AbstractBlock):
   171      """A concrete implementation of AbstractBlock that has been compiled."""
   172  
   173      def __init__(self, instructions):
   174          super().__init__()
   175          self._instructions = instructions
   176  
   177      def accept(self, visitor):
   178          if visitor.visited(self):
   179              return
   180          visitor.visit(self)
   181  
   182      @property
   183      def instructions(self):
   184          return self._instructions
   185  
   186      @property
   187      def opcodes(self):
   188          return b''.join(i.encode() for i in self._instructions)
   189  
   190      def __eq__(self, o):
   191          if not isinstance(o, BasicBlock):
   192              return False
   193          return self._instructions == o._instructions
   194  
   195  
   196  class KillProcess(BasicBlock):
   197      """A BasicBlock that unconditionally returns KILL_PROCESS."""
   198  
   199      def __init__(self):
   200          super().__init__(
   201              [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_PROCESS)])
   202  
   203  
   204  class KillThread(BasicBlock):
   205      """A BasicBlock that unconditionally returns KILL_THREAD."""
   206  
   207      def __init__(self):
   208          super().__init__(
   209              [SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_KILL_THREAD)])
   210  
   211  
   212  class Trap(BasicBlock):
   213      """A BasicBlock that unconditionally returns TRAP."""
   214  
   215      def __init__(self):
   216          super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRAP)])
   217  
   218  
   219  class Trace(BasicBlock):
   220      """A BasicBlock that unconditionally returns TRACE."""
   221  
   222      def __init__(self):
   223          super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_TRACE)])
   224  
   225  
   226  class UserNotify(BasicBlock):
   227      """A BasicBlock that unconditionally returns USER_NOTIF."""
   228  
   229      def __init__(self):
   230          super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_USER_NOTIF)])
   231  
   232  
   233  class Log(BasicBlock):
   234      """A BasicBlock that unconditionally returns LOG."""
   235  
   236      def __init__(self):
   237          super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_LOG)])
   238  
   239  
   240  class ReturnErrno(BasicBlock):
   241      """A BasicBlock that unconditionally returns the specified errno."""
   242  
   243      def __init__(self, errno):
   244          super().__init__([
   245              SockFilter(BPF_RET, 0x00, 0x00,
   246                         SECCOMP_RET_ERRNO | (errno & SECCOMP_RET_DATA))
   247          ])
   248          self.errno = errno
   249  
   250  
   251  class Allow(BasicBlock):
   252      """A BasicBlock that unconditionally returns ALLOW."""
   253  
   254      def __init__(self):
   255          super().__init__([SockFilter(BPF_RET, 0x00, 0x00, SECCOMP_RET_ALLOW)])
   256  
   257  
   258  class ValidateArch(AbstractBlock):
   259      """An AbstractBlock that validates the architecture."""
   260  
   261      def __init__(self, next_block):
   262          super().__init__()
   263          self.next_block = next_block
   264  
   265      def accept(self, visitor):
   266          if visitor.visited(self):
   267              return
   268          self.next_block.accept(visitor)
   269          visitor.visit(self)
   270  
   271  
   272  class SyscallEntry(AbstractBlock):
   273      """An abstract block that represents a syscall comparison in a DAG."""
   274  
   275      def __init__(self, syscall_number, jt, jf, *, op=BPF_JEQ):
   276          super().__init__()
   277          self.op = op
   278          self.syscall_number = syscall_number
   279          self.jt = jt
   280          self.jf = jf
   281  
   282      def __lt__(self, o):
   283          # Defined because we want to compare tuples that contain SyscallEntries.
   284          return False
   285  
   286      def __gt__(self, o):
   287          # Defined because we want to compare tuples that contain SyscallEntries.
   288          return False
   289  
   290      def accept(self, visitor):
   291          if visitor.visited(self):
   292              return
   293          self.jt.accept(visitor)
   294          self.jf.accept(visitor)
   295          visitor.visit(self)
   296  
   297      def __lt__(self, o):
   298          # Defined because we want to compare tuples that contain SyscallEntries.
   299          return False
   300  
   301      def __gt__(self, o):
   302          # Defined because we want to compare tuples that contain SyscallEntries.
   303          return False
   304  
   305  
   306  class WideAtom(AbstractBlock):
   307      """A BasicBlock that represents a 32-bit wide atom."""
   308  
   309      def __init__(self, arg_offset, op, value, jt, jf):
   310          super().__init__()
   311          self.arg_offset = arg_offset
   312          self.op = op
   313          self.value = value
   314          self.jt = jt
   315          self.jf = jf
   316  
   317      def accept(self, visitor):
   318          if visitor.visited(self):
   319              return
   320          self.jt.accept(visitor)
   321          self.jf.accept(visitor)
   322          visitor.visit(self)
   323  
   324  
   325  class Atom(AbstractBlock):
   326      """A BasicBlock that represents an atom (a simple comparison operation)."""
   327  
   328      def __init__(self, arg_index, op, value, jt, jf):
   329          super().__init__()
   330          if op == '==':
   331              op = BPF_JEQ
   332          elif op == '!=':
   333              op = BPF_JEQ
   334              jt, jf = jf, jt
   335          elif op == '>':
   336              op = BPF_JGT
   337          elif op == '<=':
   338              op = BPF_JGT
   339              jt, jf = jf, jt
   340          elif op == '>=':
   341              op = BPF_JGE
   342          elif op == '<':
   343              op = BPF_JGE
   344              jt, jf = jf, jt
   345          elif op == '&':
   346              op = BPF_JSET
   347          elif op == 'in':
   348              op = BPF_JSET
   349              # The mask is negated, so the comparison will be true when the
   350              # argument includes a flag that wasn't listed in the original
   351              # (non-negated) mask. This would be the failure case, so we switch
   352              # |jt| and |jf|.
   353              value = (~value) & ((1 << 64) - 1)
   354              jt, jf = jf, jt
   355          else:
   356              raise Exception('Unknown operator %s' % op)
   357  
   358          self.arg_index = arg_index
   359          self.op = op
   360          self.jt = jt
   361          self.jf = jf
   362          self.value = value
   363  
   364      def accept(self, visitor):
   365          if visitor.visited(self):
   366              return
   367          self.jt.accept(visitor)
   368          self.jf.accept(visitor)
   369          visitor.visit(self)
   370  
   371  
   372  class AbstractVisitor(abc.ABC):
   373      """An abstract visitor."""
   374  
   375      def __init__(self):
   376          self._visited = set()
   377  
   378      def visited(self, block):
   379          if id(block) in self._visited:
   380              return True
   381          self._visited.add(id(block))
   382          return False
   383  
   384      def process(self, block):
   385          block.accept(self)
   386          return block
   387  
   388      def visit(self, block):
   389          if isinstance(block, KillProcess):
   390              self.visitKillProcess(block)
   391          elif isinstance(block, KillThread):
   392              self.visitKillThread(block)
   393          elif isinstance(block, Trap):
   394              self.visitTrap(block)
   395          elif isinstance(block, ReturnErrno):
   396              self.visitReturnErrno(block)
   397          elif isinstance(block, Trace):
   398              self.visitTrace(block)
   399          elif isinstance(block, UserNotify):
   400              self.visitUserNotify(block)
   401          elif isinstance(block, Log):
   402              self.visitLog(block)
   403          elif isinstance(block, Allow):
   404              self.visitAllow(block)
   405          elif isinstance(block, BasicBlock):
   406              self.visitBasicBlock(block)
   407          elif isinstance(block, ValidateArch):
   408              self.visitValidateArch(block)
   409          elif isinstance(block, SyscallEntry):
   410              self.visitSyscallEntry(block)
   411          elif isinstance(block, WideAtom):
   412              self.visitWideAtom(block)
   413          elif isinstance(block, Atom):
   414              self.visitAtom(block)
   415          else:
   416              raise Exception('Unknown block type: %r' % block)
   417  
   418      @abc.abstractmethod
   419      def visitKillProcess(self, block):
   420          pass
   421  
   422      @abc.abstractmethod
   423      def visitKillThread(self, block):
   424          pass
   425  
   426      @abc.abstractmethod
   427      def visitTrap(self, block):
   428          pass
   429  
   430      @abc.abstractmethod
   431      def visitReturnErrno(self, block):
   432          pass
   433  
   434      @abc.abstractmethod
   435      def visitTrace(self, block):
   436          pass
   437  
   438      @abc.abstractmethod
   439      def visitUserNotify(self, block):
   440          pass
   441  
   442      @abc.abstractmethod
   443      def visitLog(self, block):
   444          pass
   445  
   446      @abc.abstractmethod
   447      def visitAllow(self, block):
   448          pass
   449  
   450      @abc.abstractmethod
   451      def visitBasicBlock(self, block):
   452          pass
   453  
   454      @abc.abstractmethod
   455      def visitValidateArch(self, block):
   456          pass
   457  
   458      @abc.abstractmethod
   459      def visitSyscallEntry(self, block):
   460          pass
   461  
   462      @abc.abstractmethod
   463      def visitWideAtom(self, block):
   464          pass
   465  
   466      @abc.abstractmethod
   467      def visitAtom(self, block):
   468          pass
   469  
   470  
   471  class CopyingVisitor(AbstractVisitor):
   472      """A visitor that copies Blocks."""
   473  
   474      def __init__(self):
   475          super().__init__()
   476          self._mapping = {}
   477  
   478      def process(self, block):
   479          self._mapping = {}
   480          block.accept(self)
   481          return self._mapping[id(block)]
   482  
   483      def visitKillProcess(self, block):
   484          assert id(block) not in self._mapping
   485          self._mapping[id(block)] = KillProcess()
   486  
   487      def visitKillThread(self, block):
   488          assert id(block) not in self._mapping
   489          self._mapping[id(block)] = KillThread()
   490  
   491      def visitTrap(self, block):
   492          assert id(block) not in self._mapping
   493          self._mapping[id(block)] = Trap()
   494  
   495      def visitReturnErrno(self, block):
   496          assert id(block) not in self._mapping
   497          self._mapping[id(block)] = ReturnErrno(block.errno)
   498  
   499      def visitTrace(self, block):
   500          assert id(block) not in self._mapping
   501          self._mapping[id(block)] = Trace()
   502  
   503      def visitUserNotify(self, block):
   504          assert id(block) not in self._mapping
   505          self._mapping[id(block)] = UserNotify()
   506  
   507      def visitLog(self, block):
   508          assert id(block) not in self._mapping
   509          self._mapping[id(block)] = Log()
   510  
   511      def visitAllow(self, block):
   512          assert id(block) not in self._mapping
   513          self._mapping[id(block)] = Allow()
   514  
   515      def visitBasicBlock(self, block):
   516          assert id(block) not in self._mapping
   517          self._mapping[id(block)] = BasicBlock(block.instructions)
   518  
   519      def visitValidateArch(self, block):
   520          assert id(block) not in self._mapping
   521          self._mapping[id(block)] = ValidateArch(
   522              block.arch, self._mapping[id(block.next_block)])
   523  
   524      def visitSyscallEntry(self, block):
   525          assert id(block) not in self._mapping
   526          self._mapping[id(block)] = SyscallEntry(
   527              block.syscall_number,
   528              self._mapping[id(block.jt)],
   529              self._mapping[id(block.jf)],
   530              op=block.op)
   531  
   532      def visitWideAtom(self, block):
   533          assert id(block) not in self._mapping
   534          self._mapping[id(block)] = WideAtom(
   535              block.arg_offset, block.op, block.value, self._mapping[id(
   536                  block.jt)], self._mapping[id(block.jf)])
   537  
   538      def visitAtom(self, block):
   539          assert id(block) not in self._mapping
   540          self._mapping[id(block)] = Atom(block.arg_index, block.op, block.value,
   541                                          self._mapping[id(block.jt)],
   542                                          self._mapping[id(block.jf)])
   543  
   544  
   545  class LoweringVisitor(CopyingVisitor):
   546      """A visitor that lowers Atoms into WideAtoms."""
   547  
   548      def __init__(self, *, arch):
   549          super().__init__()
   550          self._bits = arch.bits
   551  
   552      def visitAtom(self, block):
   553          assert id(block) not in self._mapping
   554  
   555          lo = block.value & 0xFFFFFFFF
   556          hi = (block.value >> 32) & 0xFFFFFFFF
   557  
   558          lo_block = WideAtom(
   559              arg_offset(block.arg_index, False), block.op, lo,
   560              self._mapping[id(block.jt)], self._mapping[id(block.jf)])
   561  
   562          if self._bits == 32:
   563              self._mapping[id(block)] = lo_block
   564              return
   565  
   566          if block.op in (BPF_JGE, BPF_JGT):
   567              # hi_1,lo_1 <op> hi_2,lo_2
   568              #
   569              # hi_1 > hi_2 || hi_1 == hi_2 && lo_1 <op> lo_2
   570              if hi == 0:
   571                  # Special case: it's not needed to check whether |hi_1 == hi_2|,
   572                  # because it's true iff the JGT test fails.
   573                  self._mapping[id(block)] = WideAtom(
   574                      arg_offset(block.arg_index, True), BPF_JGT, hi,
   575                      self._mapping[id(block.jt)], lo_block)
   576                  return
   577              hi_eq_block = WideAtom(
   578                  arg_offset(block.arg_index, True), BPF_JEQ, hi, lo_block,
   579                  self._mapping[id(block.jf)])
   580              self._mapping[id(block)] = WideAtom(
   581                  arg_offset(block.arg_index, True), BPF_JGT, hi,
   582                  self._mapping[id(block.jt)], hi_eq_block)
   583              return
   584          if block.op == BPF_JSET:
   585              # hi_1,lo_1 & hi_2,lo_2
   586              #
   587              # hi_1 & hi_2 || lo_1 & lo_2
   588              if hi == 0:
   589                  # Special case: |hi_1 & hi_2| will never be True, so jump
   590                  # directly into the |lo_1 & lo_2| case.
   591                  self._mapping[id(block)] = lo_block
   592                  return
   593              self._mapping[id(block)] = WideAtom(
   594                  arg_offset(block.arg_index, True), block.op, hi,
   595                  self._mapping[id(block.jt)], lo_block)
   596              return
   597  
   598          assert block.op == BPF_JEQ, block.op
   599  
   600          # hi_1,lo_1 == hi_2,lo_2
   601          #
   602          # hi_1 == hi_2 && lo_1 == lo_2
   603          self._mapping[id(block)] = WideAtom(
   604              arg_offset(block.arg_index, True), block.op, hi, lo_block,
   605              self._mapping[id(block.jf)])
   606  
   607  
   608  class FlatteningVisitor:
   609      """A visitor that flattens a DAG of Block objects."""
   610  
   611      def __init__(self, *, arch, kill_action):
   612          self._visited = set()
   613          self._kill_action = kill_action
   614          self._instructions = []
   615          self._arch = arch
   616          self._offsets = {}
   617  
   618      @property
   619      def result(self):
   620          return BasicBlock(self._instructions)
   621  
   622      def _distance(self, block):
   623          distance = self._offsets[id(block)] + len(self._instructions)
   624          assert distance >= 0
   625          return distance
   626  
   627      def _emit_load_arg(self, offset):
   628          return [SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, offset)]
   629  
   630      def _emit_jmp(self, op, value, jt_distance, jf_distance):
   631          if jt_distance < 0x100 and jf_distance < 0x100:
   632              return [
   633                  SockFilter(BPF_JMP | op | BPF_K, jt_distance, jf_distance,
   634                             value),
   635              ]
   636          if jt_distance + 1 < 0x100:
   637              return [
   638                  SockFilter(BPF_JMP | op | BPF_K, jt_distance + 1, 0, value),
   639                  SockFilter(BPF_JMP | BPF_JA, 0, 0, jf_distance),
   640              ]
   641          if jf_distance + 1 < 0x100:
   642              return [
   643                  SockFilter(BPF_JMP | op | BPF_K, 0, jf_distance + 1, value),
   644                  SockFilter(BPF_JMP | BPF_JA, 0, 0, jt_distance),
   645              ]
   646          return [
   647              SockFilter(BPF_JMP | op | BPF_K, 0, 1, value),
   648              SockFilter(BPF_JMP | BPF_JA, 0, 0, jt_distance + 1),
   649              SockFilter(BPF_JMP | BPF_JA, 0, 0, jf_distance),
   650          ]
   651  
   652      def visited(self, block):
   653          if id(block) in self._visited:
   654              return True
   655          self._visited.add(id(block))
   656          return False
   657  
   658      def visit(self, block):
   659          assert id(block) not in self._offsets
   660  
   661          if isinstance(block, BasicBlock):
   662              instructions = block.instructions
   663          elif isinstance(block, ValidateArch):
   664              instructions = [
   665                  SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, 4),
   666                  SockFilter(BPF_JMP | BPF_JEQ | BPF_K,
   667                             self._distance(block.next_block) + 1, 0,
   668                             self._arch.arch_nr),
   669              ] + self._kill_action.instructions + [
   670                  SockFilter(BPF_LD | BPF_W | BPF_ABS, 0, 0, 0),
   671              ]
   672          elif isinstance(block, SyscallEntry):
   673              instructions = self._emit_jmp(block.op, block.syscall_number,
   674                                            self._distance(block.jt),
   675                                            self._distance(block.jf))
   676          elif isinstance(block, WideAtom):
   677              instructions = (
   678                  self._emit_load_arg(block.arg_offset) + self._emit_jmp(
   679                      block.op, block.value, self._distance(block.jt),
   680                      self._distance(block.jf)))
   681          else:
   682              raise Exception('Unknown block type: %r' % block)
   683  
   684          self._instructions = instructions + self._instructions
   685          self._offsets[id(block)] = -len(self._instructions)
   686          return
   687  
   688  
   689  class ArgFilterForwardingVisitor:
   690      """A visitor that forwards visitation to all arg filters."""
   691  
   692      def __init__(self, visitor):
   693          self._visited = set()
   694          self.visitor = visitor
   695  
   696      def visited(self, block):
   697          if id(block) in self._visited:
   698              return True
   699          self._visited.add(id(block))
   700          return False
   701  
   702      def visit(self, block):
   703          # All arg filters are BasicBlocks.
   704          if not isinstance(block, BasicBlock):
   705              return
   706          # But the ALLOW, KILL_PROCESS, TRAP, etc. actions are too and we don't
   707          # want to visit them just yet.
   708          if (isinstance(block, KillProcess) or isinstance(block, KillThread)
   709                  or isinstance(block, Trap) or isinstance(block, ReturnErrno)
   710                  or isinstance(block, Trace) or isinstance(block, UserNotify)
   711                  or isinstance(block, Log) or isinstance(block, Allow)):
   712              return
   713          block.accept(self.visitor)