exec_sandbox 0.1.3 → 0.2.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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 0.2.0
data/exec_sandbox.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "exec_sandbox"
8
- s.version = "0.1.3"
8
+ s.version = "0.2.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Victor Costan"]
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
38
38
  "spec/exec_sandbox/wait4_spec.rb",
39
39
  "spec/fixtures/buffer.rb",
40
40
  "spec/fixtures/churn.rb",
41
+ "spec/fixtures/count.rb",
41
42
  "spec/fixtures/duplicate.rb",
42
43
  "spec/fixtures/exit_arg.rb",
43
44
  "spec/fixtures/fork.rb",
@@ -49,7 +50,7 @@ Gem::Specification.new do |s|
49
50
  s.homepage = "http://github.com/pwnall/exec_sandbox"
50
51
  s.licenses = ["MIT"]
51
52
  s.require_paths = ["lib"]
52
- s.rubygems_version = "1.8.11"
53
+ s.rubygems_version = "1.8.12"
53
54
  s.summary = "Run foreign binaries using POSIX sandboxing features"
54
55
 
55
56
  if s.respond_to? :specification_version then
data/lib/exec_sandbox.rb CHANGED
@@ -6,6 +6,7 @@ require 'English'
6
6
  require 'etc'
7
7
  require 'fcntl'
8
8
  require 'fileutils'
9
+ require 'set'
9
10
  require 'tempfile'
10
11
  require 'tmpdir'
11
12
 
@@ -92,8 +92,11 @@ class Sandbox
92
92
  # @option options [String] :out path to a file that is set as the child's
93
93
  # stdout; if not set, the child will receive the write end of a pipe whose
94
94
  # contents is returned in :out_data
95
+ # @option options [Symbol] :err :none closes the child's stderr, :out
96
+ # redirects the child's stderr to stdout; by default, the child's stderr
97
+ # is the same as the parent's
95
98
  # @return [Hash] the result of {Wait4#wait4}, plus an :out_data key if no :out
96
- # option is given
99
+ # option is given
97
100
  def run(command, options = {})
98
101
  limits = options[:limits] || {}
99
102
 
@@ -113,7 +116,14 @@ class Sandbox
113
116
  out_rd, out_wr = IO.pipe
114
117
  io[:out] = out_wr
115
118
  end
116
- io[:err] = STDERR unless options[:no_stderr]
119
+ case options[:err]
120
+ when :out
121
+ io[:err] = STDOUT
122
+ when :none
123
+ # Don't set io[:err], so the child's stderr will be closed.
124
+ else
125
+ io[:err] = STDERR
126
+ end
117
127
 
118
128
  pid = ExecSandbox::Spawn.spawn command, io, @principal, limits
119
129
  # Close the pipe ends that are meant to be used in the child.
@@ -28,32 +28,44 @@ module Spawn
28
28
  # @param [Hash] io associates file descriptors with IO objects or file paths;
29
29
  # all file descriptors not covered by io will be closed
30
30
  def self.limit_io(io)
31
+ # Sort the list of redirections by file descriptor number.
32
+ redirects = []
31
33
  [:in, :out, :err].each_with_index do |sym, fd_num|
32
- if target = io.delete(sym)
33
- io[fd_num] = target
34
+ if target = io[sym]
35
+ redirects << [fd_num, redirects.length, target]
34
36
  end
35
37
  end
36
38
  io.each do |k, v|
37
- if v.respond_to?(:fileno)
38
- if v.fileno != k
39
- LibC.close k
40
- LibC.dup2 v.fileno, k
39
+ if k.kind_of? Integer
40
+ redirects << [k, redirects.length, v]
41
+ end
42
+ end
43
+
44
+ # Perform the redirections.
45
+ redirects.sort!
46
+ redirects.each do |fd_num, _, target|
47
+ if target.respond_to?(:fileno)
48
+ # IO stream.
49
+ if target.fileno != fd_num
50
+ LibC.close fd_num
51
+ LibC.dup2 target.fileno, fd_num
41
52
  end
