Check all the fragments where a pointer is explicitly cast to integer types

The fragment is taken from IPP Samples project. The error is detected by the followingย diagnostic: V205 Explicit conversion of pointer type to 32-bit integer type: (unsigned long)(img)

void write_output_image(...., const Ipp32f *img, 
                        ...., const Ipp32s iStep) {
  ...
  img = (Ipp32f*)((unsigned long)(img) + iStep);
  ...
}

Explanation

A programmer wants to shift a pointer at a certain number of bytes. This code will execute correctly in Win32 mode because the pointer size is the same as that of the long type. But if we compile a 64-bit version of the program, the pointer will become 64-bit, and casting it to long will cause the loss of the higher bits.

Note. Linux uses a different data model. In 64-bit Linux programs, the ‘long’ type is 64-bit too, but it’s still a bad idea to use ‘long’ to store pointers there. First, such code tends to get into Windows applications quite often, where it becomes incorrect. Second, there are special types whose very names suggest that they can store pointers – for example, intptr_t. Using such types makes the program clearer.

In the example above, we can see a classic error which occurs in 64-bit programs. It should be said right off that there are lots of other errors, too, awaiting programmers in their way of 64-bit software development. But it is the writing of a pointer into a 32-bit integer variable that’s the most widespread and insidious issue.

This error can be illustrated in the following way:

image2

Figure 1. A) 32-bit program. B) 64-bit pointer refers to an object that is located in the lower addresses. C) 64-bit pointer is damaged.

Speaking about its insidiousness, this error is sometimes very difficult to notice. The program just “almost works”. Errors causing the loss of the most significant bits in pointers may only show up in a few hours of intense use of the program. First, the memory is allocated in the lower memory addresses, that’s why all the objects and arrays are stored in the first 4 GB of memory. Everything works fine.

As the program keeps running, the memory gets fragmented, and even if the program doesn’t use much of it, new objects may be created outside those first 4 GB. This is where the troubles start. It’s extremely difficult to purposely reproduce such issues.

Correct code

You can use such types as size_t, INT_PTR, DWORD_PTR, intrptr_t, etc. to store pointers.

img = (Ipp32f*)((uintptr_t)(img) + iStep);

Actually, we can do it without any explicit casting. It is not mentioned anywhere that the formatting is different from the standard one, that’s why there is no magic in using __declspec(align( # )) and so on. So, the pointers are shifted by the number of bytes that is divisible by Ipp32f; otherwise we will have undefined behavior (see EXP36-C)

So, we can write it like this:

img += iStep / sizeof(*img);

Recommendation

Use special types to store pointers – forget about int and long. The most universal types for this purpose are intptr_t and uintptr_t. In Visual C++, the following types are available: INT_PTR, UINT_PTR, LONG_PTR, ULONG_PTR, DWORD_PTR. Their very names indicate that you can safely store pointers in them.

A pointer can fit into the types size_t and ptrdiff_t too, but I still wouldn’t recommend using them for that, for they are originally intended for storing sizes and indices.

You cannot store a pointer to a member function of the class in uintptr_t. Member functions are slightly different from standard functions. Except for the pointer itself, they keep hidden value of this that points to the object class. However, it does not matter – in the 32-bit program, you can not assign such a pointer to unsigned int. Such pointers are always handled in a special way, that’s why there aren’t many problems in 64-bit programs. At least I haven’t seen such errors.

If you are going to compile your program into a 64-bit version, first, you need to review and fix all the code fragments where pointers are cast into 32-bit integer types. Reminder – there will be more troublesome fragments in the program, but you should start with the pointers.

For those who are creating or planning to create 64-bit applications, I suggest studying the following resource: Lessons on development of 64-bit C/C++ applications.

Written by Andrey Karpov.

This error was found with PVS-Studio static analysis tool.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s