Class: SeccompTools::Dumper::Handler

Inherits:
Object
  • Object
show all
Defined in:
lib/seccomp-tools/dumper.rb

Overview

Traces a forked child, single-stepping it through its syscalls and capturing the seccomp filters it installs.

Instance Method Summary collapse

Constructor Details

#initialize(pid) ⇒ Handler

Instantiate a SeccompTools::Dumper::Handler object.

Parameters:

  • pid (Integer)

    The process id after fork.



60
61
62
63
64
65
66
# File 'lib/seccomp-tools/dumper.rb', line 60

def initialize(pid)
  @pids = [pid]
  Process.waitpid(pid)
  opt = Ptrace::O_TRACESYSGOOD | Ptrace::O_TRACECLONE | Ptrace::O_TRACEFORK | Ptrace::O_TRACEVFORK
  Ptrace.setoptions(pid, 0, opt)
  Ptrace.syscall(pid, 0, 0)
end

Instance Method Details

#handle(limit, timeout: nil) {|bpf, arch| ... } ⇒ Array<Object>, Array<String>

Tracer.

Parameters:

  • limit (Integer)

    Child will be killed when number of calling prctl(SET_SECCOMP) reaches limit.

  • timeout (Float?) (defaults to: nil)

    Kill the child processes when timeout seconds have elapsed. nil for no timeout.

Yield Parameters:

  • bpf (String)

    Seccomp bpf in raw bytes.

  • arch (Symbol)

    Architecture. See Syscall::ABI for supported architectures.

Returns:

  • (Array<Object>, Array<String>)

    One entry per dumped filter: the block's return values when a block is given, otherwise the raw bytes.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/seccomp-tools/dumper.rb', line 81

def handle(limit, timeout: nil, &block)
  collect = []
  syscalls = {} # record last syscall
  begin
    Timeout.timeout(timeout) do
      loop while wait_syscall do |child|
        if syscalls[child].nil? # invoke syscall
          syscalls[child] = syscall(child)
          next true
        end
        # syscall finished
        sys = syscalls[child]
        syscalls[child] = nil
        if sys.set_seccomp? && syscall(child).ret.zero? # consider successful call only
          bpf = sys.dump_bpf
          collect << (block.nil? ? bpf : yield(bpf, sys.arch))
          limit -= 1
        end
        !limit.zero?
      end
    end
  rescue Timeout::Error
    # keep the filters dumped so far; fall through to kill the children
  end
  @pids.each { |cpid| Process.kill('KILL', cpid) if alive?(cpid) }
  Process.waitall
  collect
end