Andrey Karpov presented on common patterns of 64-bit errors seen in games and other applications. Some key points:
1) 64-bit errors are issues that only appear when building 64-bit applications, often due to implicit type conversions or assumptions made for 32-bit.
2) Examples included incorrect type sizes leading to overflows, missing includes causing errors in libraries, and pointer arithmetic issues.
3) To write high quality 64-bit code, developers should specify coding standards, use correct integer types like size_t, avoid magic numbers, and employ static analysis and unit testing.
3. Speaker
• Karpov Andrey Nikolaevich
• Habr profile: habr.com/users/Andrey2008/
• Microsoft MVP, Intel Black Belt Software Developer
• One of the founders of the PVS-Studio project, which
started right from the search of 64-bit errors
• https://www.viva64.com
5. In practice
• 64-bit errors are the ones which reveal themselves after building a
64-bit application
• By the way, many of them do it only when the 4 Gigabyte limit is
exceeded
7. void Foo()
{
const size_t Gigabyte = 1073741824;
void *A[10];
for (size_t i = 0; i < 10; ++i)
{
A[i] = malloc(Gigabyte);
printf("%pn", A[i]);
}
for (size_t i = 0; i < 10; ++i)
free(A[i]);
}
8.
9. In C-code developers forget to include
headers
• Your program might not contain calls of malloc, but they might be in
third-party libraries
• It is revealed only when consuming large amount of memory
#include <stdio.h>
#include <string.h>
//#include <stdlib.h>
10. The case isn’t only about malloc
• Fennec Media
• memset
• memcpy
• Ffdshow (media decoder)
• memset
• memcpy
• libZPlay
• strlen
12. long type in Win64
const std::vector<Vec2> &vertexList =
body->getCalculatedVertexList();
unsigned long length = vertexList.size();
PVS-Studio: V103 Implicit type conversion from memsize to 32-bit type.
CCArmature.cpp 614
Cocos2d-x
13. int type is a bad idea in any case
PVS-Studio: V104 Implicit conversion of 'i' to memsize type in an arithmetic
expression: i < points_.size() sweep_context.cc 76
std::vector<Point*> points_;
void SweepContext::InitTriangulation()
{
// Calculate bounds.
for (unsigned int i = 0; i < points_.size(); i++) {
Point& p = *points_[i];
....
Cocos2d-x
14. By the way, such errors are more insidious
than it looks
• When overflowing int, undefined behavior occurs
• UB might be the case when everything works correctly :)
• More details: "Undefined behavior is closer than you think"
https://www.viva64.com/ru/b/0374/
size_t N = foo();
for (int i = 0; i < N; ++i)
16. "memsize" types
• ptrdiff_t - signed type (distance between pointers)
• size_t – maximum size of theoretically possible object of any type,
including arrays
• rsize_t – size of a single object
• intptr_t - signed type, can store a pointer
• uintptr_t – unsigned type, can store a pointer
17. Usage of memsize-types
• Number of elements in an array
• Buffer size
• Counters
• Pointer storing (although, it’s better not to do it)
• Pointer arithmetic
• And so on
18. Using of memsize-types: bad thing
PVS-Studio: V109 Implicit type conversion of return value 'a * b * c' to memsize
type. test.cpp 22
Potential overflow
size_t Foo(int a, int b, int c)
{
return a * b * c;
}
19. Using of memsize-types: OK
size_t Foo(int a, int b, int c)
{
return static_cast<size_t>(a)
* static_cast<size_t>(b)
* static_cast<size_t>(c);
}
size_t Foo(int a, int b, int c)
{
return static_cast<size_t>(a) * b * c;
}
20. JUST DON’T DO IT!
std::vector<unsigned char> buf(
static_cast<size_t>(exr_image->width * h * pixel_data_size));
PVS-Studio: V1028 CWE-190 Possible overflow. Consider casting operands, not the
result. tinyexr.h 11682
Possible
overflow again
TinyEXR
21. This way errors only hide more
if (components == 1) {
images[0].resize(static_cast<size_t>(width * height));
memcpy(images[0].data(), data, sizeof(float) * size_t(width * height));
} else {
images[0].resize(static_cast<size_t>(width * height));
images[1].resize(static_cast<size_t>(width * height));
images[2].resize(static_cast<size_t>(width * height));
images[3].resize(static_cast<size_t>(width * height));
for (size_t i = 0; i < static_cast<size_t>(width * height); i++) {
TinyEXR
22. More about pointer arithmetic
int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B);
printf("%in", *ptr); //Access violation
//on 64-bit platform
34. Dynamic memory
• Is it worth doing some fighting?
• Are 64-bit integer types needed everywhere?
• Are the pointers needed everywhere?
• Structures’ size
35. Redundant growth of structures’ size
struct Candidate {
uint32_t face;
ChartBuildData *chart;
float metric;
};
PVS-Studio: V802 On 64-bit platform, structure size can be reduced from 24 to 16
bytes by rearranging the fields according to their sizes in decreasing order. xatlas.cpp
5237
24 bytes
40. Dangerous tricks with type casting
ResourceBase::Header *header = (*iter).value;
char fourCC[ 5 ];
*( ( U32* ) fourCC ) = header->getSignature();
fourCC[ 4 ] = 0;
Everything will be fine thanks to header, but still very unstable.
PVS-Studio: V1032 The pointer 'fourCC' is cast to a more strictly aligned pointer
type. resourceManager.cpp 127
Torque 3D
46. Specify the code standard
• Use correct types:
ptrdiff_t, size_t, intptr_t, uintptr_t, INT_PTR, DWORD_PTR и т.д.
• Say «NO!» to magic numbers. Use this:
UINT_MAX, ULONG_MAX, std::numeric_limits<size_t>::max() и т.д.
• Explain how to correctly use the explicit type casting in the
expressions and what’s the difference between 1 and
static_cast<size_t>(1)
• unsigned long != size_t
50. Conclusion
• Even if you already have released a 64-bit application, this doesn’t
mean that it works correctly
• A 64-bit error can lie low for ages
• How to solve the situation:
• learning
• Code standard
• Static code analysis
51. Useful links
• Lessons on development of 64-bit C/C++ applications (single file)
https://www.viva64.com/ru/l/full/
• A Collection of Examples of 64-bit Errors in Real Programs
https://www.viva64.com/ru/a/0065/
• Undefined behavior is closer than you think
https://www.viva64.com/ru/b/0374/
52. Time for your questions!
Andrey Karpov
karpov@viva64.com
www.viva64.com