Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(MODULES-2409) Endian-ness incorrect for DWORD and QWORD #95

Conversation

Iristyle
Copy link
Contributor

  • In 0b99718 a helper method to_byte_array was introduced to convert
    a numeric value into a properly padded byte array. Unfortunately
    that introduced an error as the byte array ordering was incorrect.

    This has caused DWORD and QWORD values to be written wrong.

    For instance, creating a native (little) endian string from a large
    32-bit value (4294967294) should yield the following bytes:

    pry(main)> [2*32-2].pack('L').unpack('C')
    => [254, 255, 255, 255]

    That can be further round-tripped with .pack('C_').unpack('L_')[0]

    However, using to_bytes_array, the bytes come back improperly ordered

    pry(main)> to_byte_array(2**32-2, 4)
    => [255, 255, 255, 254]

    pry(main)> [255, 255, 255, 254].pack('C_').unpack('L_')[0]
    => 4278190079

    As a result, because of the improper ordering of bytes, the wrong
    value is written.

  • Previous tests were passing since the range of written values
    tested was below 0xFF (255). The first point at which written
    values diverge is 256, where the byte array [0, 1, 0, 0] should
    have been created, but instead [1, 0, 0, 0] was written.

  • The simplest additional test to perform was to validate that the
    written values from the deletion test match what is expected.

@Iristyle
Copy link
Contributor Author

@joshcooper if you want to take a look this should be good.

when Win32::Registry::REG_QWORD
bytes = to_byte_array(data, FFI::Type::UINT64.size)
# Q is 64-bit unsigned native (little) endian order
bytes = [data].pack('Q').unpack('C*')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

irb(main):007:0> [0x12345678].pack('L').unpack('C*').map {|ch| ch.to_s(16)}
=> ["78", "56", "34", "12"]
irb(main):008:0> [0x1234567890ABCDEF].pack('Q').unpack('C*').map {|ch| ch.to_s(16)}
=> ["ef", "cd", "ab", "90", "78", "56", "34", "12"]

👍

 - In 0b99718 a helper method to_byte_array was introduced to convert
   a numeric value into a properly padded byte array.  Unfortunately
   that introduced an error as the byte array ordering was incorrect.

   This has caused DWORD and QWORD values to be written wrong.

   For instance, creating a native (little) endian string from a large
   32-bit value (4294967294) should yield the following bytes:

   pry(main)> [2**32-2].pack('L').unpack('C*')
   => [254, 255, 255, 255]

   That can be further round-tripped with .pack('C*').unpack('L*')[0]

   However, using to_bytes_array, the bytes come back improperly ordered

   pry(main)> to_byte_array(2**32-2, 4)
   => [255, 255, 255, 254]

   pry(main)> [255, 255, 255, 254].pack('C*').unpack('L*')[0]
   => 4278190079

   As a result, because of the improper ordering of bytes, the wrong
   value is written.

 - Previous tests were passing since the range of written values
   tested was below 0xFF (255).  The first point at which written
   values diverge is 256, where the byte array [0, 1, 0, 0] should
   have been created, but instead [1, 0, 0, 0] was written.  Therefore,
   new tests have been added to test writing DWORD and QWORD values at
   each byte boundary + 1 bit - for instance 0xFF + 1, 0xFFFF + 1,
   ad nauseum.

 - The simplest additional test to perform was to validate that the
   written values from the deletion test match what is expected. These
   values are randomly generated.
 - Add an additional check to ensure that the registry 'write' method
   is never called, which can trigger Ruby's bad behavior.
@Iristyle Iristyle force-pushed the ticket/master/MODULES-2409-dword-qword-endianess-wrong branch from da3bd6e to c022d8c Compare August 13, 2015 06:30
@Iristyle
Copy link
Contributor Author

@joshcooper added some additional tests that verify round-trips. With the previous code, they all fail:

  1) Puppet::Type::Registry_value::ProviderRegistry#destroy can destroy a randomly created REG_DWORD value
     Failure/Error: expect(reg_value.provider.data).to eq([data].flatten)

       expected: [931501358]
            got: [781550903]

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:99:in `create_and_destroy'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:118:in `block (3 levels) in <top (required)>'

  2) Puppet::Type::Registry_value::ProviderRegistry#destroy can destroy a randomly created REG_QWORD value
     Failure/Error: expect(reg_value.provider.data).to eq([data].flatten)

       expected: [990134975776607700]
            got: [15334152783694314765]

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:99:in `create_and_destroy'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:122:in `block (3 levels) in <top (required)>'

  3) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 256
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:158:in `block (4 levels) in <top (required)>'

  4) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 65536
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:158:in `block (4 levels) in <top (required)>'

  5) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 16777216
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:158:in `block (4 levels) in <top (required)>'

  6) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 1099511627776
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:165:in `block (4 levels) in <top (required)>'

  7) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 281474976710656
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:165:in `block (4 levels) in <top (required)>'

  8) Puppet::Type::Registry_value::ProviderRegistry when writing numeric values properly round-trips written values by converting endianness properly
     Failure/Error: expect(written).to eq(value)

       expected: 72057594037927936
            got: 1

       (compared using ==)
     # ./spec/unit/puppet/provider/registry_value_spec.rb:151:in `write_and_read_value'
     # ./spec/unit/puppet/provider/registry_value_spec.rb:165:in `block (4 levels) in <top (required)>'

I think this is ready to go now.

@Iristyle Iristyle changed the title (MODULES-2409) Endian-ness incorrect for DWORD (MODULES-2409) Endian-ness incorrect for DWORD and QWORD Aug 13, 2015
@@ -30,6 +30,9 @@
# also, expect that we're not using Rubys each_key / each_value which exhibit bad behavior
Win32::Registry.any_instance.expects(:each_key).never
Win32::Registry.any_instance.expects(:each_value).never

# this covers []= write_s write_i and write_bin
Win32::Registry.any_instance.expects(:write).never
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@joshcooper
Copy link
Contributor

👍

@ferventcoder
Copy link
Contributor

Looks good. Merging.

ferventcoder added a commit that referenced this pull request Aug 13, 2015
…-qword-endianess-wrong

(MODULES-2409) Endian-ness incorrect for DWORD and QWORD
@ferventcoder ferventcoder merged commit a5f6ccb into puppetlabs:master Aug 13, 2015
@Iristyle Iristyle deleted the ticket/master/MODULES-2409-dword-qword-endianess-wrong branch August 14, 2015 06:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants