Module: Seccomp::Notify

Defined in:
lib/seccomp/notify.rb,
lib/seccomp/notify/libc.rb,
lib/seccomp/notify/ioctl.rb,
lib/seccomp/notify/errors.rb,
lib/seccomp/notify/filter.rb,
lib/seccomp/notify/policy.rb,
lib/seccomp/notify/request.rb,
lib/seccomp/notify/structs.rb,
lib/seccomp/notify/version.rb,
lib/seccomp/notify/features.rb,
lib/seccomp/notify/syscalls.rb,
lib/seccomp/notify/constants.rb,
lib/seccomp/notify/fd_passing.rb,
lib/seccomp/notify/supervisor.rb,
lib/seccomp/notify/bpf/builder.rb,
lib/seccomp/notify/bpf/program.rb,
lib/seccomp/notify/target_memory.rb,
lib/seccomp/notify/bpf/instruction.rb

Defined Under Namespace

Modules: BPF, Constants, FdPassing, Features, Filter, Ioctl, Libc, Structs, Syscalls Classes: AlreadyRespondedError, Error, FilterTooLargeError, InvalidPolicyError, MemoryReadError, NotSupportedError, Policy, Request, StaleNotificationError, Supervisor, TargetMemory

Constant Summary collapse

VERSION =
"0.2.0"

Class Method Summary collapse

Class Method Details

.featuresHash<Symbol, Boolean>

Detects optional kernel capabilities with isolated child probes.

Returns:

  • (Hash<Symbol, Boolean>)


37
38
39
# File 'lib/seccomp/notify.rb', line 37

def features
  @features ||= Features.detect
end

.notif_sizesHash<Symbol, Integer>

Returns kernel-defined notification structure sizes.

Returns:

  • (Hash<Symbol, Integer>)


43
44
45
# File 'lib/seccomp/notify.rb', line 43

def notif_sizes
  @notif_sizes ||= Libc.notif_sizes
end

.spawn(policy, recv_timeout: 10, **supervisor_options) { ... } ⇒ Supervisor

Starts a filtered child and returns its unfiltered parent supervisor.

Parameters:

Yields:

  • runs in the filtered child

Returns:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/seccomp/notify.rb', line 59

def spawn(policy, recv_timeout: 10, **supervisor_options, &target)
  raise ArgumentError, "target block is required" unless target
  unless recv_timeout.is_a?(Numeric) && recv_timeout.real? && recv_timeout.finite? && recv_timeout.positive?
    raise ArgumentError, "recv_timeout must be positive"
  end

  parent_socket, child_socket = UNIXSocket.pair
  program, install_flags = prepare_supervision(policy, supervisor_options, transfer_fd: child_socket.fileno)
  health_reader, health_writer = IO.pipe
  pid = fork do
    parent_socket.close
    health_writer.close
    keep_health_reader(health_reader)
    listener_fd = Filter.install!(program, flags: install_flags, raw: true)
    FdPassing.send_fd(child_socket, listener_fd)
    listener = IO.for_fd(listener_fd, "r")
    child_socket.close
    listener.close
    target.call
    exit! 0
  rescue Exception => error # rubocop:disable Lint/RescueException
    warn("seccomp-notify target setup failed: #{error.message}")
    exit! 127
  end
  child_socket.close
  health_reader.close
  listener = FdPassing.recv_fd(parent_socket, timeout: recv_timeout)
  parent_socket.close
  Supervisor.new(listener, target_pid: pid, target_child: true, health_writer:, **supervisor_options)
rescue StandardError
  listener&.close unless listener&.closed?
  parent_socket&.close unless parent_socket&.closed?
  child_socket&.close unless child_socket&.closed?
  health_reader&.close unless health_reader&.closed?
  health_writer&.close unless health_writer&.closed?
  terminate_child(pid)
  raise
end

.supervise_self(policy, supervisor: :spawn, **options, &configure) ⇒ Integer

Installs a filter in the caller and starts a detached supervisor child.

Parameters:

  • policy (Policy)
  • supervisor (Symbol) (defaults to: :spawn)

    :spawn or :fork

Returns:

  • (Integer)

    supervisor process id



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/seccomp/notify.rb', line 102

def supervise_self(policy, supervisor: :spawn, **options, &configure)
  raise ArgumentError, "supervisor must be :spawn or :fork" unless %i[spawn fork].include?(supervisor)
  raise ArgumentError, "supervisor: :spawn does not support Ruby handler blocks" if supervisor == :spawn && configure

  selected_features = options.fetch(:features, features)
  raise NotSupportedError, "supervise_self requires CONTINUE when no handler block is given" if !configure && !selected_features[:continue]

  parent_socket, supervisor_socket = UNIXSocket.pair
  program, install_flags = prepare_supervision(policy, options, transfer_fd: parent_socket.fileno)
  health_reader, health_writer = IO.pipe
  target_pid = Process.pid
  supervisor_pid = case supervisor
  when :fork
    fork_supervisor(supervisor_socket, parent_socket, health_reader, health_writer, target_pid, options, configure)
  when :spawn
    spawn_supervisor(supervisor_socket, parent_socket, health_writer, target_pid, options)
  end

  supervisor_socket.close
  health_writer.close
  keep_health_reader(health_reader)
  listener_fd = Filter.install!(program, flags: install_flags, raw: true)
  FdPassing.send_fd(parent_socket, listener_fd)
  listener = IO.for_fd(listener_fd, "r")
  listener.close
  parent_socket.close
  Process.detach(supervisor_pid)
  supervisor_pid
rescue StandardError
  listener&.close unless listener&.closed?
  parent_socket&.close unless parent_socket&.closed?
  supervisor_socket&.close unless supervisor_socket&.closed?
  health_reader&.close unless health_reader&.closed?
  health_writer&.close unless health_writer&.closed?
  clear_health_reader(health_reader)
  terminate_child(supervisor_pid)
  raise
end

.supervisor_alive?Boolean?

Returns whether the supervisor-side health pipe is still open.

Returns:

  • (Boolean, nil)

    nil when the process was not started by this gem



50
51
52
53
# File 'lib/seccomp/notify.rb', line 50

def supervisor_alive?
  reader = supervisor_health_reader
  reader ? IO.select([reader], nil, nil, 0).nil? : nil
end

.supported?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/seccomp/notify.rb', line 31

def supported?
  Features.supported?
end