130

I came across the following C puzzle:

Q: Why does the following program segfault on IA-64, but work fine on IA-32?

  int main()
  {
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
  }

I know that the size of int on a 64 bit machine may not be the same as the size of a pointer (int could be 32 bits and pointer could be 64 bits). But I am not sure how this relates to the above program. Any ideas?

user7
  • 2,339
  • 5
  • 25
  • 29
  • 54
    Is it something silly like `stdlib.h` not being included? – user786653 Sep 25 '11 at 12:07
  • 3
    This code runs fine on my 64 bit machine. It even compiles without warnings if you `#include stdlib.h` (for malloc) – mpenkov Sep 25 '11 at 12:08
  • 1
    D'oh! @user786653 nailed the important bit. With `#include `, it's perfectly find, but that's not in the question. –  Sep 25 '11 at 12:08
  • I don't think this will segfault on 32 or 64 bit. It looks right. – sashang Sep 25 '11 at 12:09
  • @user786653 could be...i don't know the answer and hence the posting! – user7 Sep 25 '11 at 12:10
  • 1
    If `sizeof(int) == sizeof(int*)` the code works fine. This is quite independent on ia32 vs ia64. – Alexandre C. Sep 25 '11 at 12:11
  • i just tested that program on both a 32 and 64 bit linux system and saw no segmentation fault. – Dan D. Sep 25 '11 at 12:13
  • @AlexandreC.: Yes, hence the wording of the puzzle. The question is why it doesn't work when `sizeof int != sizeof int *`. –  Sep 25 '11 at 12:13
  • 8
    @delnan - it doesn't have to work like that though, it could legitimately fail on a platform where `sizeof(int) == sizeof(int*)`, if for example pointers got returned though a different register to `int`s in the calling convention used. – Flexo Sep 25 '11 at 13:29
  • @awoodland: Good point, and yet another reason not to do silly things like this. –  Sep 25 '11 at 13:30
  • 9
    In a C99 environment, the compiler should be giving you at least a warning about the implicit declaration of `malloc()`. GCC says: `warning: incompatible implicit declaration of built-in function 'malloc'` too. – Jonathan Leffler Sep 25 '11 at 20:34
  • See : https://www.securecoding.cert.org/confluence/display/seccode/INT11-C.+Take+care+when+converting+from+pointer+to+integer+or+integer+to+pointer – vine'th Sep 29 '11 at 14:03
  • 1
    This is a duplicate of http://stackoverflow.com/questions/5387090/segfault-on-ia-64-but-not-on-ia-32 although it would be silly to vote for a close at this point. – Zan Lynx Jan 17 '14 at 21:09
  • 1
    Are you sure this is IA-64 and not x86-64 (or x64)? – S.S. Anne Apr 23 '20 at 19:17

3 Answers3

152

The cast to int* masks the fact that without the proper #include the return type of malloc is assumed to be int. IA-64 happens to have sizeof(int) < sizeof(int*) which makes this problem obvious.

(Note also that because of the undefined behaviour it could still fail even on a platform where sizeof(int)==sizeof(int*) holds true, for example if the calling convention used different registers for returning pointers than integers)

