Segmentation fault in vsnprintf() from /lib64/tls/libc.so.6

S

saumya.agarwal

Hi,

I am executing a piece of code which continually tries to do the
sprintf into the allocated buffer on a 64-bit RedHat linux machine.

Here are the details of the system and the gcc version used -

bash-3.00$ uname -a
Linux saumya.foo.com 2.6.9-5.ELsmp #1 SMP Wed Jan 5 19:29:47 EST 2005
x86_64 x86_64 x86_64 GNU/Linux

bash-3.00$ gcc -v
Reading specs from /usr/lib/gcc/x86_64-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-languages=c,c++,objc,java,f77
--enable-java-awt=gtk --host=x86_64-redhat-linux
Thread model: posix
gcc version 3.4.3 20041212 (Red Hat 3.4.3-9.EL4)

On executing the code (snippet below) I get a segmentation fault at run
time. The code builds fine. The same code runs fine on a 32-bit linux
machine.

if (NULL != *strp) {
for ( ; NULL != *strp; ) {
left = *sizep - len - 1;
if (left > 0) {
result = vsnprintf(&(*strp)[len], left, format, ap);
if ((result != -1) && (result < left)) { //vsnprintf
truncated the output string
break;
}
}
*sizep *= 2;
Renew(*strp, *sizep, char); //reallocate sizep amount of
space to strp
}
}

The crash happens in the second iteration of the for loop. It goes
through fine in the first iteration.

Here is the gdb backtrace -

#0 0x00000037d776fc10 in strlen () from /lib64/tls/libc.so.6
#1 0x00000037d7742b4b in vfprintf () from /lib64/tls/libc.so.6
#2 0x00000037d7761ce4 in vsnprintf () from /lib64/tls/libc.so.6
#3 0x00000000004965a6 in str_vappend (strp=0x7fbfffe790,
sizep=0x7fbfffe788,
format=0x4adf1b "%s /%s HTTP/1.1\r\n", ap=0x7fbfffe7e0) at
str.c:684


Is this a known issue with vsnprintf() on 64-bit linux platforms? Is
there a fix or any workaround available?

Thanks,
saumya
 
I

Insik Park

Hi,

I am executing a piece of code which continually tries to do the
sprintf into the allocated buffer on a 64-bit RedHat linux machine.

Here are the details of the system and the gcc version used -

bash-3.00$ uname -a
Linux saumya.foo.com 2.6.9-5.ELsmp #1 SMP Wed Jan 5 19:29:47 EST 2005
x86_64 x86_64 x86_64 GNU/Linux

bash-3.00$ gcc -v
Reading specs from /usr/lib/gcc/x86_64-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info --enable-shared --enable-threads=posix
--disable-checking --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-languages=c,c++,objc,java,f77
--enable-java-awt=gtk --host=x86_64-redhat-linux
Thread model: posix
gcc version 3.4.3 20041212 (Red Hat 3.4.3-9.EL4)

On executing the code (snippet below) I get a segmentation fault at run
time. The code builds fine. The same code runs fine on a 32-bit linux
machine.

if (NULL != *strp) {
for ( ; NULL != *strp; ) {
left = *sizep - len - 1;
if (left > 0) {
result = vsnprintf(&(*strp)[len], left, format, ap);
if ((result != -1) && (result < left)) { //vsnprintf
truncated the output string
break;
}
}
*sizep *= 2;
Renew(*strp, *sizep, char); //reallocate sizep amount of
space to strp
}
}

The crash happens in the second iteration of the for loop. It goes
through fine in the first iteration.

Here is the gdb backtrace -

#0 0x00000037d776fc10 in strlen () from /lib64/tls/libc.so.6
#1 0x00000037d7742b4b in vfprintf () from /lib64/tls/libc.so.6
#2 0x00000037d7761ce4 in vsnprintf () from /lib64/tls/libc.so.6
#3 0x00000000004965a6 in str_vappend (strp=0x7fbfffe790,
sizep=0x7fbfffe788,
format=0x4adf1b "%s /%s HTTP/1.1\r\n", ap=0x7fbfffe7e0) at
str.c:684


Is this a known issue with vsnprintf() on 64-bit linux platforms? Is
there a fix or any workaround available?

Thanks,
saumya

Excerpt from the man page for vsnfprintf:

The functions vprintf, vfprintf, vsprintf, vsnprintf are
equivalent to
the functions printf, fprintf, sprintf, snprintf, respectively,
except
that they are called with a va_list instead of a variable
number of
arguments. These functions do not call the va_end macro.
Consequently,
the value of ap is undefined after the call. The application
should
call va_end(ap) itself afterwards.
 
C

Chris Torek

On executing the code (snippet below) I get a segmentation fault at run
time. ... The crash happens in the second iteration of the for loop.

if (NULL != *strp) {
for ( ; NULL != *strp; ) {
left = *sizep - len - 1;
if (left > 0) {
result = vsnprintf(&(*strp)[len], left, format, ap);
if ((result != -1) && (result < left)) { //vsnprintf
truncated the output string
break;
}
}
*sizep *= 2;
Renew(*strp, *sizep, char); //reallocate sizep amount of
space to strp
}
}

(I think it is worth pointing out that this code snippet will no
longer compile due to line-wrapping of "//" comments. C89-style
comments survive USENET posting better than these C99-specific
comments.)

You have already gotten a correct answer from (e-mail address removed)
(namely, the v*printf family of functions destroy the "ap" parameter,
at least in theory, and sometimes but not always in practice).

The snippet above does not have enough information to tell whether
this can be fixed without using a new C99 feature. If your function
looks something vaguely like:

void foo(const char *fmt, ...) {
va_list ap;

va_start(ap, fmt);
some_sort_of_loop {
/* BUG HERE - may fail if the loop runs more than once */
result = some_vprintf_function(args, ap);
...
}
va_end(ap);
}

you can rewrite it as:

void foo(const char *fmt, ...) {
va_list ap;

some_sort_of_loop {
va_start(ap, fmt);
result = some_vprintf_function(args, ap);
va_end(ap);
...
}
}

But in general it is better to write a function like foo() in
terms of its "vfoo" counterpart:

void foo(const char *fmt, ...) {
va_list ap;

va_start(fmt, ap);
vfoo(fmt, ap);
va_end(ap);
}

void vfoo(const char *fmt, va_list ap) {

some_sort_of_loop {
/* BUG HERE */
result = some_vprintf_function(args, ap);
...
}
}

In this case, there is no way to "va_end" and "re-va_start" inside
the loop. C99 provides the missing part of the puzzle, using a
macro[%] spelled "va_copy":

void vfoo(const char *fmt, va_list ap) {
va_list copy;

some_sort_of_loop {
va_copy(ap, copy);
result = some_vprintf_function(args, copy);
va_end(copy);
...
}
}

Instead of re-"va_start"-ing, you va_copy the still-valid "ap"
value, then va_end the copy, inside the loop.
 
L

loic-dev

Hello,
I am executing a piece of code which continually tries to do the
sprintf into the allocated buffer on a 64-bit RedHat linux machine.

Here are the details of the system and the gcc version used -

On executing the code (snippet below) I get a segmentation fault at run
time. The code builds fine. The same code runs fine on a 32-bit linux
machine.

This is likely a bug in your code, which didn't pop up on 32-bit
Linux... This is more a matter of luck than code correctness...
if (NULL != *strp) {
for ( ; NULL != *strp; ) {
left = *sizep - len - 1;
if (left > 0) {
result = vsnprintf(&(*strp)[len], left, format, ap);
if ((result != -1) && (result < left)) { //vsnprintf
truncated the output string
break;
}
}
*sizep *= 2;
Renew(*strp, *sizep, char); //reallocate sizep amount of
space to strp
}
}

The crash happens in the second iteration of the for loop. It goes
through fine in the first iteration.

As mentioned by Insik in the man page's excerpt, the ap va_list
variable will have an undefined value after the call to /vsnprintf()/.
Hence, you need to reset the va_list by calling /va_end()/ first and
then /va_start()/ at each iteration of the for(...) loop.

Is this a known issue with vsnprintf() on 64-bit linux platforms? Is
there a fix or any workaround available?

I guess, the easiest workaround available is to fix your code ;-)

Cheers,
Loic.
 
S

saumya.agarwal

Thank you very much! Using va_copy() worked !!

Chris said:
On executing the code (snippet below) I get a segmentation fault at run
time. ... The crash happens in the second iteration of the for loop.

if (NULL != *strp) {
for ( ; NULL != *strp; ) {
left = *sizep - len - 1;
if (left > 0) {
result = vsnprintf(&(*strp)[len], left, format, ap);
if ((result != -1) && (result < left)) { //vsnprintf
truncated the output string
break;
}
}
*sizep *= 2;
Renew(*strp, *sizep, char); //reallocate sizep amount of
space to strp
}
}

(I think it is worth pointing out that this code snippet will no
longer compile due to line-wrapping of "//" comments. C89-style
comments survive USENET posting better than these C99-specific
comments.)

You have already gotten a correct answer from (e-mail address removed)
(namely, the v*printf family of functions destroy the "ap" parameter,
at least in theory, and sometimes but not always in practice).

The snippet above does not have enough information to tell whether
this can be fixed without using a new C99 feature. If your function
looks something vaguely like:

void foo(const char *fmt, ...) {
va_list ap;

va_start(ap, fmt);
some_sort_of_loop {
/* BUG HERE - may fail if the loop runs more than once */
result = some_vprintf_function(args, ap);
...
}
va_end(ap);
}

you can rewrite it as:

void foo(const char *fmt, ...) {
va_list ap;

some_sort_of_loop {
va_start(ap, fmt);
result = some_vprintf_function(args, ap);
va_end(ap);
...
}
}

But in general it is better to write a function like foo() in
terms of its "vfoo" counterpart:

void foo(const char *fmt, ...) {
va_list ap;

va_start(fmt, ap);
vfoo(fmt, ap);
va_end(ap);
}

void vfoo(const char *fmt, va_list ap) {

some_sort_of_loop {
/* BUG HERE */
result = some_vprintf_function(args, ap);
...
}
}

In this case, there is no way to "va_end" and "re-va_start" inside
the loop. C99 provides the missing part of the puzzle, using a
macro[%] spelled "va_copy":

void vfoo(const char *fmt, va_list ap) {
va_list copy;

some_sort_of_loop {
va_copy(ap, copy);
result = some_vprintf_function(args, copy);
va_end(copy);
...
}
}

Instead of re-"va_start"-ing, you va_copy the still-valid "ap"
value, then va_end the copy, inside the loop.
-----
[% At least, the C99 draft I have specifies "a macro". If it is
required to be a macro, you can test for its presence, even in a
compiler that is not yet fully C99-conformant, with "#ifdef".]
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top