| Prior to ruby 2.1.0, it was possible for multibyte strings to have only a single null terminator, followed by garbage which could cause buffer overruns. This was fixed in https://github.com/ruby/ruby/commit/1549894a0699abdfab1fbba0e468afd3f2f990b3, but never backported to 1.9.3. We added code to puppet to protect against this. See https://github.com/puppetlabs/puppet/commit/cabd95cbd06ac3d834e0e401bcb07c2878a0b0f2 and https://github.com/puppetlabs/puppet/commit/b7c10f5d045af5890f83159b74ab5d5a23e0abc9 However, puppet's from_string_to_wide_string copies the wide string from the ruby heap to the native heap using FFI::MemoryPointer#put_array_of_uchar. But it does not explicitly double null terminate the resulting wide string pointer. For example, here's a string of character length 1 and byte length 2.
irb(main):025:0> str = "a".encode("UTF-16LE") |
=> "a" |
irb(main):026:0> str.bytes.length |
=> 2 |
irb(main):027:0> str.length |
=> 1
|
When copying from the ruby heap to the native heap, we get a pointer to memory of length 2 bytes, which doesn't have room for the double null:
irb(main):028:0> ptr = FFI::MemoryPointer.new(:byte, str.bytes.length) |
=> #<FFI::MemoryPointer address=0x0000000003aea340 size=2> |
irb(main):029:0> ptr.total |
=> 2
|
This hasn't been an issue because Puppet::Util::Windows::String.wide_string embeds a double null in the ruby string:
irb(main):030:0> str = Puppet::Util::Windows::String.wide_string("a") |
=> "a\u0000" |
irb(main):031:0> str.bytes.length |
=> 4 |
irb(main):032:0> str.length |
=> 2
|
We should always explicitly double null terminate the wide string in the native heap, so as to not rely on the ruby workaround. |