Confused by segfault with legitimate call to strerror(3) on amd64 / sysctl (3) setting `odd' errno's

Nate Eldredge neldredge at math.ucsd.edu
Fri Jan 16 10:33:18 PST 2009


On Fri, 16 Jan 2009, Garrett Cooper wrote:

> On Fri, Jan 16, 2009 at 2:52 AM, Thierry Herbelot
> <thierry.herbelot at free.fr> wrote:
>> Le Friday 16 January 2009, Garrett Cooper a écrit :
>>> On Fri, Jan 16, 2009 at 2:21 AM, Christoph Mallon
>>>
>>> #include <errno.h>
>>> #include <stdio.h>
>>> #include <sys/stat.h>
>>>
>>> int
>>> main()
>>> {
>>>
>>>         struct stat sb;
>>>
>>>         int o_errno;
>>>
>>>         if (stat("/some/file/that/doesn't/exist", &sb) != 0) {
>>>                 o_errno = errno;
>>>                 printf("Errno: %d\n", errno);
>>>                 printf("%s\n", strerror(o_errno));
>>>         }
>>>
>>>         return 0;
>>>
>>> }
>>>
>> with this, it's better on an amd64/ RELENG_7 machine :
>>
>> % diff -ub badfile.c.ori badfile.c
>> --- badfile.c.ori       2009-01-16 11:49:44.778991057 +0100
>> +++ badfile.c   2009-01-16 11:49:03.470465677 +0100
>> @@ -1,6 +1,7 @@
>>  #include <errno.h>
>>  #include <stdio.h>
>>  #include <sys/stat.h>
>> +#include <string.h>
>>
>>  int
>>  main()
>>
>>        Cheers
>>
>>        TfH
>
> That's hilarious -- why does it pass though without issue on x86 though?
> -Garrett

As pointed out, when you don't have a declaration for strerror, it's 
implicitly assumed to return `int'.  This "feature" was widely used in the 
early days of C and so continues to be accepted by compilers, and gcc by 
default doesn't warn about it.

On x86, int and char * are the same size.  So even though the compiler 
thinks strerror is returning an int which is being passed to printf, the 
code it generates is the same as for a char *.  On amd64, int is 32 bits 
but char * is 64.  When the compiler thinks it's using int, it only keeps 
track of the lower 32 bits, and the upper 32 bits get zeroed.  So the 
pointer that printf receives has had its upper 32 bits zeroed, and no 
longer points where it should.  Hence segfault.

Since running on amd64 I've seen a lot of bugs where people carelessly 
assume (perhaps without noticing) that ints and pointers are practically 
interchangeable, which works on x86 and the like but breaks on amd64. 
Variadic functions are special offenders because the compiler can't do 
much type checking.

Pop quiz: which of the following statements is correct?

#include <stdlib.h>
#include <unistd.h>

execl("/bin/sh", "/bin/sh", 0);
execl("/bin/sh", "/bin/sh", NULL);

-- 

Nate Eldredge
neldredge at math.ucsd.edu


More information about the freebsd-hackers mailing list