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