gcc optimization problem related to varargs
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
eglibc (Ubuntu) |
Triaged
|
Wishlist
|
Unassigned | ||
gcc-4.4 (Ubuntu) |
Triaged
|
Wishlist
|
Unassigned |
Bug Description
Binary package hint: gcc-4.4
This is reproducable on Ubuntu lucid (development branch) 10.04
Passing a variable argument list to vsnprintf, of which I take the fifth argument and
print the output to the console. Works without compiler optimization, breaks starting
-O1 through -O3.
[code]
#include <stdio.h>
#include <stdarg.h>
void testfunc(char *string, ...)
{
char buf[256];
va_list arglist;
va_
vsnprintf(buf, sizeof(buf), string, arglist);
puts(buf);
}
int main(int argc, char **argv)
{
testfunc(
}
[/code]
Compile with
g++ -O0 -o test test.cpp
and
g++ -O3 -o test test.cpp
respectively.
Same behaviour when gcc is invoked instead of g++
Expected output:
TEST=Hello
getting instead (with -O1 or higher)
*** invalid %N$ use detected ***
Likely a compiler optimization issue, less likely an internal problem inside glibc. Possibly also a user error.
If it is a user error, glibc should also complain with -O0, right?
This g++ behaviour prevents our application from working on Ubuntu when compiled in release (optimized) mode.
Christian Buchner
Changed in eglibc (Ubuntu): | |
importance: | Undecided → Wishlist |
Changed in gcc-4.4 (Ubuntu): | |
importance: | Undecided → Wishlist |
(I think there's an extra "n" in your first argument to testfunc(), at
least when compared to your expected output.)
Thanks for your report. What's happening here is that, when you pass
five arguments to vsnprintf but only consume the fifth, vsnprintf has no
idea what the types of the other four arguments are. Due to the way
variable-length argument lists work, this means that vsnprintf doesn't
know the position of the fifth argument on the stack.
The reason that it doesn't fail with -O0 is that Ubuntu's gcc sets
-D_FORTIFY_SOURCE=2 at -O2 and higher. When _FORTIFY_SOURCE is not set,
all positional arguments are assumed to be int unless there's evidence
to the contrary. Otherwise, any undefined-type positional arguments
produce this error. (I think %N$ is a GNU extension, so it seems within
its rights to behave this way, and it's not an unreasonable security
check since this can at the very least cause surprising behaviour,
particularly in conjunction with %n.)
Thus, a short-term workaround for you is to build with
-U_FORTIFY_SOURCE, but I think you should also change your application
to avoid using undefined-type positional arguments like this. The two
bugs in Ubuntu here are that (as far as I can see) glibc doesn't
document this behaviour, and that gcc doesn't warn about the risky code.
affects ubuntu/gcc-4.4
status triaged
severity wishlist
affects ubuntu/eglibc
status triaged
severity low