Segmentation fault on 64 bit

L

Linny

Hi All,
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.

But when I include the system file <malloc.h> , the code executes fine
on both the systems.

#include <stdio.h>
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);
strcpy(mystring2, "bar\n\0");
printf("%s%s", mystring1, mystring2);

return 0;
}

Output on 32 bit
foo bar

I'll tried to debug it but was unable to target the cause. pasting a
clip of the gdb output.

Program received signal SIGSEGV, Segmentation fault.
0x800003ffff743ac8 in strlen+0x10 () from /usr/lib/pa20_64/libc.2
(gdb) bt
#0 0x800003ffff743ac8 in strlen+0x10 () from /usr/lib/pa20_64/libc.2
#1 0x4000000000001f08 in main (argc=1, argv=0x800003ffff7f07a0) at
test1.c:8

I assume it is to do with the malloc argument 10 which is typecasted to
long but unable to reason out. Can anyone explain the details?
 
C

Chris Dollin

Linny wrote:
=
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.

But when I include the system file <malloc.h> ,

the code executes fine
on both the systems.

#include <stdio.h>
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);

BOOOOOOOOOOOOM.

What did you do? You did not have a declaration of `malloc`
in scope. The compiler had to declare it for you. It gave
it a return-type of `int`, as it had to. The cast to char*
conceals this.

My bet is that the calling conventions are different on
the 64-bit implementation and the 32-bit implementation,
and what happens is that the 64-bit address gets
passed back in a different register than an int would,
so the code casts garbage to char*. (Or maybe the
result gets truncated to 32 bits, or something. Who
knows?)
I assume it is to do with the malloc argument 10 which is typecasted to
long but unable to reason out. Can anyone explain the details?

No, it's because the compiler was first misled and then
prevented from complaining. The code was wrong to start with,
and what you're seeing is the /reason/ why it's wrong.

#include <stdlib.h>

...
char *mystring2 = malloc( 10 * sizeof( *mystring2 ) );
...

would fix it.
 
R

Richard Heathfield

Linny said:
Hi All,
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.

You failed to provide a prototype for malloc, which is not a function
returning int, so the behaviour is undefined. Your compiler would have
warned you, but you stopped it by (pointlessly) casting malloc's result.

Your problem is explained in some detail in my essay on casting, which you
can find at http://www.cpax.org.uk/prg/writings/casting.php
But when I include the system file <malloc.h> , the code executes fine
on both the systems.

What you really want is a proper prototype for malloc, which you can get by
including the standard system header, <stdlib.h> - and, while you're about
it, why not add <string.h> for strcpy?

Headers are not decorative! They are provided for a purpose. Make sure you
use the ones you need.
#include <stdio.h>
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);
strcpy(mystring2, "bar\n\0");
printf("%s%s", mystring1, mystring2);

return 0;
}

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = malloc(sizeof *mystring2 * 10);
if(mystring2 != NULL)
{
strcpy(mystring2, "bar\n");
printf("%s%s", mystring1, mystring2);

free(mystring2);
}

return 0;
}

