seccomp-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,184 @@
1
+ #include "seccomp_ext.h"
2
+ #include <errno.h>
3
+ #include <ruby/thread.h>
4
+ #include <string.h>
5
+ #include <sys/ioctl.h>
6
+
7
+ struct seccomp_receive_args {
8
+ int fd;
9
+ struct seccomp_notif *request;
10
+ int rc;
11
+ };
12
+
13
+ static void *
14
+ seccomp_ext_notify_receive_without_gvl(void *pointer)
15
+ {
16
+ struct seccomp_receive_args *args = pointer;
17
+
18
+ args->rc = seccomp_notify_receive(args->fd, args->request);
19
+ return NULL;
20
+ }
21
+
22
+ static VALUE
23
+ seccomp_ext_notify_receive_free(VALUE pointer)
24
+ {
25
+ struct seccomp_receive_args *args = (struct seccomp_receive_args *)pointer;
26
+
27
+ seccomp_notify_free(args->request, NULL);
28
+ args->request = NULL;
29
+ return Qnil;
30
+ }
31
+
32
+ static VALUE
33
+ seccomp_ext_notify_receive_call(VALUE pointer)
34
+ {
35
+ struct seccomp_receive_args *args = (struct seccomp_receive_args *)pointer;
36
+ VALUE result;
37
+ VALUE values;
38
+ uint64_t request_args[6];
39
+ int index;
40
+
41
+ do {
42
+ rb_thread_call_without_gvl(seccomp_ext_notify_receive_without_gvl,
43
+ args, RUBY_UBF_IO, NULL);
44
+ } while (args->rc == -EINTR);
45
+ if (args->rc < 0)
46
+ return rb_ary_new_from_args(2, INT2NUM(args->rc), Qnil);
47
+
48
+ result = rb_hash_new();
49
+ rb_hash_aset(result, ID2SYM(rb_intern("id")), ULL2NUM(args->request->id));
50
+ rb_hash_aset(result, ID2SYM(rb_intern("pid")), UINT2NUM(args->request->pid));
51
+ rb_hash_aset(result, ID2SYM(rb_intern("flags")), UINT2NUM(args->request->flags));
52
+ rb_hash_aset(result, ID2SYM(rb_intern("syscall")), INT2NUM(args->request->data.nr));
53
+ rb_hash_aset(result, ID2SYM(rb_intern("arch")),
54
+ seccomp_ext_uint32(args->request->data.arch));
55
+ rb_hash_aset(result, ID2SYM(rb_intern("instruction_pointer")),
56
+ ULL2NUM(args->request->data.instruction_pointer));
57
+ memcpy(request_args, args->request->data.args, sizeof(request_args));
58
+ values = rb_ary_new_capa(6);
59
+ for (index = 0; index < 6; index++)
60
+ rb_ary_push(values, ULL2NUM(request_args[index]));
61
+ rb_hash_aset(result, ID2SYM(rb_intern("args")), values);
62
+ return rb_ary_new_from_args(2, INT2NUM(0), result);
63
+ }
64
+
65
+ static VALUE
66
+ seccomp_ext_notify_fd(VALUE self, VALUE object)
67
+ {
68
+ seccomp_filter_t *filter = seccomp_ext_filter_check(object);
69
+
70
+ (void)self;
71
+ return INT2NUM(seccomp_notify_fd(filter->ctx));
72
+ }
73
+
74
+ static VALUE
75
+ seccomp_ext_notify_receive(VALUE self, VALUE fd_value)
76
+ {
77
+ struct seccomp_receive_args args = {
78
+ .fd = seccomp_ext_num_to_int(fd_value, "fd"),
79
+ .request = NULL,
80
+ .rc = 0,
81
+ };
82
+ int rc;
83
+
84
+ (void)self;
85
+ rc = seccomp_notify_alloc(&args.request, NULL);
86
+ if (rc < 0)
87
+ return rb_ary_new_from_args(2, INT2NUM(rc), Qnil);
88
+ return rb_ensure(seccomp_ext_notify_receive_call, (VALUE)&args,
89
+ seccomp_ext_notify_receive_free, (VALUE)&args);
90
+ }
91
+
92
+ static VALUE
93
+ seccomp_ext_notify_respond(VALUE self, VALUE fd_value, VALUE id, VALUE value,
94
+ VALUE error, VALUE flags)
95
+ {
96
+ struct seccomp_notif_resp *response = NULL;
97
+ uint64_t response_id;
98
+ int64_t response_value;
99
+ int response_error;
100
+ uint32_t response_flags;
101
+ int fd;
102
+ int rc;
103
+
104
+ (void)self;
105
+ response_id = seccomp_ext_num_to_uint64(id, "id");
106
+ if (!RB_INTEGER_TYPE_P(value))
107
+ rb_raise(rb_eTypeError, "value must be an Integer");
108
+ response_value = NUM2LL(value);
109
+ response_error = seccomp_ext_num_to_int(error, "error");
110
+ response_flags = seccomp_ext_num_to_uint32(flags, "flags");
111
+ fd = seccomp_ext_num_to_int(fd_value, "fd");
112
+ rc = seccomp_notify_alloc(NULL, &response);
113
+ if (rc < 0)
114
+ return INT2NUM(rc);
115
+ response->id = response_id;
116
+ response->val = response_value;
117
+ response->error = response_error;
118
+ response->flags = response_flags;
119
+ rc = seccomp_notify_respond(fd, response);
120
+ seccomp_notify_free(NULL, response);
121
+ return INT2NUM(rc);
122
+ }
123
+
124
+ static VALUE
125
+ seccomp_ext_notify_id_valid(VALUE self, VALUE fd_value, VALUE id)
126
+ {
127
+ (void)self;
128
+ return INT2NUM(seccomp_notify_id_valid(seccomp_ext_num_to_int(fd_value, "fd"),
129
+ seccomp_ext_num_to_uint64(id, "id")));
130
+ }
131
+
132
+ static VALUE
133
+ seccomp_ext_notify_addfd(VALUE self, VALUE fd_value, VALUE id, VALUE flags,
134
+ VALUE source_fd, VALUE new_fd, VALUE new_fd_flags)
135
+ {
136
+ #if defined(HAVE_SECCOMP_NOTIFY_ADDFD) || defined(HAVE_SECCOMP_IOCTL_NOTIF_ADDFD)
137
+ uint32_t target_flags = seccomp_ext_num_to_uint32(new_fd_flags, "new fd flags");
138
+ struct seccomp_notif_addfd addfd = {
139
+ .id = seccomp_ext_num_to_uint64(id, "id"),
140
+ .flags = seccomp_ext_num_to_uint32(flags, "flags"),
141
+ .srcfd = seccomp_ext_num_to_uint32(source_fd, "source fd"),
142
+ .newfd = seccomp_ext_num_to_uint32(new_fd, "new fd"),
143
+ #ifdef HAVE_STRUCT_SECCOMP_NOTIF_ADDFD_NEWFD_FLAGS
144
+ .newfd_flags = target_flags,
145
+ #endif
146
+ };
147
+ int fd = seccomp_ext_num_to_int(fd_value, "fd");
148
+
149
+ (void)self;
150
+ #ifndef HAVE_STRUCT_SECCOMP_NOTIF_ADDFD_NEWFD_FLAGS
151
+ if (target_flags != 0)
152
+ rb_raise(rb_path2class("Seccomp::NotSupportedError"),
153
+ "new fd flags are unavailable with these headers");
154
+ #endif
155
+ #ifdef HAVE_SECCOMP_NOTIFY_ADDFD
156
+ return INT2NUM(seccomp_notify_addfd(fd, &addfd));
157
+ #else
158
+ {
159
+ int rc = ioctl(fd, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd);
160
+
161
+ return INT2NUM(rc < 0 ? -errno : rc);
162
+ }
163
+ #endif
164
+ #else
165
+ (void)self;
166
+ (void)fd_value;
167
+ (void)id;
168
+ (void)flags;
169
+ (void)source_fd;
170
+ (void)new_fd;
171
+ (void)new_fd_flags;
172
+ rb_raise(rb_path2class("Seccomp::NotSupportedError"), "seccomp_notify_addfd unavailable");
173
+ #endif
174
+ }
175
+
176
+ void
177
+ seccomp_ext_init_notify(void)
178
+ {
179
+ rb_define_singleton_method(mSeccompLowLevel, "notify_fd", seccomp_ext_notify_fd, 1);
180
+ rb_define_singleton_method(mSeccompLowLevel, "notify_receive", seccomp_ext_notify_receive, 1);
181
+ rb_define_singleton_method(mSeccompLowLevel, "notify_respond", seccomp_ext_notify_respond, 5);
182
+ rb_define_singleton_method(mSeccompLowLevel, "notify_id_valid", seccomp_ext_notify_id_valid, 2);
183
+ rb_define_singleton_method(mSeccompLowLevel, "notify_addfd", seccomp_ext_notify_addfd, 6);
184
+ }
@@ -0,0 +1,20 @@
1
+ #include "seccomp_ext.h"
2
+
3
+ VALUE mSeccomp;
4
+ VALUE mSeccompLowLevel;
5
+ VALUE cSeccompContext;
6
+ VALUE eSeccompClosedFilterError;
7
+
8
+ void
9
+ Init_seccomp_ext(void)
10
+ {
11
+ mSeccomp = rb_define_module("Seccomp");
12
+ mSeccompLowLevel = rb_define_module_under(mSeccomp, "LowLevel");
13
+ eSeccompClosedFilterError = rb_path2class("Seccomp::ClosedFilterError");
14
+
15
+ seccomp_ext_init_util();
16
+ seccomp_ext_init_constants();
17
+ seccomp_ext_init_filter();
18
+ seccomp_ext_init_syscall();
19
+ seccomp_ext_init_notify();
20
+ }
@@ -0,0 +1,35 @@
1
+ #ifndef SECCOMP_EXT_H
2
+ #define SECCOMP_EXT_H
3
+
4
+ #include "ruby.h"
5
+ #include <seccomp.h>
6
+
7
+ typedef struct {
8
+ scmp_filter_ctx ctx;
9
+ int loaded;
10
+ int notify_fd;
11
+ int in_transaction;
12
+ int has_notification;
13
+ int notification_used;
14
+ int transaction_has_notification;
15
+ } seccomp_filter_t;
16
+
17
+ extern VALUE mSeccomp;
18
+ extern VALUE mSeccompLowLevel;
19
+ extern VALUE cSeccompContext;
20
+ extern VALUE eSeccompClosedFilterError;
21
+ extern const rb_data_type_t seccomp_filter_type;
22
+
23
+ seccomp_filter_t *seccomp_ext_filter_check(VALUE object);
24
+ VALUE seccomp_ext_uint32(uint32_t value);
25
+ uint32_t seccomp_ext_num_to_uint32(VALUE value, const char *name);
26
+ int seccomp_ext_num_to_int(VALUE value, const char *name);
27
+ uint64_t seccomp_ext_num_to_uint64(VALUE value, const char *name);
28
+
29
+ void seccomp_ext_init_constants(void);
30
+ void seccomp_ext_init_filter(void);
31
+ void seccomp_ext_init_syscall(void);
32
+ void seccomp_ext_init_notify(void);
33
+ void seccomp_ext_init_util(void);
34
+
35
+ #endif
@@ -0,0 +1,86 @@
1
+ #include "seccomp_ext.h"
2
+ #include <stdlib.h>
3
+
4
+ static VALUE
5
+ seccomp_ext_syscall_name_to_string(VALUE pointer)
6
+ {
7
+ return rb_str_new_cstr((char *)pointer);
8
+ }
9
+
10
+ static VALUE
11
+ seccomp_ext_syscall_name_free(VALUE pointer)
12
+ {
13
+ free((char *)pointer);
14
+ return Qnil;
15
+ }
16
+
17
+ static VALUE
18
+ seccomp_ext_arch_resolve_name(VALUE self, VALUE name)
19
+ {
20
+ (void)self;
21
+ Check_Type(name, T_STRING);
22
+ return seccomp_ext_uint32(seccomp_arch_resolve_name(StringValueCStr(name)));
23
+ }
24
+
25
+ static VALUE
26
+ seccomp_ext_arch_native(VALUE self)
27
+ {
28
+ (void)self;
29
+ return seccomp_ext_uint32(seccomp_arch_native());
30
+ }
31
+
32
+ static VALUE
33
+ seccomp_ext_syscall_resolve_name(VALUE self, VALUE name)
34
+ {
35
+ (void)self;
36
+ Check_Type(name, T_STRING);
37
+ return INT2NUM(seccomp_syscall_resolve_name(StringValueCStr(name)));
38
+ }
39
+
40
+ static VALUE
41
+ seccomp_ext_syscall_resolve_name_arch(VALUE self, VALUE arch, VALUE name)
42
+ {
43
+ (void)self;
44
+ Check_Type(name, T_STRING);
45
+ return INT2NUM(seccomp_syscall_resolve_name_arch(
46
+ seccomp_ext_num_to_uint32(arch, "arch"), StringValueCStr(name)));
47
+ }
48
+
49
+ static VALUE
50
+ seccomp_ext_syscall_resolve_name_rewrite(VALUE self, VALUE arch, VALUE name)
51
+ {
52
+ (void)self;
53
+ Check_Type(name, T_STRING);
54
+ return INT2NUM(seccomp_syscall_resolve_name_rewrite(
55
+ seccomp_ext_num_to_uint32(arch, "arch"), StringValueCStr(name)));
56
+ }
57
+
58
+ static VALUE
59
+ seccomp_ext_syscall_resolve_num_arch(VALUE self, VALUE arch, VALUE number)
60
+ {
61
+ char *name;
62
+
63
+ (void)self;
64
+ name = seccomp_syscall_resolve_num_arch(seccomp_ext_num_to_uint32(arch, "arch"),
65
+ seccomp_ext_num_to_int(number, "number"));
66
+ if (name == NULL)
67
+ return Qnil;
68
+ return rb_ensure(seccomp_ext_syscall_name_to_string, (VALUE)name,
69
+ seccomp_ext_syscall_name_free, (VALUE)name);
70
+ }
71
+
72
+ void
73
+ seccomp_ext_init_syscall(void)
74
+ {
75
+ rb_define_singleton_method(mSeccompLowLevel, "arch_resolve_name",
76
+ seccomp_ext_arch_resolve_name, 1);
77
+ rb_define_singleton_method(mSeccompLowLevel, "arch_native", seccomp_ext_arch_native, 0);
78
+ rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name",
79
+ seccomp_ext_syscall_resolve_name, 1);
80
+ rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name_arch",
81
+ seccomp_ext_syscall_resolve_name_arch, 2);
82
+ rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_name_rewrite",
83
+ seccomp_ext_syscall_resolve_name_rewrite, 2);
84
+ rb_define_singleton_method(mSeccompLowLevel, "syscall_resolve_num_arch",
85
+ seccomp_ext_syscall_resolve_num_arch, 2);
86
+ }
@@ -0,0 +1,110 @@
1
+ #include "seccomp_ext.h"
2
+ #include <limits.h>
3
+
4
+ VALUE
5
+ seccomp_ext_uint32(uint32_t value)
6
+ {
7
+ return ULONG2NUM(value);
8
+ }
9
+
10
+ uint32_t
11
+ seccomp_ext_num_to_uint32(VALUE value, const char *name)
12
+ {
13
+ unsigned long long number;
14
+
15
+ if (!RB_INTEGER_TYPE_P(value))
16
+ rb_raise(rb_eTypeError, "%s must be an Integer", name);
17
+ number = NUM2ULL(value);
18
+ if (number > UINT32_MAX)
19
+ rb_raise(rb_eRangeError, "%s must fit in uint32", name);
20
+ return (uint32_t)number;
21
+ }
22
+
23
+ int
24
+ seccomp_ext_num_to_int(VALUE value, const char *name)
25
+ {
26
+ long long number;
27
+
28
+ if (!RB_INTEGER_TYPE_P(value))
29
+ rb_raise(rb_eTypeError, "%s must be an Integer", name);
30
+ number = NUM2LL(value);
31
+ if (number < INT_MIN || number > INT_MAX)
32
+ rb_raise(rb_eRangeError, "%s must fit in int", name);
33
+ return (int)number;
34
+ }
35
+
36
+ uint64_t
37
+ seccomp_ext_num_to_uint64(VALUE value, const char *name)
38
+ {
39
+ if (!RB_INTEGER_TYPE_P(value))
40
+ rb_raise(rb_eTypeError, "%s must be an Integer", name);
41
+ if (RTEST(rb_funcall(value, rb_intern("<"), 1, INT2FIX(0))))
42
+ rb_raise(rb_eRangeError, "%s must not be negative", name);
43
+ return NUM2ULL(value);
44
+ }
45
+
46
+ static VALUE
47
+ seccomp_ext_version(VALUE self)
48
+ {
49
+ const struct scmp_version *version = seccomp_version();
50
+
51
+ (void)self;
52
+ return rb_ary_new_from_args(3, UINT2NUM(version->major),
53
+ UINT2NUM(version->minor), UINT2NUM(version->micro));
54
+ }
55
+
56
+ static VALUE
57
+ seccomp_ext_api_get(VALUE self)
58
+ {
59
+ (void)self;
60
+ return UINT2NUM(seccomp_api_get());
61
+ }
62
+
63
+ static VALUE
64
+ seccomp_ext_api_set(VALUE self, VALUE level)
65
+ {
66
+ (void)self;
67
+ return INT2NUM(seccomp_api_set(seccomp_ext_num_to_uint32(level, "level")));
68
+ }
69
+
70
+ static VALUE
71
+ seccomp_ext_supports(VALUE self, VALUE feature)
72
+ {
73
+ ID id;
74
+
75
+ (void)self;
76
+ if (!SYMBOL_P(feature))
77
+ rb_raise(rb_eTypeError, "feature must be a Symbol");
78
+ id = SYM2ID(feature);
79
+ #ifdef HAVE_SECCOMP_PRECOMPUTE
80
+ if (id == rb_intern("precompute")) return Qtrue;
81
+ #endif
82
+ #if defined(HAVE_SECCOMP_NOTIFY_ALLOC)
83
+ if (id == rb_intern("notify")) return Qtrue;
84
+ #endif
85
+ #if defined(HAVE_SECCOMP_NOTIFY_ADDFD) || defined(HAVE_SECCOMP_IOCTL_NOTIF_ADDFD)
86
+ if (id == rb_intern("notify_addfd")) return Qtrue;
87
+ #endif
88
+ #if defined(HAVE_SECCOMP_TRANSACTION_START) && defined(HAVE_SECCOMP_TRANSACTION_COMMIT) && defined(HAVE_SECCOMP_TRANSACTION_REJECT)
89
+ if (id == rb_intern("transaction")) return Qtrue;
90
+ #endif
91
+ #ifdef HAVE_SECCOMP_EXPORT_BPF_MEM
92
+ if (id == rb_intern("bpf_mem")) return Qtrue;
93
+ #endif
94
+ #ifdef HAVE_SCMP_ACT_TRAPX
95
+ if (id == rb_intern("trapx")) return Qtrue;
96
+ #endif
97
+ #ifdef HAVE_CONST_SCMP_FLTATR_CTL_WAITKILL
98
+ if (id == rb_intern("wait_killable")) return Qtrue;
99
+ #endif
100
+ return Qfalse;
101
+ }
102
+
103
+ void
104
+ seccomp_ext_init_util(void)
105
+ {
106
+ rb_define_singleton_method(mSeccompLowLevel, "version", seccomp_ext_version, 0);
107
+ rb_define_singleton_method(mSeccompLowLevel, "api_get", seccomp_ext_api_get, 0);
108
+ rb_define_singleton_method(mSeccompLowLevel, "api_set", seccomp_ext_api_set, 1);
109
+ rb_define_singleton_method(mSeccompLowLevel, "supports?", seccomp_ext_supports, 1);
110
+ }
data/lib/libseccomp.rb ADDED
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "seccomp/version"
4
+ require_relative "seccomp/errors"
5
+ require_relative "seccomp/low_level"
6
+ require "seccomp_ext"
7
+ require_relative "seccomp/action"
8
+ require_relative "seccomp/arch"
9
+ require_relative "seccomp/syscall"
10
+ require_relative "seccomp/arg"
11
+ require_relative "seccomp/attributes"
12
+ require_relative "seccomp/dsl"
13
+ require_relative "seccomp/filter"
14
+ require_relative "seccomp/notification"
15
+ require_relative "seccomp/notifier"
16
+
17
+ ## Native bindings and Ruby-friendly APIs for libseccomp.
18
+ module Seccomp
19
+ FEATURE_API_LEVELS = { notify: 5, notify_addfd: 5, wait_killable: 7 }.freeze
20
+ private_constant :FEATURE_API_LEVELS
21
+
22
+ ## Semantic version of the linked libseccomp library.
23
+ LibraryVersion = Struct.new(:major, :minor, :micro) do
24
+ # @return [String] dotted version string
25
+ # @example `Seccomp.library_version.to_s #=> "2.6.1"`
26
+ def to_s
27
+ values.join(".")
28
+ end
29
+ end
30
+
31
+ class << self
32
+ # @return [LibraryVersion] linked library version
33
+ # @example `Seccomp.library_version.major`
34
+ def library_version
35
+ LibraryVersion.new(*LowLevel.version)
36
+ end
37
+
38
+ # @return [Integer] runtime libseccomp API level
39
+ # @example `Seccomp.api_level #=> 6`
40
+ def api_level
41
+ LowLevel.api_get
42
+ end
43
+
44
+ # @param level [Integer] process-global API level
45
+ # @return [Integer] assigned level
46
+ # @raise [Error] if libseccomp rejects the level
47
+ # @example `Seccomp.api_level = 6`
48
+ def api_level=(level)
49
+ Error.check!(LowLevel.api_set(level), call: "seccomp_api_set")
50
+ level
51
+ end
52
+
53
+ # @param feature [Symbol] feature name
54
+ # @return [Boolean] whether the linked build provides it
55
+ # @example `Seccomp.supports?(:transaction)`
56
+ def supports?(feature)
57
+ required = FEATURE_API_LEVELS[feature]
58
+ LowLevel.supports?(feature) && (!required || api_level >= required)
59
+ end
60
+
61
+ # @param default [Symbol, Integer] default action
62
+ # @yield [Filter] configured filter for arity-one blocks
63
+ # @return [Filter] configured filter
64
+ # @raise [Error] if configuration fails
65
+ # @example `Seccomp.filter(default: :allow) { deny :ptrace }`
66
+ def filter(default: :kill_process, &block)
67
+ filter = Filter.new(default)
68
+ return filter unless block
69
+
70
+ block.arity.zero? ? filter.instance_eval(&block) : block.call(filter)
71
+ completed = true
72
+ filter
73
+ ensure
74
+ filter&.close if block && !completed
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seccomp
4
+ # Seccomp action constants and conversions.
5
+ module Action
6
+ # Mask for action payload data.
7
+ # @api private
8
+ DATA_MASK = 0xFFFF
9
+ # Base value for errno actions.
10
+ # @api private
11
+ ERRNO = 0x0005_0000
12
+ # Base value for trace actions.
13
+ # @api private
14
+ TRACE = 0x7FF0_0000
15
+ # Mask selecting an action without its payload.
16
+ # @api private
17
+ ACTION_MASK = 0xFFFF_0000
18
+ # Minimum runtime API level for newer actions.
19
+ # @api private
20
+ API_LEVELS = {
21
+ KILL_PROCESS => 3,
22
+ LOG => 3,
23
+ NOTIFY => 5
24
+ }.freeze
25
+
26
+ # Symbol-to-action lookup.
27
+ # @api private
28
+ NAMES = {
29
+ kill_process: KILL_PROCESS,
30
+ kill_thread: KILL_THREAD,
31
+ kill: KILL,
32
+ trap: TRAP,
33
+ notify: NOTIFY,
34
+ log: LOG,
35
+ allow: ALLOW
36
+ }.freeze
37
+
38
+ module_function
39
+
40
+ # @param value [Integer, SystemCallError, Class] positive or negative errno
41
+ # @return [Integer] SCMP_ACT_ERRNO action
42
+ # @raise [ArgumentError] for an invalid value
43
+ # @example `Action.errno(Errno::EPERM)`
44
+ def errno(value)
45
+ number = errno_number(value)
46
+ raise ArgumentError, "errno must not be zero" if number.zero?
47
+
48
+ ERRNO | data(number.abs)
49
+ end
50
+
51
+ # @param value [Integer] 16-bit trace payload
52
+ # @return [Integer] SCMP_ACT_TRACE action
53
+ # @raise [ArgumentError] outside 0..65535
54
+ # @example `Action.trace(42)`
55
+ def trace(value)
56
+ TRACE | data(value)
57
+ end
58
+
59
+ # @param value [Integer] optional 16-bit trap payload
60
+ # @return [Integer] SCMP_ACT_TRAP action
61
+ # @raise [NotSupportedError] when parameterized traps are unavailable
62
+ # @example `Action.trap(7)`
63
+ def trap(value = 0)
64
+ value = data(value)
65
+ return TRAP if value.zero?
66
+ raise NotSupportedError, "SCMP_ACT_TRAPX is not available" unless Seccomp.supports?(:trapx)
67
+
68
+ TRAP | value
69
+ end
70
+
71
+ # @param value [Symbol, Integer, Array] action representation
72
+ # @return [Integer] action constant
73
+ # @raise [ArgumentError] for an unknown action
74
+ # @raise [NotSupportedError] when the runtime API level is too low
75
+ # @example `Action.resolve([:errno, Errno::EPERM])`
76
+ def resolve(value)
77
+ action = if value.is_a?(Integer)
78
+ value
79
+ elsif value.is_a?(Array)
80
+ resolve_tuple(value)
81
+ else
82
+ unless value.respond_to?(:to_sym)
83
+ raise ArgumentError, "unknown action: #{value.inspect}"
84
+ end
85
+
86
+ NAMES.fetch(value.to_sym) do
87
+ raise ArgumentError, "unknown action: #{value.inspect}"
88
+ end
89
+ end
90
+ ensure_supported!(action)
91
+ action
92
+ end
93
+
94
+ def ensure_supported!(action)
95
+ if (action & ACTION_MASK) == TRAP && action.anybits?(DATA_MASK) && !Seccomp.supports?(:trapx)
96
+ raise NotSupportedError, "SCMP_ACT_TRAPX is not available"
97
+ end
98
+
99
+ required = API_LEVELS.fetch(action & ACTION_MASK, 1)
100
+ return if required == 1 || Seccomp.api_level >= required
101
+
102
+ raise NotSupportedError, "action requires libseccomp API level #{required}"
103
+ end
104
+ private_class_method :ensure_supported!
105
+
106
+ # @param value [Integer] action value
107
+ # @return [String] debug representation
108
+ # @example `Action.describe(Action.errno(1))`
109
+ def describe(value)
110
+ return "SCMP_ACT_ERRNO(#{value & DATA_MASK})" if (value & 0xFFFF_0000) == ERRNO
111
+ return "SCMP_ACT_TRACE(#{value & DATA_MASK})" if (value & 0xFFFF_0000) == TRACE
112
+
113
+ name = NAMES.key(value)
114
+ name ? "SCMP_ACT_#{name.to_s.upcase}" : format("0x%08x", value)
115
+ end
116
+
117
+ def errno_number(value)
118
+ number = if value.is_a?(Class) && value < SystemCallError
119
+ value.const_get(:Errno, false) if value.const_defined?(:Errno, false)
120
+ elsif value.is_a?(SystemCallError)
121
+ value.errno
122
+ else
123
+ value
124
+ end
125
+ unless number.is_a?(Integer)
126
+ raise ArgumentError, "errno must be an Integer or system error with an errno"
127
+ end
128
+
129
+ number
130
+ end
131
+ private_class_method :errno_number
132
+
133
+ def data(value)
134
+ unless value.is_a?(Integer) && (0..DATA_MASK).cover?(value)
135
+ raise ArgumentError, "action data must be between 0 and 65535"
136
+ end
137
+
138
+ value
139
+ end
140
+ private_class_method :data
141
+
142
+ def resolve_tuple(tuple)
143
+ raise ArgumentError, "unknown action: #{tuple.inspect}" unless tuple.length == 2
144
+
145
+ type, value = tuple
146
+ return errno(value) if type == :errno
147
+ return trace(value) if type == :trace
148
+ return trap(value) if type == :trap
149
+
150
+ raise ArgumentError, "unknown action: #{tuple.inspect}"
151
+ end
152
+ private_class_method :resolve_tuple
153
+ end
154
+ end