42
53
  else
43
- LibC.close k
44
- open_fd = IO.sysopen(v, 'r+')
45
- if open_fd != k
46
- LibC.dup2 open_fd, k
54
+ # Filename string.
55
+ LibC.close fd_num
56
+ open_fd = IO.sysopen(target, 'r+')
57
+ if open_fd != fd_num
58
+ LibC.dup2 open_fd, fd_num
47
59
  LibC.close open_fd
48
60
  end
49
61
  end
50
62
  end
51
63
 
52
- # Close all file descriptors.
64
+ # Close all file descriptors not in the redirection table.
65
+ redirected_fds = Set.new redirects.map(&:first)
53
66
  max_fd = LibC.getdtablesize
54
67
  0.upto(max_fd) do |fd|
55
- next if io[fd]
56
- LibC.close fd
68
+ LibC.close fd unless redirected_fds.include?(fd)
57
69
  end
58
70
  end
59
71
 
@@ -8,23 +8,44 @@ describe ExecSandbox::Sandbox do
8
8
  @temp_in.close
9
9
  @temp_out = Tempfile.new 'exec_sandbox_rspec'
10
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
11
  end
17
12
  after do
18
13
  @temp_in.unlink
19
14
  @temp_out.unlink
20
15
  end
21
16
 
22
- it 'should not crash' do
23
- @result[:exit_code].should == 0
17
+ describe 'duplicate.rb' do
18
+ before do
19
+ ExecSandbox.use do |s|
20
+ @result = s.run bin_fixture(:duplicate), :in => @temp_in.path,
21
+ :out => @temp_out.path
22
+ end
23
+ end
24
+
25
+ it 'should not crash' do
26
+ @result[:exit_code].should == 0
27
+ end
28
+
29
+ it 'should produce the correct result' do
30
+ File.read(@temp_out.path).should == "I/O test\nI/O test\n"
31
+ end
24
32
  end
25
-
26
- it 'should produce the correct result' do
27
- File.read(@temp_out.path).should == "I/O test\nI/O test\n"
33
+
34
+ describe 'count.rb' do
35
+ before do
36
+ ExecSandbox.use do |s|
37
+ @result = s.run [bin_fixture(:count), '9'], :in => @temp_in.path,
38
+ :out => @temp_out.path, :err => :out
39
+ end
40
+ end
41
+
42
+ it 'should not crash' do
43
+ @result[:exit_code].should == 0
44
+ end
45
+
46
+ it 'should produce the correct result' do
47
+ File.read(@temp_out.path).should == (1..9).map { |i| "#{i}\n" }.join('')
48
+ end
28
49
  end
29
50
  end
30
51
 
@@ -61,6 +82,22 @@ describe ExecSandbox::Sandbox do
61
82
  @result[:out_data].should == "S" * buffer_size
62
83
  end
63
84
  end
85
+
86
+ describe 'count.rb' do
87
+ before do
88
+ ExecSandbox.use do |s|
89
+ @result = s.run [bin_fixture(:count), '9'], :err => :out
90
+ end
91
+ end
92
+
93
+ it 'should not crash' do
94
+ @result[:exit_code].should == 0
95
+ end
96
+
97
+ it 'should produce the correct result' do
98
+ @result[:out_data].should == (1..9).map { |i| "#{i}\n" }.join('')
99
+ end
100
+ end
64
101
  end
65
102
 
66
103
 
@@ -69,6 +69,35 @@ describe ExecSandbox::Spawn do
69
69
  @status[:exit_code].should_not == 0
70
70
  end
71
71
  end
