Re: Discussion about why GNU/Linux system upgrades cause old programs to break
Arsen Arsenović <[email protected]>
| Newsgroups | gmane.emacs.devel |
|---|---|
| Message-ID | <[email protected]> |
Richard Stallman <[email protected]> writes: > > Again, as an example, it's enough for a pointer to be used as a return > > value on a 64-bit platforms, which are not at all uncommon, or for a > > 'long long' to be used as a return value on any platform, for *visible* > > breakage when such a function is called through an implicit function > > declaration even in the most forgiving of conditions. > > Would you please show an example for the pointer on a 64-bit platform? > And one for `long long'? I'd like to see what sort of usage would > have this problem. The earlier reallocarray example was one such example. But, sure: test.c: #include <stdio.h> int main (void) { /* Cast present due to third subtle issue implicit declarations cause: wrong types being used for vararg arguments. */ printf ("%llu\n", (long long) foo ()); printf ("%s\n", bar ()); } test2.c: #include <string.h> long long foo (void) { return ((long long) -1U) + 1; } const char * bar (void) { return strdup ("hello, world"); } Compile with: gcc -fpermissive test{,2}.c In practice, 999 times out of a thousand, there's a test2.h, that declares: long long foo (void); const char *bar (void); ... that the developer merely forgot to include. On my x86_64 GNU/Linux machine, this manifests as: /tmp$ ./a.out 0 Segmentation fault (core dumped) ./a.out ... where the correct result is: /tmp$ ./a.out 4294967296 hello, world The former failure mode - the wrongful zero extension and thus truncation of the value passed to printf - is significantly more insidious than the latter - a loud crash, because it can be quite difficult to discover and debug. The crash is quite obvious on the other hand. Though note that I only got the crash because I got lucky (the malloc pool could've been below the 4G mark and I wouldn't have been able to get it, and it may even have "worked"). The comment in test.c also alludes to a third issue: implicit function declarations and similar can lead to vararg functions being called incorrectly. This is a special case of wrongful argument promotion, which can also happen if the function being called (rather than a call subexpression of one of its arguments; in the example above printf is 'the function being called', and foo and bar are in the 'call subexpressions' of printf's arguments) is implicitly declared, as in the following: test.c: int main () { foo (-1); } test2.c: #include <stdio.h> void foo (long long x) { printf ("%lld\n", x); } (in this particular example, this is a missing argument promotion, from int to long long) On that same machine, the program above prints 4294967295. Incidentally, this gets worse on 32-bit x86, where arguments are passed on the stack, as one argument being pushed wrong shifts all other arguments (on 64-bit x86, since the first few arguments are in registers, this is a less likely - but not impossible - eventuality; not that this property of the x86 64-bit ABI should be relied on for this purpose). Bugs caused by these mistakes can sneak into builds of programs - old and new - and hide for a really long time, and manifest as very hard to catch miscompilation and, thus, as strange runtime errors. Obviously, all of the above are dumb, constructed examples, but the real world insidious cases boil down to unintentionally instantiating a pattern like those above. Cases of such mistakes far outnumber the cases of intentional use of these features, in my experience. -- Arsen Arsenović
signature.asc
(application/pgp-signature, 288 B)
-----BEGIN PGP SIGNATURE----- iKIEARYKAEoWIQT+4rPRE/wAoxYtYGFSwpQwHqLEkwUCapTD4xsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDIQHGFyc2VuQGFhcnNlbi5tZQAKCRBSwpQwHqLEkzgX AP924Ij/k3bOOGAmCAAJojVnXIbFwCwO1vmEbchToCFrqQD/Wzi1OOLTzeko2/7m hH84y4uX7uazZlkrfG3OJr4g6A4= =uV3o -----END PGP SIGNATURE-----