me@here:~/scratch> make
gcc -W -Wall -ansi -pedantic -Wformat-nonliteral -Wcast-align
-Wpointer-arith -Wbad-function-cast -Wmissing-prototypes
-Wstrict-prototypes -Wmissing-declarations -Winline -Wundef
-Wnested-externs -Wcast-qual -Wshadow -Wconversion -Wwrite-strings
-Wno-conversion -ffloat-store -O2 -g -pg -c -o foo.o foo.c
foo.c: In function `main':
foo.c:5: warning: unused parameter `argc'
foo.c:5: warning: unused parameter `argv'
gcc -W -Wall -ansi -pedantic -Wformat-nonliteral -Wcast-align
-Wpointer-arith -Wbad-function-cast -Wmissing-prototypes
-Wstrict-prototypes -Wmissing-declarations -Winline -Wundef
-Wnested-externs -Wcast-qual -Wshadow -Wconversion -Wwrite-strings
-Wno-conversion -ffloat-store -O2 -g -pg -o foo foo.o -lm
me@here:~/scratch> ./foo
foobar
 
?

=?ISO-8859-1?Q?=22Nils_O=2E_Sel=E5sdal=22?=

Linny said:
Hi All,
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.

But when I include the system file <malloc.h> , the code executes fine
on both the systems.

#include <stdio.h>
You forgot to include the header file for malloc.
I'll leave it up to you to find which one you need - it
is not malloc.h though.
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);
This cast to a char *is not needed, and might supress a warning
you should care about.

sizeof(char) is always one, and there isn't any justification
for the cast to long. Don't throw around casts unless you know
exactly why.
strcpy(mystring2, "bar\n\0");
String literals are already null terminated, no need
to add another one in this case.
printf("%s%s", mystring1, mystring2);

return 0;
}

Output on 32 bit
[snip]
 
T

Tak-Shing Chan

Linny said:
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.

But when I include the system file <malloc.h> ,

the code executes fine
on both the systems.

#include <stdio.h>
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);

BOOOOOOOOOOOOM.

What did you do? You did not have a declaration of `malloc`
in scope. The compiler had to declare it for you. It gave
it a return-type of `int`, as it had to. The cast to char*
conceals this.

My bet is that the calling conventions are different on
the 64-bit implementation and the 32-bit implementation,
and what happens is that the 64-bit address gets
passed back in a different register than an int would,
so the code casts garbage to char*. (Or maybe the
result gets truncated to 32 bits, or something. Who
knows?)
I assume it is to do with the malloc argument 10 which is typecasted to
long but unable to reason out. Can anyone explain the details?

No, it's because the compiler was first misled and then
prevented from complaining. The code was wrong to start with,
and what you're seeing is the /reason/ why it's wrong.

#include <stdlib.h>

...
char *mystring2 = malloc( 10 * sizeof( *mystring2 ) );
...

would fix it.

The OP has also forgotten to include <string.h> before
invoking strcpy(), which is UB.

Tak-Shing
 
R

Richard Heathfield

Chris Dollin said:
Linny wrote:
=

Indeed.

What did you do? You did not have a declaration of `malloc`
in scope. The compiler had to declare it for you. It gave
it a return-type of `int`, as it had to. The cast to char*
conceals this.

We keep getting told by casting advocates that this never happens, but it's
not the first time such a report (of it actually happening) has reached
comp.lang.c.

Unnecessary casting - Just Say No.
 
J

Jens Thoms Toerring

Linny said:
I am pasting a piece of code which executes fine on 32 bit system but
fails with a segmentation fault when compiled 64 bit compiler.I am
using a HP-UX C compiler on PA-RISC system. This code was picked up
from a document mentioning portability issues from 32 to 64 bit
systems.
But when I include the system file <malloc.h> , the code executes fine
on both the systems.

#include <stdio.h>
int main(int argc, char **argv)
{
char mystring1[10] = "foo";
char *mystring2;
mystring2 = (char *)malloc(sizeof(char)*(long)10);
strcpy(mystring2, "bar\n\0");
printf("%s%s", mystring1, mystring2);
return 0;
}
Output on 32 bit
foo bar

Obviously a (char) pointer can't be stored in an int on the system
where you you get the segmentation fault. Without the inclusion of
<stdlib.h> (or mayby <malloc.h>) the compiler has to assume that
malloc() returns an int (since it doesn't have any other information)
and thus what malloc() returns gets converted to an int to fit into
memory sufficient for an int, if necessary truncating the value. This
value then gets, due to your cast, converted to a char pointer. But
if a char pointer doesn't fit into an int, the result of malloc() has
already been truncated beyond repair and casting back to a char poin-
ter can't undo the damage. If you then use this broken pointer every-
thing can happen because you then try to access memory you don't own.

What you should take from that experience is to never cast the return
value of malloc() etc. If the compiler gives you a warning about
assigning an int to a pointer then chances are high that you forgot
to include <stdlib.h>. The solution then is *not* to cast the return
value (that can break things as you just have found out) but to in-
clude <stdlib.h> in order tell the compiler what return type malloc()
has and thus to enable it to create correct code.

Regards, Jens

BTW: 'malloc(sizeof(char)*(long)10)' is pretty useless, a simple
'malloc(10)' will do nicely - sizeof(char) is always 1 and
if you really want to insure that 10 is a long than write
'10L'. On the other hand, malloc() expects a size_t argument,
which is an unsigned integral value, and you can't know if
this is an unsigned int or an unsigned long. You better let
the rules for integral argument promotion and convertion do
the job for you.
 
R

Richard Heathfield

Jens Thoms Toerring said:
Don't use <malloc.h> but <stdlib.h>
Right...

- that's the file where, according
to the C standard, malloc() and friends are defined.

....and wrong. They are declared in <stdlib.h>, but not defined there. They
are defined in the library source (which need not be available at compile
time, of course, as long as the standard library is there for linking
purposes).

BTW: 'malloc(sizeof(char)*(long)10)' is pretty useless, a simple
'malloc(10)' will do nicely - sizeof(char) is always 1

True. But despite this, char *p = malloc(n * sizeof *p) is a good template,
even though sizeof *p is known to be 1. If, later on in the development
cycle, p is changed to, say, wchar_t *, the code survives the change
without itself having to be modified.
 
P

pete

Tak-Shing Chan wrote:
The OP has also forgotten to include <string.h> before
invoking strcpy(), which is UB.

Undefined only in C99.

ISO/IEC 9899: 1990
6.3.2.2 Function calls
Semantics
If the expression that precedes
the parenthesized argument list in a function call
consists solely of an identifier,
and if no declaration is visible for this identifier,
the identifier is implicitly declared exactly as if,
in the innermost block containing the function call,
the declaration

extern int identifier ();

appeared.

7.1.7 Use of library functions

Provided that a library function can be declared
without reference to any type defined in a header,
it is also permissible to declare the function,
either explicitly or implicitly,
and use it without including its associated header.

7.11.2.3 The strcpy function

char *strcpy(char *s1, const char *s2);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,175
Latest member
Vinay Kumar_ Nevatia
Top