0

I came across a weird issue when testing my opensource project MHTextSearch. At line 169 of MHTextIndex.m:

uint64_t maxLength = [indexedString maximumLengthOfBytesUsingEncoding:encoding];
// some code

for (...) {

    [indexedString getBytes:keyPtr
                  maxLength:maxLength
                 usedLength:&usedLength
                   encoding:encoding
                    options:NSStringEncodingConversionAllowLossy
                      range:subRange
             remainingRange:NULL];

    // some more code
}

Nothing, anywhere else, modifies maxLength. On the second iteration, maxLength is equal to 0, no matter what its previous value was. If I set a watchpoint on it, I see it changes in -[NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange:], at

0x100038d19:  movq   -0x30(%rbp), %rdx
0x100038d1d:  movq   %rdx, (%rsi)
0x100038d20:  testq  %rcx, %rcx            << this instruction

The very weird thing about this is that it only happens on the x86_64 architecture, and it can be fixed if I change the code like this

uint64_t maxLength = [indexedString maximumLengthOfBytesUsingEncoding:encoding];
uint64_t strLength = maxLength;
// some code

for (...) {

    [indexedString getBytes:keyPtr
                  maxLength:strLength
                 usedLength:&usedLength
                   encoding:encoding
                    options:NSStringEncodingConversionAllowLossy
                      range:subRange
             remainingRange:NULL];

    // some more code
}

With this code, maxLength still gets changed to 0 at the same instruction, but strLength stays consistent, so the effect is removed.

How come?

vcsjones
  • 138,677
  • 31
  • 291
  • 286
matehat
  • 5,214
  • 2
  • 29
  • 40

1 Answers1

2

usedLength has the wrong type. It's declared uint32_t. However, it should be declared NSUInteger, which is 32 bits on 32 bit architectures and 64 bits on 64 bit architectures.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • You're right! But still, I don't get understand how that affected the value of maxLength ... – matehat Jan 16 '14 at 20:29
  • They are consecutive on the stack. Since `getBytes:maxLength:usedLength:encoding:options:range:remainingRange:` treats it as 64 bits, it overwrites the 32 bits adjacent to your `usedLength` variable. That happens to be the low 32 bits of `maxLength`. – rob mayoff Jan 16 '14 at 20:31
  • Clear as day. Thanks a lot! – matehat Jan 16 '14 at 20:35
  • Did you not get a compiler diagnostic complaining that you passed a value of type uint32_t* to a parameter of type NSInteger* ? – Greg Parker Jan 16 '14 at 21:01