[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Implicition declarations of functions and bugs



"David Mosberger-Tang" <David.Mosberger@acm.org> writes:

> Samuel,
>
> You're missing the point of the check-implicit-pointer-functions
> script.  Its purposes is not to "grep for warnings" but instead to
> look for pairs of warnings that are *guaranteed* to cause crashes on
> 64-bit machines.  gcc -Wall normally spits out tons of spurious
> warnings for 64-bit dirty code, but most of those warnings are just
> noise.  The problem with gcc-4.0 warnings is that you can't
> distinguish between harmless implicit function declarations and ones
> that need to be flagged.  Example:
>
> $ cat t.c
> char *
> foo (char *str)
> {
>   return strdup (str);
> }
>
> enum e_t { a, b };
>
> enum e_t
> bar (char *str)
> {
>   return strlen (str);
> }
> $ gcc-3.3 -c -g -O -Wall t.c
> t.c: In function `foo':
> t.c:4: warning: implicit declaration of function `strdup'
> t.c:4: warning: return makes pointer from integer without a cast

(all asm is from amd64)

0000000000400500 <foo>:
  400500:       48 83 ec 08             sub    $0x8,%rsp
  400504:       31 c0                   xor    %eax,%eax
  400506:       e8 d5 fe ff ff          callq  4003e0 <strdup@plt>
  40050b:       48 83 c4 08             add    $0x8,%rsp
  40050f:       48 98                   cltq   
  400511:       c3                      retq   

The return value of strdup is passed back unaltered. No crash.

> t.c: In function `bar':
> t.c:12: warning: implicit declaration of function `strlen'
> $ gcc-4.0 -c -g -O -Wall t.c
> t.c: In function 'foo':
> t.c:4: warning: implicit declaration of function 'strdup'
> t.c:4: warning: incompatible implicit declaration of built-in function 'strdup'

00000000004004b0 <foo>:
  4004b0:       e9 2b ff ff ff          jmpq   4003e0 <strdup@plt>

Same thing but gcc detected tail recursion and just jumps into strdup.

> t.c: In function 'bar':
> t.c:12: warning: implicit declaration of function 'strlen'
> t.c:12: warning: incompatible implicit declaration of built-in function 'strlen'
>
> As you can see, the gcc-4.0 warnings are inferior because they
> provides no way to distinguish the "foo" vs. "bar" case, even though
> they are qualitatively different.
>
>   --david

They aren't different as both mean data loss if the implicit types
would be used as such:

int strdup(char *s); vs. char *strdup(const char *s);

Conversion between int and pointer loose the upper 32bit of the
address.


int strlen(char *s); vs. size_t strlen(const char *s);

Conversion between 32bit int to 64bit int looses the top 32bit of the
size. This usualy goes unnoticed since strings usualy are longer than
2GB. But think about lseek() on files > 2GB. This is just as dangerous.




I actualy had to think hard to come up with an example of an implicit
function declaration doing the wrong thing on amd64. For an implicit
function declaration to fail the binary data passed to the function
(or back) would have to differ in format. And the format is mostly
just a register.

Case one: Custom function returning a double

// double myfabs(double x);
double foo(double x) {
  return myfabs(x);
}

Without prototype:
foo.c:2: warning: implicit declaration of function 'myfabs'
0000000000000000 <foo>:
   0:   48 83 ec 08             sub    $0x8,%rsp
   4:   b8 01 00 00 00          mov    $0x1,%eax
   9:   e8 00 00 00 00          callq  e <foo+0xe>
   e:   f2 0f 2a c0             cvtsi2sd %eax,%xmm0
  12:   48 83 c4 08             add    $0x8,%rsp
  16:   c3                      retq   

With prototype:
0000000000000000 <foo>:
   0:   e9 00 00 00 00          jmpq   5 <foo+0x5>


Case two: Overloading an implicit function
// int fabs(double x);
int foo(double x) {
  return fabs(x);
}

Without prototype:
foo.c:3: warning: implicit declaration of function 'fabs'
foo.c:3: warning: incompatible implicit declaration of built-in function 'fabs'

0000000000000000 <foo>:
   0:   66 0f 54 05 00 00 00    andpd  0(%rip),%xmm0        # 8 <foo+0x8>
   7:   00 
   8:   f2 0f 2c c0             cvttsd2si %xmm0,%eax
   c:   c3                      retq   

With prototype:
foo.c:1: warning: conflicting types for built-in function 'fabs'

0000000000000000 <foo>:
   0:   e9 00 00 00 00          jmpq   5 <foo+0x5>

In both cases the code does the wrong thing without prototype. In the
first case the int register will be returned instead of the float
register giving a random result. In the second case the overloaded
function isn't even called.

So the gcc-4.0 warning about built-in function actualy servers a very
important function: It warns when gcc does not call the (potentialy)
users function. I've seen enough code that define their own fabs,
strnlen, ....

MfG
        Goswin

PS: Systems that pass arguments on the stack are far more vulnerable
to implicit functions.



Reply to: