mmap causing a bus error

T

Tim Pease

$ cat mmap-abort.rb

require 'mmap'
require 'fileutils'

FileUtils.touch 'output.txt'
File.open('mmap.bin', 'w') {|f| f.truncate 16} unless File.exist? 'mmap.bin'

m = Mmap.new 'output.txt', 'rw'

(0...4).each do |x|
fork do
mmap = Mmap.new 'mmap.bin', 'rw'
range = (x*4)...(x*4+4)
val = mmap[range].unpack('N')[0]
mmap[range] = [val+x].pack('N')
mmap.unmap
end
end

m << "Done!\n"

# EOF
$ ruby mmap-abort.rb
mmap-abort.rb:20: [BUG] Bus Error
ruby 1.8.4 (2005-12-24) [powerpc-linux]

Aborted (core dumped)
$

The line at the end -- m << "Done!\n" -- is causing the abort. I'm
seeing this behavior on all the Unix platforms I have access to ...

SunOS ravenclaw 5.9 Generic_112233-12 sun4u sparc SUNW,Sun-Blade-1500

Linux panic 2.6.17-1.2157_FC5 #1 SMP Tue Jul 11 23:03:20 EDT 2006
ppc64 ppc64 ppc64 GNU/Linux

Linux pong 2.6.17-1.2142_FC4 #1 Tue Jul 11 22:41:14 EDT 2006 i686 i686
i386 GNU/Linux


So, the interesting part is that if I open the "output.txt" file and
add a single character to it, the problem disappears ...

$ echo 'x' > output.txt
$ ruby mmap-abort.rb
$ cat output.txt
x
Done!
$

I can also make the problem go away by creating the mmap to output.txt
after the each/fork block.

I can also make the problem go away by commenting out the "mmap.unmap"
line at the end of the fork block.

The bus error is being caused by a call to memcpy in the "mm_cat"
method of the mmap source code -- line 1480 of mmap.c

Any patches out there?
 
A

ara.t.howard

$ cat mmap-abort.rb

require 'mmap'
require 'fileutils'

FileUtils.touch 'output.txt'
File.open('mmap.bin', 'w') {|f| f.truncate 16} unless File.exist? 'mmap.bin'

m = Mmap.new 'output.txt', 'rw'

(0...4).each do |x|
fork do
mmap = Mmap.new 'mmap.bin', 'rw'
range = (x*4)...(x*4+4)
val = mmap[range].unpack('N')[0]
mmap[range] = [val+x].pack('N')
mmap.unmap
end
end

m << "Done!\n"

# EOF
$ ruby mmap-abort.rb
mmap-abort.rb:20: [BUG] Bus Error
ruby 1.8.4 (2005-12-24) [powerpc-linux]

Aborted (core dumped)
$

The line at the end -- m << "Done!\n" -- is causing the abort. I'm
seeing this behavior on all the Unix platforms I have access to ...

SunOS ravenclaw 5.9 Generic_112233-12 sun4u sparc SUNW,Sun-Blade-1500

Linux panic 2.6.17-1.2157_FC5 #1 SMP Tue Jul 11 23:03:20 EDT 2006
ppc64 ppc64 ppc64 GNU/Linux

Linux pong 2.6.17-1.2142_FC4 #1 Tue Jul 11 22:41:14 EDT 2006 i686 i686
i386 GNU/Linux


So, the interesting part is that if I open the "output.txt" file and
add a single character to it, the problem disappears ...

$ echo 'x' > output.txt
$ ruby mmap-abort.rb
$ cat output.txt
x
Done!
$

I can also make the problem go away by creating the mmap to output.txt
after the each/fork block.

I can also make the problem go away by commenting out the "mmap.unmap"
line at the end of the fork block.

The bus error is being caused by a call to memcpy in the "mm_cat"
method of the mmap source code -- line 1480 of mmap.c

Any patches out there?


harp:~ > cat a.rb
require 'mmap'
require 'fileutils'

FileUtils.touch 'output.txt'
File.open('mmap.bin', 'w') {|f| f.truncate 16} unless File.exist? 'mmap.bin'

m = Mmap.new 'output.txt', 'rw'

(0...4).each do |x|
fork do
mmap = Mmap.new 'mmap.bin', 'rw'
range = (x*4)...(x*4+4)
val = mmap[range].unpack('N')[0]
mmap[range] = [val+x].pack('N')
p mmap.to_str
mmap.unmap
exit! # don't let child unmap automatically
end
Process.wait # if you don't do this it'll be non-deterministic
end

m << "Done!\n"


harp:~ > ruby a.rb
"\000\000\000\000\000\000\000\010\000\000\000\020\000\000\000\030"
"\000\000\000\000\000\000\000\t\000\000\000\020\000\000\000\030"
"\000\000\000\000\000\000\000\t\000\000\000\022\000\000\000\030"
"\000\000\000\000\000\000\000\t\000\000\000\022\000\000\000\e"


basically, you can't carry the mmap across the fork and hope that the child's
at_exit handlers don't interfere with the parent. check this out:

harp:~ > cat a.rb
require 'mmap'
STDOUT.sync = true
Mmap.new __FILE__, 'r'
fork{ 42 } and Process.wait


harp:~ > strace -f ruby a.rb 2>&1|egrep 'map|:[ABCD]:'|tail -3
mmap2(NULL, 88, PROT_READ, MAP_SHARED, 3, 0) = 0xb75ae000
[pid 2544] munmap(0xb75ae000, 88) = 0
munmap(0xb75ae000, 88) = 0


and with exit!


harp:~ > cat a.rb
require 'mmap'
STDOUT.sync = true
Mmap.new __FILE__, 'r'
fork{ 42 and exit! } and Process.wait


harp:~ > strace -f ruby a.rb 2>&1|egrep 'map|:[ABCD]:'|tail -3
old_mmap(0xbab000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 3, 0x7000) = 0xbab000
mmap2(NULL, 98, PROT_READ, MAP_SHARED, 3, 0) = 0xb75af000
munmap(0xb75af000, 98) = 0



note the double munmap. i don't know if this is os specific or not or if it's
expected or not - but preventing the child from freeing the map seems to fix
it.


kind regards.

-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top