72
+
73
+ shared_examples_for 'count.rb' do
74
+ it 'should not crash' do
75
+ @status[:exit_code].should == 0
76
+ end
77
+
78
+ it 'should write successfully' do
79
+ @temp_out.open
80
+ begin
81
+ @temp_out.read.should == (1..9).map { |i| "#{i}\n" }.join('')
82
+ ensure
83
+ @temp_out.close
84
+ end
85
+ end
86
+ end
87
+
88
+ describe 'with file descriptor and stderr redirected to stdout' do
89
+ before do
90
+ File.open(@temp_in.path, 'r') do |in_io|
91
+ File.open(@temp_out.path, 'w') do |out_io|
92
+ pid = ExecSandbox::Spawn.spawn [bin_fixture(:count), '9'],
93
+ {:in => in_io, :out => out_io, :err => STDOUT}
94
+ @status = ExecSandbox::Wait4.wait4 pid
95
+ end
96
+ end
97
+ end
98
+
99
+ it_behaves_like 'count.rb'
100
+ end
72
101
  end
73
102
 
74
103
  describe '#spawn principal' do
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Counts from 1 to the first argument, and writes each number on a separate
4
+ # line. Odd numbers go to STDOUT, even numbers go to STDERR.
5
+ # on both STDOUT and STDERR.
6
+
7
+ limit = ARGV[0].to_i
8
+ 1.upto(limit) do |i|
9
+ file = (i % 2 == 0) ? STDERR : STDOUT
10
+ file.write "#{i}\n"
11
+ file.flush
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exec_sandbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2011-12-20 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi
16
- requirement: &15824600 !ruby/object:Gem::Requirement
16
+ requirement: &26627980 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.11
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *15824600
24
+ version_requirements: *26627980
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rdoc
27
- requirement: &15824120 !ruby/object:Gem::Requirement
27
+ requirement: &26627380 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '3.10'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *15824120
35
+ version_requirements: *26627380
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &15823640 !ruby/object:Gem::Requirement
38
+ requirement: &26626760 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.6.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *15823640
46
+ version_requirements: *26626760
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: yard
49
- requirement: &15823160 !ruby/object:Gem::Requirement
49
+ requirement: &26626260 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.7.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *15823160
57
+ version_requirements: *26626260
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: yard-rspec
60
- requirement: &15822680 !ruby/object:Gem::Requirement
60
+ requirement: &26625740 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0.1'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *15822680
68
+ version_requirements: *26625740
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: bundler
71
- requirement: &15822200 !ruby/object:Gem::Requirement
71
+ requirement: &26625220 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: 1.0.21
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *15822200
79
+ version_requirements: *26625220
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: jeweler
82
- requirement: &15821720 !ruby/object:Gem::Requirement
82
+ requirement: &26624740 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: 1.6.4
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *15821720
90
+ version_requirements: *26624740
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rcov
93
- requirement: &15821240 !ruby/object:Gem::Requirement
93
+ requirement: &26624240 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *15821240
101
+ version_requirements: *26624240
102
102
  description: Temporary users and groups, rlimits
103
103
  email: costan@gmail.com
104
104
  executables: []
@@ -128,6 +128,7 @@ files:
128
128
  - spec/exec_sandbox/wait4_spec.rb
129
129
  - spec/fixtures/buffer.rb
130
130
  - spec/fixtures/churn.rb
131
+ - spec/fixtures/count.rb
131
132
  - spec/fixtures/duplicate.rb
132
133
  - spec/fixtures/exit_arg.rb
133
134
  - spec/fixtures/fork.rb
@@ -150,7 +151,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
150
151
  version: '0'
151
152
  segments:
152
153
  - 0
153
- hash: -3240483873242314683
154
+ hash: -1397950588643828772
154
155
  required_rubygems_version: !ruby/object:Gem::Requirement
155
156
  none: false
156
157
  requirements:
@@ -159,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
160
  version: '0'
160
161
  requirements: []
161
162
  rubyforge_project:
162
- rubygems_version: 1.8.11
163
+ rubygems_version: 1.8.12
163
164
  signing_key:
164
165
  specification_version: 3
165
166
  summary: Run foreign binaries using POSIX sandboxing features