The comp.lang.c FAQ has an entry discussing why casting the return from malloc is never needed and potentially bad.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • 1
    Note that the cast isn't needed in C, but it is needed in C++. Anyway, this question was about C so +1. –  Sep 25 '11 at 12:15
  • 6
    without the proper #include, why is the return type of malloc assumed to be an int? – user7 Sep 25 '11 at 12:15
  • 11
    @WTP - which is a good reason to always use `new` in C++ and always compile C with a C compiler and not a C++ compiler. – Flexo Sep 25 '11 at 12:16
  • 9
    @user7 - that's the rules. Any return type is assumed to be `int` if it's not known – Flexo Sep 25 '11 at 12:17
  • @user7: if it assumes for you the return type, is there a better idea? `int` is as arbitrary as everything other. – Vlad Sep 25 '11 at 12:17
  • @WTP depends on the standard though. – chacham15 Sep 25 '11 at 12:18
  • 2
    @vlad - the better idea is to *always* declare functions rather than rely upon implicit declarations for exactly this reason. (And not cast the return from `malloc`) – Flexo Sep 25 '11 at 12:19
  • @awoodland: I meant, a better idea for the compiler to determine the return type if it _must_ guess. – Vlad Sep 25 '11 at 12:22
  • @awoodland: C++ doesn't guess the function signature, so the cast in C++ doesn't cause the same harm as in C. I agree that C shouldn't be compiled with a C++ compiler, but this is not one of the reasons for that. – Steve Jessop Sep 25 '11 at 12:22
  • 1
    @awoodland : At the bit level, what happens when an int is cast to an int*? in this case, we have a pointer p (of size 64) which is pointing to 32 bits of memory (allocated by malloc). What happens when *p = 10 is executed? – user7 Sep 25 '11 at 12:28
  • 16
    @user7: "we have a pointer p (of size 64) which is pointing to 32 bits of memory" - wrong. The address of the block allocated by malloc was returned according to the calling convention for a `void*`. But the calling code thinks the function returns `int` (since you opted not to tell it otherwise), so it tries to read the return value according to the calling convention for an `int`. Hence `p` does *not* necessarily point to the allocated memory. It just so happened to work for IA32 because an `int` and a `void*` are the same size, and returned in the same way. On IA64 you get the wrong value. – Steve Jessop Sep 25 '11 at 12:41
  • 2
    @user7 For the return convention of type `int`, see [here](http://c-faq.com/decl/implfdecl.html) – ViniciusArruda Dec 08 '15 at 03:49
38

Most likely because you're not including the header file for malloc and, while the compiler would normally warn you of this, the fact that you're explicitly casting the return value means you're telling it you know what you're doing.

That means the compiler expects an int to be returned from malloc which it then casts to a pointer. If they're different sizes, that's going to cause you grief.

This is why you never cast the malloc return in C. The void* that it returns will be implicitly converted to a pointer of the correct type (unless you haven't included the header in which case it probably would have warned you of the potentially unsafe int-to-pointer conversion).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • sorry for sounding naive, but I always assumed that malloc returns a void pointer which can be cast to an appropriate type. I am not a C programmer and hence would appreciate a little more detail. – user7 Sep 25 '11 at 12:19
  • 5
    @user7: without the #include the C compiler assumes the return value of malloc is an int. – sashang Sep 25 '11 at 12:31
  • 4
    @user7: The void pointer *can* be cast, but it's not needed in C as `void *` can be converted to any other pointer type implicitly. `int *p = malloc(sizeof(int))` works if the proper prototype is in scope and fails if it isn't (because then the result is assumed to be `int`). With the cast, both would compile and the latter would result in errors when `sizeof(int) != sizeof(void *)`. –  Sep 25 '11 at 12:31
  • 2
    @user7 But if you not include `stdlib.h`, the compiler doesn't know `malloc` and neither its return type. So it just assumes `int` as default. – Christian Rau Sep 25 '11 at 12:38
13

This is why you never compile without warnings about missing prototypes.

This is why you never cast the malloc return in C.

The cast is needed for C++ compatibility. There is little reason (read: no reason here) to omit it.

C++ compatibility is not always needed, and in a few cases not possible at all, but in most cases it is very easily achieved.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
  • 28
    Why on earth would I care if my C code is "compatible" with C++? I don't care if it's compatible with perl or java or Eiffel or ... – Stephen Canon Sep 25 '11 at 18:05
  • 5
    If you guarantee somebody down the line isn't going to look at your C code and go, hey I'm going to compile it with a C++ compiler because that should work! – Steven Lu Sep 25 '11 at 18:32
  • 5
    That's cause most C code can be **trivially** made C++ compatible. – curiousguy Sep 26 '11 at 21:22