exec_sandbox 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.
- data/.document +5 -0
- data/.project +18 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +42 -0
- data/VERSION +1 -0
- data/lib/exec_sandbox/sandbox.rb +169 -0
- data/lib/exec_sandbox/spawn.rb +143 -0
- data/lib/exec_sandbox/users.rb +156 -0
- data/lib/exec_sandbox/wait4.rb +85 -0
- data/lib/exec_sandbox.rb +23 -0
- data/spec/exec_sandbox/sandbox_spec.rb +138 -0
- data/spec/exec_sandbox/spawn_spec.rb +327 -0
- data/spec/exec_sandbox/users_spec.rb +125 -0
- data/spec/exec_sandbox/wait4_spec.rb +24 -0
- data/spec/fixtures/buffer.rb +10 -0
- data/spec/fixtures/churn.rb +16 -0
- data/spec/fixtures/duplicate.rb +7 -0
- data/spec/fixtures/exit_arg.rb +5 -0
- data/spec/fixtures/fork.rb +18 -0
- data/spec/fixtures/pwd.rb +8 -0
- data/spec/fixtures/write_arg.rb +8 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/code_fixture.rb +7 -0
- metadata +165 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
# namespace
|
2
|
+
module ExecSandbox
|
3
|
+
|
4
|
+
# Interface to the wait4 system call using the ffi library.
|
5
|
+
module Wait4
|
6
|
+
# Waits for a process to end, and collects its exit status and resource usage.
|
7
|
+
#
|
8
|
+
# @param [Fixnum] pid the PID of the process to wait for; should be a child of
|
9
|
+
# this process
|
10
|
+
# @return [Hash] exit code and resource usage information
|
11
|
+
def self.wait4(pid)
|
12
|
+
status_ptr = FFI::MemoryPointer.new :int
|
13
|
+
rusage = ExecSandbox::Wait4::Rusage.new
|
14
|
+
returned_pid = LibC.wait4(pid, status_ptr, 0, rusage.pointer)
|
15
|
+
raise SystemCallError, FFI.errno if returned_pid < 0
|
16
|
+
status = { :bits => status_ptr.read_int }
|
17
|
+
status_ptr.free
|
18
|
+
|
19
|
+
signal_code = status[:bits] & 0x7f
|
20
|
+
status[:exit_code] = (signal_code != 0) ? -signal_code : status[:bits] >> 8
|
21
|
+
status[:user_time] = rusage[:ru_utime_sec] +
|
22
|
+
rusage[:ru_utime_usec] * 0.000_001
|
23
|
+
status[:system_time] = rusage[:ru_utime_sec] +
|
24
|
+
rusage[:ru_utime_usec] * 0.000_001
|
25
|
+
status[:rss] = rusage[:ru_maxrss] / 1024.0
|
26
|
+
return status
|
27
|
+
end
|
28
|
+
|
29
|
+
# Maps wait4 in libc.
|
30
|
+
module LibC
|
31
|
+
extend FFI::Library
|
32
|
+
ffi_lib FFI::Library::LIBC
|
33
|
+
attach_function :wait4, [:int, :pointer, :int, :pointer], :int,
|
34
|
+
:blocking => true
|
35
|
+
end # module ExecSandbox::Wait4::Libc
|
36
|
+
|
37
|
+
# Maps struct rusage in sys/resource.h, used by wait4.
|
38
|
+
class Rusage < FFI::Struct
|
39
|
+
# Total amount of user time used.
|
40
|
+
layout :ru_utime_sec, :time_t,
|
41
|
+
:ru_utime_usec, :suseconds_t,
|
42
|
+
# Total amount of system time used.
|
43
|
+
:ru_stime_sec, :time_t,
|
44
|
+
:ru_stime_usec, :suseconds_t,
|
45
|
+
# Maximum resident set size (in kilobytes).
|
46
|
+
:ru_maxrss, :long,
|
47
|
+
# Amount of sharing of text segment memory with other processes
|
48
|
+
# (kilobyte-seconds).
|
49
|
+
:ru_ixrss, :long,
|
50
|
+
# Amount of data segment memory used (kilobyte-seconds).
|
51
|
+
:ru_idrss, :long,
|
52
|
+
# Amount of stack memory used (kilobyte-seconds).
|
53
|
+
:ru_isrss, :long,
|
54
|
+
# Number of soft page faults (i.e. those serviced by reclaiming a page from
|
55
|
+
# the list of pages awaiting reallocation.
|
56
|
+
:ru_minflt, :long,
|
57
|
+
# Number of hard page faults (i.e. those that required I/O).
|
58
|
+
:ru_majflt, :long,
|
59
|
+
# Number of times a process was swapped out of physical memory.
|
60
|
+
:ru_nswap, :long,
|
61
|
+
# Number of input operations via the file system. Note: This and
|
62
|
+
# `ru_oublock' do not include operations with the cache.
|
63
|
+
:ru_inblock, :long,
|
64
|
+
# Number of output operations via the file system.
|
65
|
+
:ru_oublock, :long,
|
66
|
+
# Number of IPC messages sent.
|
67
|
+
:ru_msgsnd, :long,
|
68
|
+
# Number of IPC messages received.
|
69
|
+
:ru_msgrcv, :long,
|
70
|
+
# Number of signals delivered.
|
71
|
+
:ru_nsignals, :long,
|
72
|
+
# Number of voluntary context switches, i.e. because the process gave up the
|
73
|
+
# process before it had to (usually to wait for some resource to be
|
74
|
+
# available).
|
75
|
+
:ru_nvcsw, :long,
|
76
|
+
# Number of involuntary context switches, i.e. a higher priority process
|
77
|
+
# became runnable or the current process used up its time slice.
|
78
|
+
:ru_nivcsw, :long,
|
79
|
+
# Padding, so we don't crash if the struct gets ammended on newer OSes.
|
80
|
+
:padding, :uchar, 256
|
81
|
+
end # struct ExecSandbox::Wait4::Rusage
|
82
|
+
|
83
|
+
end # module ExecSandbox::Wait4
|
84
|
+
|
85
|
+
end # namespace ExecSandbox
|
data/lib/exec_sandbox.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# @title ExecSandbox - Run foreign binaries using POSIX sandboxing features
|
2
|
+
# @author Victor Costan
|
3
|
+
|
4
|
+
# Standard library
|
5
|
+
require 'English'
|
6
|
+
require 'etc'
|
7
|
+
require 'fcntl'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'tempfile'
|
10
|
+
require 'tmpdir'
|
11
|
+
|
12
|
+
# Gems
|
13
|
+
require 'ffi'
|
14
|
+
|
15
|
+
# TODO(pwnall): documentation
|
16
|
+
module ExecSandbox
|
17
|
+
end # namespace ExecSandbox
|
18
|
+
|
19
|
+
# Code
|
20
|
+
require 'exec_sandbox/sandbox.rb'
|
21
|
+
require 'exec_sandbox/spawn.rb'
|
22
|
+
require 'exec_sandbox/users.rb'
|
23
|
+
require 'exec_sandbox/wait4.rb'
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ExecSandbox::Sandbox do
|
4
|
+
describe 'IO redirection' do
|
5
|
+
before do
|
6
|
+
@temp_in = Tempfile.new 'exec_sandbox_rspec'
|
7
|
+
@temp_in.write "I/O test\n"
|
8
|
+
@temp_in.close
|
9
|
+
@temp_out = Tempfile.new 'exec_sandbox_rspec'
|
10
|
+
@temp_out.close
|
11
|
+
|
12
|
+
ExecSandbox.use do |s|
|
13
|
+
@result = s.run bin_fixture(:duplicate), :in => @temp_in.path,
|
14
|
+
:out => @temp_out.path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
after do
|
18
|
+
@temp_in.unlink
|
19
|
+
@temp_out.unlink
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should not crash' do
|
23
|
+
@result[:exit_code].should == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should produce the correct result' do
|
27
|
+
File.read(@temp_out.path).should == "I/O test\nI/O test\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'pipe redirection' do
|
32
|
+
before do
|
33
|
+
ExecSandbox.use do |s|
|
34
|
+
@result = s.run bin_fixture(:duplicate), :in_data => "Pipe test\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should not crash' do
|
39
|
+
@result[:exit_code].should == 0
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should produce the correct result' do
|
43
|
+
@result[:out_data].should == "Pipe test\nPipe test\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
describe 'resource limitations' do
|
49
|
+
describe 'churn.rb' do
|
50
|
+
before do
|
51
|
+
@temp_out = Tempfile.new 'exec_sandbox_rspec'
|
52
|
+
@temp_out.close
|
53
|
+
end
|
54
|
+
after do
|
55
|
+
@temp_out.unlink
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'without limitations' do
|
59
|
+
before do
|
60
|
+
ExecSandbox.use do |s|
|
61
|
+
@result = s.run [bin_fixture(:churn), 'stdout', 3.to_s]
|
62
|
+
s.pull 'stdout', @temp_out.path
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should not crash' do
|
67
|
+
@result[:exit_code].should == 0
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should run for at least 2 seconds' do
|
71
|
+
(@result[:user_time] + @result[:system_time]).should > 2
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should output something' do
|
75
|
+
File.stat(@temp_out.path).size.should > 0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'with CPU time limitation' do
|
80
|
+
before do
|
81
|
+
ExecSandbox.use do |s|
|
82
|
+
@result = s.run [bin_fixture(:churn), 'stdout', 3.to_s],
|
83
|
+
:limits => {:cpu => 1}
|
84
|
+
s.pull 'stdout', @temp_out.path
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should run for at least 0.5 seconds' do
|
89
|
+
(@result[:user_time] + @result[:system_time]).should >= 0.5
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should run for less than 2 seconds' do
|
93
|
+
(@result[:user_time] + @result[:system_time]).should < 2
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should not have a chance to output' do
|
97
|
+
File.stat(@temp_out.path).size.should == 0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#push' do
|
104
|
+
let(:test_user) { Etc.getlogin }
|
105
|
+
let(:test_uid) { Etc.getpwnam(test_user).uid }
|
106
|
+
let(:test_gid) { Etc.getpwnam(test_user).gid }
|
107
|
+
let(:test_group) { Etc.getgrgid(test_gid).name }
|
108
|
+
|
109
|
+
before do
|
110
|
+
@sandbox = ExecSandbox.open test_user
|
111
|
+
end
|
112
|
+
after do
|
113
|
+
@sandbox.close
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'a file' do
|
117
|
+
before do
|
118
|
+
@to = @sandbox.push __FILE__
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should copy straight to the sandbox directory' do
|
122
|
+
File.dirname(@to).should == @sandbox.path
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should use the same file name' do
|
126
|
+
File.basename(@to).should == 'sandbox_spec.rb'
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should set the file's owner to the admin" do
|
130
|
+
File.stat(@to).uid.should == test_uid
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should not set the file's group to the admin" do
|
134
|
+
File.stat(@to).gid.should_not == test_gid
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ExecSandbox::Spawn do
|
4
|
+
let(:test_user) { Etc.getlogin }
|
5
|
+
let(:test_uid) { Etc.getpwnam(test_user).uid }
|
6
|
+
let(:test_gid) { Etc.getpwnam(test_user).gid }
|
7
|
+
let(:test_group) { Etc.getgrgid(test_gid).name }
|
8
|
+
|
9
|
+
describe '#spawn IO redirection' do
|
10
|
+
before do
|
11
|
+
@temp_in = Tempfile.new 'exec_sandbox_rspec'
|
12
|
+
@temp_in.write "Spawn IO test\n"
|
13
|
+
@temp_in.close
|
14
|
+
@temp_out = Tempfile.new 'exec_sandbox_rspec'
|
15
|
+
@temp_out.close
|
16
|
+
end
|
17
|
+
after do
|
18
|
+
@temp_in.unlink
|
19
|
+
@temp_out.unlink
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_examples_for 'duplicate.rb' do
|
23
|
+
it 'should not crash' do
|
24
|
+
@status[:exit_code].should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should write successfully' do
|
28
|
+
@temp_out.open
|
29
|
+
begin
|
30
|
+
@temp_out.read.should == "Spawn IO test\nSpawn IO test\n"
|
31
|
+
ensure
|
32
|
+
@temp_out.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'with paths' do
|
38
|
+
before do
|
39
|
+
pid = ExecSandbox::Spawn.spawn bin_fixture(:duplicate),
|
40
|
+
{:in => @temp_in.path, :out => @temp_out.path}
|
41
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
42
|
+
end
|
43
|
+
|
44
|
+
it_behaves_like 'duplicate.rb'
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'with file descriptors' do
|
48
|
+
before do
|
49
|
+
File.open(@temp_in.path, 'r') do |in_io|
|
50
|
+
File.open(@temp_out.path, 'w') do |out_io|
|
51
|
+
pid = ExecSandbox::Spawn.spawn bin_fixture(:duplicate),
|
52
|
+
{:in => in_io, :out => out_io, :err => STDERR}
|
53
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it_behaves_like 'duplicate.rb'
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'without stdout' do
|
62
|
+
before do
|
63
|
+
pid = ExecSandbox::Spawn.spawn bin_fixture(:duplicate),
|
64
|
+
{:in => @temp_in.path}
|
65
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should crash' do
|
69
|
+
@status[:exit_code].should_not == 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#spawn principal' do
|
75
|
+
before do
|
76
|
+
@temp = Tempfile.new 'exec_sandbox_rspec'
|
77
|
+
@temp_path = @temp.path
|
78
|
+
@temp.close
|
79
|
+
end
|
80
|
+
after do
|
81
|
+
File.unlink(@temp_path) if File.exist?(@temp_path)
|
82
|
+
end
|
83
|
+
|
84
|
+
describe 'with root credentials' do
|
85
|
+
before do
|
86
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:write_arg),
|
87
|
+
@temp_path, "Spawn uid test\n"], {:err => STDERR},
|
88
|
+
{:uid => 0, :gid => 0}
|
89
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
90
|
+
@fstat = File.stat(@temp_path)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should not crash' do
|
94
|
+
@status[:exit_code].should == 0
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should have the UID set to root' do
|
98
|
+
@fstat.uid.should == 0
|
99
|
+
end
|
100
|
+
it 'should have the GID set to root' do
|
101
|
+
@fstat.gid.should == 0
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should have the correct output' do
|
105
|
+
File.read(@temp_path).should == "Spawn uid test\n"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'with non-root credentials' do
|
110
|
+
before do
|
111
|
+
@temp.unlink
|
112
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:write_arg),
|
113
|
+
@temp_path, "Spawn uid test\n"], {:err => STDERR},
|
114
|
+
{:uid => test_uid, :gid => test_gid}
|
115
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should not crash' do
|
119
|
+
@status[:exit_code].should == 0
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should have the UID set to the test user' do
|
123
|
+
File.stat(@temp_path).uid.should == test_uid
|
124
|
+
end
|
125
|
+
it 'should have the GID set to the test group' do
|
126
|
+
File.stat(@temp_path).gid.should == test_gid
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should have the correct output' do
|
130
|
+
File.read(@temp_path).should == "Spawn uid test\n"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'with non-root credentials and a root-owned redirect file' do
|
135
|
+
before do
|
136
|
+
File.chmod 0700, @temp_path
|
137
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:write_arg),
|
138
|
+
@temp_path, "Spawn uid test\n"], {},
|
139
|
+
{:uid => test_uid, :gid => test_gid}
|
140
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should crash (euid is set correctly)' do
|
144
|
+
@status[:exit_code].should_not == 0
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should not have the correct output' do
|
148
|
+
File.read(@temp_path).should_not == "Spawn uid test\n"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'with non-root credentials and a root-owned redirect file' do
|
153
|
+
before do
|
154
|
+
File.chmod 070, @temp_path
|
155
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:write_arg), @temp_path,
|
156
|
+
"Spawn uid test\n"], {},
|
157
|
+
{:uid => test_uid, :gid => test_gid}
|
158
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should crash (egid is set correctly)' do
|
162
|
+
@status[:exit_code].should_not == 0
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should not have the correct output' do
|
166
|
+
File.read(@temp_path).should_not == "Spawn uid test\n"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe 'with a working directory' do
|
171
|
+
before do
|
172
|
+
@temp_dir = Dir.mktmpdir 'exec_sandbox_rspec'
|
173
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:pwd), @temp_path],
|
174
|
+
{}, {:dir => @temp_dir}
|
175
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
176
|
+
end
|
177
|
+
after do
|
178
|
+
Dir.rmdir @temp_dir
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'should not crash' do
|
182
|
+
@status[:exit_code].should == 0
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should set the working directory' do
|
186
|
+
File.read(@temp_path).should == @temp_dir
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#spawn resource limits' do
|
192
|
+
before do
|
193
|
+
@temp = Tempfile.new 'exec_sandbox_rspec'
|
194
|
+
@temp_path = @temp.path
|
195
|
+
@temp.close
|
196
|
+
end
|
197
|
+
after do
|
198
|
+
File.unlink(@temp_path) if File.exist?(@temp_path)
|
199
|
+
end
|
200
|
+
|
201
|
+
describe 'buffer.rb' do
|
202
|
+
describe 'without limitations' do
|
203
|
+
before do
|
204
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:buffer), @temp_path,
|
205
|
+
(100 * 1024 * 1024).to_s], {:err => STDERR}, {}, {}
|
206
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should not crash' do
|
210
|
+
@status[:exit_code].should == 0
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should output 100 megs' do
|
214
|
+
File.stat(@temp_path).size.should == 100 * 1024 * 1024
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe 'with memory limitation' do
|
219
|
+
before do
|
220
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:buffer), @temp_path,
|
221
|
+
(100 * 1024 * 1024).to_s], {}, {}, {:data => 64 * 1024 * 1024}
|
222
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should crash' do
|
226
|
+
@status[:exit_code].should_not == 0
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'should not have a chance to output data' do
|
230
|
+
File.stat(@temp_path).size.should == 0
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe 'with output limitation' do
|
235
|
+
before do
|
236
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:buffer), @temp_path,
|
237
|
+
(100 * 1024 * 1024).to_s], {}, {}, {:file_size => 8 * 1024 * 1024}
|
238
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'should crash' do
|
242
|
+
@status[:exit_code].should_not == 0
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'should not output more than 16 megs' do
|
246
|
+
File.stat(@temp_path).size.should <= 16 * 1024 * 1024
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe 'fork.rb' do
|
252
|
+
describe 'without limitations' do
|
253
|
+
before do
|
254
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:fork), @temp_path,
|
255
|
+
10.to_s], {:err => STDERR}, {}, {}
|
256
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should not crash' do
|
260
|
+
@status[:exit_code].should == 0
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should output 10 +es' do
|
264
|
+
File.stat(@temp_path).size.should == 10
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe 'with sub-process limitation' do
|
269
|
+
before do
|
270
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:fork), @temp_path,
|
271
|
+
10.to_s], {}, {}, {:processes => 4}
|
272
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'should crash' do
|
276
|
+
@status[:exit_code].should_not == 0
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'should output less than 5 +es' do
|
280
|
+
File.stat(@temp_path).size.should < 5
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe 'churn.rb' do
|
286
|
+
describe 'without limitations' do
|
287
|
+
before do
|
288
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:churn), @temp_path,
|
289
|
+
3.to_s], {:err => STDERR}, {}, {}
|
290
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should not crash' do
|
294
|
+
@status[:exit_code].should == 0
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'should run for at least 2 seconds' do
|
298
|
+
(@status[:user_time] + @status[:system_time]).should > 2
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'should output something' do
|
302
|
+
File.stat(@temp_path).size.should > 0
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe 'with CPU time limitation' do
|
307
|
+
before do
|
308
|
+
pid = ExecSandbox::Spawn.spawn [bin_fixture(:churn), @temp_path,
|
309
|
+
10.to_s], {}, {}, {:cpu => 1}
|
310
|
+
@status = ExecSandbox::Wait4.wait4 pid
|
311
|
+
end
|
312
|
+
|
313
|
+
it 'should run for at least 0.5 seconds' do
|
314
|
+
(@status[:user_time] + @status[:system_time]).should >= 0.5
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'should run for less than 2 seconds' do
|
318
|
+
(@status[:user_time] + @status[:system_time]).should < 2
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should not have a chance to output' do
|
322
|
+
File.stat(@temp_path).size.should == 0
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ExecSandbox::Users do
|
4
|
+
let(:test_user) { 'exec_sandbox_rspec' }
|
5
|
+
let(:test_group) { Etc.getgrgid(Etc.getpwnam(Etc.getlogin).gid).name }
|
6
|
+
|
7
|
+
describe '#temp' do
|
8
|
+
before { @user_name = ExecSandbox::Users.temp 'exsbx.rspec' }
|
9
|
+
after { ExecSandbox::Users.destroy @user_name }
|
10
|
+
|
11
|
+
it 'should create a user whose name starts with the prefix' do
|
12
|
+
@user_name.should match(/^exsbx\.rspec/)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should create a user' do
|
16
|
+
Etc.getpwnam(@user_name).should_not be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should create a group with the same name' do
|
20
|
+
Etc.getgrgid(Etc.getpwnam(@user_name).gid).name.should == @user_name
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should create a home directory for the user' do
|
24
|
+
File.exist?(Etc.getpwnam(@user_name).dir).should be_true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#create' do
|
29
|
+
shared_examples_for 'user creation' do
|
30
|
+
it 'should return the UID of a user with the right name' do
|
31
|
+
Etc.getpwuid(@uid).name.should == test_user
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should create the new user's home directory" do
|
35
|
+
File.exist?(Etc.getpwuid(@uid).dir).should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should have the new user's name in its home directory" do
|
39
|
+
Etc.getpwuid(@uid).dir.should match(test_user)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'with no group' do
|
44
|
+
before do
|
45
|
+
@uid = ExecSandbox::Users.create test_user
|
46
|
+
end
|
47
|
+
|
48
|
+
after do
|
49
|
+
ExecSandbox::Users.destroy test_user
|
50
|
+
end
|
51
|
+
|
52
|
+
it_should_behave_like 'user creation'
|
53
|
+
|
54
|
+
it "should create a group with the user's name" do
|
55
|
+
Etc.getgrnam(test_user).should_not be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should set the new user's GID to the group" do
|
59
|
+
Etc.getpwuid(@uid).gid.should == Etc.getgrnam(test_user).gid
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'with given group' do
|
64
|
+
before do
|
65
|
+
@uid = ExecSandbox::Users.create test_user, test_group
|
66
|
+
end
|
67
|
+
|
68
|
+
after do
|
69
|
+
ExecSandbox::Users.destroy test_user
|
70
|
+
end
|
71
|
+
|
72
|
+
it_should_behave_like 'user creation'
|
73
|
+
|
74
|
+
it "should not create a group with the user's name" do
|
75
|
+
lambda {
|
76
|
+
Etc.getgrnam(test_user)
|
77
|
+
}.should raise_error(ArgumentError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should set the new user's GID to the correct group" do
|
81
|
+
Etc.getpwuid(@uid).gid.should == Etc.getgrnam(test_group).gid
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#destroy' do
|
87
|
+
describe 'with single-use group' do
|
88
|
+
before do
|
89
|
+
ExecSandbox::Users.create test_user
|
90
|
+
@home_dir = Etc.getpwnam(test_user)
|
91
|
+
ExecSandbox::Users.destroy test_user
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should remove the user' do
|
95
|
+
lambda {
|
96
|
+
Etc.getpwnam(test_user)
|
97
|
+
}.should raise_error(ArgumentError)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should remove the user's group" do
|
101
|
+
lambda {
|
102
|
+
Etc.getgrnam(test_user)
|
103
|
+
}.should raise_error(ArgumentError)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe 'delete_user with shared group' do
|
108
|
+
before do
|
109
|
+
ExecSandbox::Users.create test_user, test_group
|
110
|
+
@home_dir = Etc.getpwnam(test_user)
|
111
|
+
ExecSandbox::Users.destroy test_user
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should remove the user' do
|
115
|
+
lambda {
|
116
|
+
Etc.getpwnam(test_user)
|
117
|
+
}.should raise_error(ArgumentError)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not remove the generic group" do
|
121
|
+
Etc.getgrnam(test_group).should_not be_nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|