Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
A Collection of Examples of 64-bit Errors in Real Programs (intel.com)
143 points by toni on March 31, 2011 | hide | past | favorite | 30 comments


I found myself chuckling at all the odd little pictures used to denote invalid memory areas or edge conditions (16-bit Windows is a semi-truck stuck in mud, integer truncation is the top of a truck getting shorn off by an overpass). Haha!

This is definitely one of the more colourful technical documents I've read.


Despite the passing years, programs or some of their parts written in C remain as large as life. The code of these programs is much more subject to 64-bit errors because of less strict rules of type checking in the C language.

Yeah, because a language with 4 different typecast operators is type safe.


As far as I know, C only has one; perhaps you are thinking of C++?


I'm making fun of C++ programmers that think C++ is better than C, like the authors of this article.


The problem with C++ casting isn't that there are 4 different typecast operators. On the contrary, that allows you to more clearly state your intent to the compiler and prevent unexpected bugs. The problem with C++ casting is that they allow you to do C-style casting at all.


The C++-style casts are pretty nasty, too. Want to un-const something? const_cast it! Want to make that int * a FooObject *? reinterpret_cast it! What could go wrong!

C++ pretends to be a safe language with features like encapsulation. The problem is that it isn't. One bad strcpy later and your "encapsulated" class's data is now "HELLO WORLD\0".

C, on the other hand, makes no effort to pretend that the abstraction being offered is anything other than a big block of RAM and some functions.

(If you want a safe language, use Haskell. Notice how nobody ever needs "unsafeCoerce :: a -> b"?)


While some of these are great examples of common pitfalls with 64 bits errors, I find it amusing that the first example has a deeper conceptual problem: You can't (shouldn't) use memset to assign NULL to a pointer variable. In many (maybe most) implementations the address 0 is used by the implementation for NULL pointers but the standard allow any number (that can't be a valid address). The compiler automatically converts both 0 and NULL to the proper number when assigned to a pointer; but setting all bytes to 0 (as with memset) only works in the implementations that actually use address 0 for NULL pointers.


I was under the impression that NULL is required to be zero by the standard by now. If still not, it's high time that they give up! Differences like this between theory and practice do not go away easily. The decision is changing which one is easier, the culture or the rule...


It hasn't, and it won't, because a definition of NULL as "all-bits-zero" would be inconvenient for some embedded systems, where I/O ports or interrupt vectors are mapped at low addresses.


There are two separate NULLs. In C for instance a pointer with the value 0 is a "null pointer". This is a language construct. When a compiler sees this construct it knows to use whatever it thinks is best to represent a null pointer on the target architecture (which may not be the same thing as a representation of the value 0).


Pretty basic stuff, but unfortunately when these errors are made in real life, they are sometimes very hard-to-catch heisenbugs.


I hadn't heard the term heisenbug! Here's a link for anybody else who may not know the term (and others of that sort): http://en.wikipedia.org/wiki/Unusual_software_bug


Wikipedia's attempts to come up with names for these classes of things that "everyone" already groups together, but which have no widely used name, because it has to give the article some sort of title, are a pretty nice feature of the encyclopedia. "Unusual software bug" has a nicely euphemistic feel to it too, sort of like "anomalous condition".


Ah ... I remember the good old days when you just had to be careful about big-endian/little-endian when doing bit twiddling :)


The article failed to mentioned that there are few "C" language 64-bit models - where ints, shorts and longs can be of different size (or all equal to the pointer size). Some compilers, operating systems, and combinations of take one model as preferred over another (or just assumes that model).

http://en.wikipedia.org/wiki/64-bit#Specific_C-language_data...


Why do some people put C in quotes when talking about the language? Do they also type "Java" or "Perl"? It's bizarre.


Non C/C++ user here...

A lot of the problems are due to the assumption an integer is big enough to store a pointer.

Just wondering: is there a way to make the compiler barf if such an assignment is done? Or for generating a runtime error if the assignment doesn't fit?


I believe gcc will warn about this, unless you explicitly pass -Wno-pointer-to-int-cast, which will "suppress warnings from casts from a pointer to an integer type of a different size."


Using stricter compiler flags is often the fastest and easiest way to catch such bugs. Here at CMU, we usually require all assignments for undergrad C/C++ courses to compile with no warnings using "gcc -ansi -pedantic -Wall -Wextra".

If you never acquire the habit of writing sloppy code and ignoring warnings, it's not even that bad. In fact, it's only the kids who already know C coming in who complain about the stricter flags.


Relevant:

Comparing capabilities of PVS-Studio and Visual Studio 2010 in detecting defects in 64-bit programs - http://www.viva64.com/en/a/0066/

64 bits, Wp64, Visual Studio 2008, Viva64 and all the rest - http://www.viva64.com/en/a/0021/


Yes they can. VC++ has a setting for 64bit compatibility warnings. I would be surprised if g++ didn't.


Right, I think that almost all the problems state are int-to-something conversion (one was about putting a number into a double).

Used to be (old guy talking) 'int' meant 'register size' so on a 64-bit target it whould be 64 bits. Whereupon all the 'bugs' evaporate.


This article can be summarized as follows: use size_t and ssize_t when doing pointer arithmetic


Sure, as long as you are working with a standard and linear memory model. The assumption that size_t and ptrdiff_t can store a pointer may break down on segmented memory architectures for example. If you want to code against the standard instead of an assumption, use uintptr_t and intptr_t instead.


When would someone want to use intptr_t instead of uintptr_t? Does a signed memory address even make sense? Perhaps intptr_t is available to avoid compiler warnings about mixing signed/unsigned ints when adding a uintptr_t and a (signed) ptrdiff_t.


While I don't know of any specific use-case, it is of course very easy to imagine an architecture with signed memory-address space. One could for example separate protected/kernel memory from user land memory with the signedness.


There's more to it than that. Also, ssize_t is not standard C++, use ptrdiff_t instead.


ptrdiff_t is not guaranteed to be the same size as size_t, but ssize_t is. Also, IIRC ptrdiff_t is unsigned, whereas ssize_t is signed, which could make 32/64 bit arithmetic a PITA.

you are correct inasfar as ssize_t is a posix extension.



I get a:

504 Gateway Time-out nginx

Is this one of the example 64-bit errors?

Edit: Now it's working




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: