You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
3.1 KiB
Ruby

# frozen_string_literal: true
require 'fileutils'
require 'set'
linux_rb_so = File.join __dir__, 'linux_rb.so'
linux_rb_c = File.join __dir__, 'linux_rb.c'
if File.exist?(linux_rb_c) && (!File.exist?(linux_rb_so) ||
File.mtime(linux_rb_so) < File.mtime(linux_rb_c))
require 'rbconfig'
cfg = RbConfig::CONFIG
cmd = "#{cfg['DLDSHARED']} #{cfg['CCDLFLAGS']} #{cfg['CFLAGS']} #{cfg['LDFLAGS']}".
split(/ +/)
cmd += %W(-I#{cfg['rubyarchhdrdir']} -I#{cfg['rubyhdrdir']} -g -o #{linux_rb_so}
#{linux_rb_c})
system *cmd or fail 'Failed to compile linux_rb'
end
require linux_rb_so
module Linux
extend self
def clonens &blk
uid = Process.uid
gid = Process.gid
clone CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | \
CLONE_NEWNET do
uid_gid_map uid, gid
mount '/', '/', nil, MS_REC | MS_PRIVATE
mount 'none', '/proc', 'proc', MS_NOSUID | MS_NODEV | MS_RDONLY
FileUtils.mkdir_p 'tmp'
mount 'tmp', '/tmp', nil, MS_BIND
mount nil, '/tmp', nil, MS_BIND | MS_REMOUNT | MS_NOSUID | MS_NODEV
# Make home unwriteable
cwd = File.expand_path('.')
mount cwd, cwd, nil, MS_BIND rescue nil
mount nil, cwd, nil, MS_BIND | MS_REMOUNT | MS_NOSUID | MS_NODEV
make_ro [cwd, '/tmp']
# Try to automatically reap children. This does some magic so normal wait
# still works while still reaping most children. A single zombie
# conhost.exe seem to remain after executing wine, but it's much better
# that the 4 zombie processes I see without this.
Signal.trap 'CHLD', 'IGNORE'
blk.call
rescue Exception
puts $!.full_message
exit! 1
end
end
def uid_gid_map uid, gid, pid = 'self'
File.write "/proc/#{pid}/setgroups", 'deny'
File.write "/proc/#{pid}/gid_map", "#{gid} #{gid} 1\n"
File.write "/proc/#{pid}/uid_map", "#{uid} #{uid} 1\n"
end
def make_ro except
# read the full file in advance, because we will change the mounts that can
# change the file, and I have no freakin idea what happens in that case.
# Also mount points can be hidden by later mounts, ignore them as trying to
# remount them gives an EINVAL
points = {}
File.foreach('/proc/self/mountinfo').each do |l|
fields = l.split ' '
opts = fields[5].split ','
point = fields[4]
point_slash = point.end_with?('/') ? point : point + '/'
points.reject! {|p,ro| p.start_with? point_slash }
points[point] = opts.member? 'ro'
end
points.each do |point,ro|
next if except.include? point
# skip already RO mounts, depending on the sandbox sometimes they can't be
# remounted as RO, but that's not a problem...
next if ro
begin
mount nil, point, nil, MS_BIND | MS_REMOUNT | MS_RDONLY
rescue Errno::EPERM, Errno::EACCES
# /sys usually gives EPERM when trying to remount, ignore it...
$stderr.puts " \e[31;1m*\e[0m Failed to make #{point.inspect} ro"
end
end
end
def clonens_wait &blk
pid = clonens &blk
Process.wait pid
fail "Subprocess failed: #{$?}" unless $?.success?
end
end