1. it’s only abuse if it crashes
the art of low-level ruby and other macabre tales
Eleanor McHugh
http://slides.games-with-brains.net/
2. with Kernel.syscall
process 1 process 2
require ‘fcntl’ Open, Wait, TryWait, Post = 268, 271, 272, 273
Open, Wait, Post, Close = 268, 271, 273, 269 s = syscall Open, “/tmp/s”
s = syscall Open, “/tmp/s”, Fcntl::O_CREAT, 1911 begin
syscall Wait, s t = Time.now
puts “locked at #{Time.now}” syscall TryWait, s
sleep 50 puts “locked at #{t}”
puts “posted at #{Time.now}” rescue Exception => e
syscall Post, s puts “busy at #{t}”
syscall Close, s syscall Wait, s
puts “waited #{Time.now - t} seconds”
end
produces: produces:
locked at Thu May 28 01:03:23 +0100 2009 busy at Thu May 28 01:03:36 +0100 2009
posted at Thu May 28 01:04:13 +0100 2009 waited 47.056508 seconds
3. or the ruby/dl way
require ‘dl’
require ‘fcntl’
LIBC = DL::dlopen ‘libc.dylib’
open = LIBC[‘sem_open’, ‘ISII’]
try_wait = LIBC[‘sem_trywait’, ‘II’]
wait = LIBC[‘sem_wait’, ‘II’]
post = LIBC[‘sem_post’, ‘II’]
close = LIBC[‘sem_close’, ‘II’]
process 1 process 2
s = open.call(“/tmp/s”, Fcntl::O_CREAT, 1911)[0] s = open.call(“/tmp/s”)
wait.call s t = Time.now
puts “locked at #{Time.now}” if try_wait.call(s)[0] == 0 then
sleep 50 puts “locked at #{t}”
puts “posted at #{Time.now}” else
post.call s puts “busy at #{t}”
close.call s wait.call s
puts “waited #{Time.now - t} seconds”
=> locked at Thu May 28 01:03:23 +0100 2009 end
=> posted at Thu May 28 01:04:13 +0100 2009
=> busy at Thu May 28 01:03:36 +0100 2009
=> waited 47.056508 seconds
6. but we can fix that?
Signal.trap(:SEGV) { $stderr.puts "segfault triggered" }
Process.kill :SEGV, 0
$stderr.puts "but I carry on as usual"
require 'dl'
memory = DL::malloc 1
4096.times { |i| memory[0] = 42.chr * i }
$stderr.puts "even genuine segfaults don't phase me"
produces:
segfault triggered
but I carry on as usual
7. maybe with some tenderlove
require 'dl'
require ‘neversaydie’
memory = DL::malloc 1
4096.times do |i|
begin
puts "#{i} : #{(memory[1] = 42.chr * i).length}"
rescue NeverSayDie => e
$stderr.puts "even genuine segfaults don't phase me"
break
end
end
produces:
even genuine segfaults don't phase me
8. but what I really want
require 'dl'
SIGSEGV = DL::dlopen('libsigsegv.dylib')
install_handler = SIGSEGV['sigsegv_install_handler', 'IP']
deinstall_handler = SIGSEGV['sigsegv_deinstall_handler', '0']
leave_handler = SIGSEGV['sigsegv_leave_handler', 'IPPPP']
continuation = DL.callback('IPPP') do |address, b, c|
raise RuntimeError, "segfault at #{address}"
end
handler = DL.callback('IPI') do |fault_address, serious|
leave_handler.call continuation, fault_address, nil, nil
end
install_handler.call handler