Forum: Ruby String segfault with wide character function, Windows

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Daniel B. (Guest)
on 2006-05-28 09:07
(Received via mailing list)
Ruby 1.8.4 (one click)
Windows XP

Check this out segfault:

# strtest.rb
require 'Win32API'

GetFullPathNameW = Win32API.new('kernel32','GetFullPathNameW','PLPP',
'L')

path = "C:\\test"
buf  = 0.chr * 260

if GetFullPathNameW.call(path, buf.size, buf, 0) == 0
    puts "Failed"
end

#p buf
p buf.split("\0\0").first # boom!

# Result
strtest.rb:13: [BUG] Segmentation fault
ruby 1.8.4 (2005-12-24) [i386-mswin32]

This application has requested the Runtime to terminate it in an unusual
way. Please contact the application's support team for more information.

I noticed that if I dropped the 'buf' to 245 or fewer characters, it
works ok.  Also, it segfaults on just about any op that modifies buf,
not just String#split.

Any ideas?

Thanks,

Dan
Daniel B. (Guest)
on 2006-05-29 07:14
(Received via mailing list)
Daniel B. wrote:
> path = "C:\\test"
> strtest.rb:13: [BUG] Segmentation fault
>
> Thanks,
>
> Dan
>
>

The solution was found by Heesob, which I have pasted below (taken from
the win32utils-devel mailing list):

I have found out what is the problem.
It's not bug of Ruby or Windows, it is only bug of code.

First try this:

require 'Win32API'
GetFullPathNameW = Win32API.new('kernel32','GetFullPathNameW','PLPP',
'L')
for i in 1..100
path = "c:\\test"
buf  = 0.chr * 260
if GetFullPathNameW.call(path, buf.size, buf, 0) == 0
     puts "Failed"
end
p buf.split("\0\0").first
end

It will cause various errors like
uninitialized constant GetFullPathNameW (NameError)
or
segfault.

Next, try this:

require 'Win32API'
GetFullPathNameW = Win32API.new('kernel32','GetFullPathNameW','PLPP',
'L')
for i in 1..100
path = "c:\\test"
buf  = 0.chr * 260
# buf.size/2 -> actual length of buf
if GetFullPathNameW.call(path, buf.size/2, buf, 0) == 0
     puts "Failed"
end
p buf.split("\0\0").first
end

It runs Ok. but the result is not correct.

Next , try this:

require 'Win32API'
GetFullPathNameW = Win32API.new('kernel32','GetFullPathNameW','PLPP',
'L')
for i in 1..100
# append \0 to path
path = "c:\\test\0"
buf  = 0.chr * 260
# buf.size/2 -> actual length of buf in unicode string
if GetFullPathNameW.call(path, buf.size/2, buf, 0) == 0
     puts "Failed"
end
p buf.split("\0\0").first
end

It runs ok. The result is correct.

Finally, the complete and correct code is like this:

require 'Win32API'
GetFullPathNameW = Win32API.new('kernel32','GetFullPathNameW','PLPP',
'L')
for i in 1..100
path = "c\0:\0\\\0t\0e\0s\0t\0\0"
buf  = 0.chr * 260
# buf.size/2 -> actual length of buf in unicode string
if GetFullPathNameW.call(path, buf.size/2, buf, 0) == 0
     puts "Failed"
end
buf = buf.split("\0\0").first
buf = (buf.size % 2).zero? ? buf : buf+"\0"
p buf
end

Remeber, Ruby's string is terminated with "\0" implicitly, but UTF16
string requires double "\0".
For ascii chars, it happens trailing three "\0"  : one for ascii char
and two for string termination.

Regards,

Park H.
This topic is locked and can not be replied to.