Bogus longjmp clobber warning?
From: Ian Pilcher (i.pilcher_at_comcast.net)
Date: 02/22/05
- Next message: Jan Panteltje: "Re: Microsoft patents 'isnot' operator"
- Previous message: Leon Mergen: "Re: Microsoft patents 'isnot' operator"
- Next in thread: Thomas Richter: "Re: Bogus longjmp clobber warning?"
- Reply: Thomas Richter: "Re: Bogus longjmp clobber warning?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Tue, 22 Feb 2005 14:33:11 -0600
Apologies in advance for the length of this post.
BACKGROUND
I'm working on a multi-threaded server program, and I've been tearing my
hair out over error logging. My goal is to be able to use a construct
such as:
if ((pt_errno = pthread_function(...)) != 0)
{
FOO_LOG(FOO_ERROR, "Error in pthread_function: %s",
FOO_MSG(pt_errno));
return -1;
}
Almost as important, I'd like to minimize the number of errors that can
occur in the error-logging code itself. My initial attempt to write a
thread-safe strerror equivalent got derailed by this; it had so many
pthread calls, each of which could fail, which required logging, etc.,
etc.
I thought that I had found a rather clever solution, using a combination
of macros and setjmp/longjmp:
extern void foo_log_fn(int pri, const char *format, ...);
#define BUFFER_CHUNK 128
const char *foo_msg_fn(int buf_size, char *buf, int errnum,
jmp_buf env)
{
if (buf_size == 0)
{
do
{
assert(buf_size <= INT_MAX - BUFFER_CHUNK);
buf_size += BUFFER_CHUNK;
{
char test_buf[buf_size];
if (strerror_r(errnum, buf, buf_size) == 0)
return buf_size;
}
}
while (errno == ERANGE);
if (errno == EINVAL)
return "INTERNAL ERROR: Invalid error number";
else
return "INTERNAL ERROR: Unexpected strerror_r error";
}
else
{
if (strerror_r(errnum, buf, buf_size) == 0)
return buf;
else
FOO_UNREACHABLE;
}
}
#define FOO_MSG(errnum) foo_msg_fn(buf_size, buf, (errnum), env)
#define FOO_LOG(pri, ...) \
\
do \
{ \
int save_errno = errno; \
int buf_size; \
jmp_buf env; \
\
if ((buf_size = setjmp(env)) == 0) \
{ \
char *buf = NULL; /* suppress warning */ \
foo_log_fn((pri), __VA_ARGS__); \
buf = NULL; /* suppress warning */ \
} \
else \
{ \
char buf[buf_size]; \
errno = save_errno; \
foo_log_fn((pri), __VA_ARGS__); \
buf[0] = '\0'; /* suppress warning */ \
} \
\
errno = save_errno; \
} \
while (0) \
THE PROBLEM
Here is a self-contained usage example, with the macros expanded:
#include <stddef.h>
#include <setjmp.h>
#include <errno.h>
extern void foo_log_fn(int pri, const char *format, ...);
extern int foo_msg_fn(int buf_size, char *buf, int errnum, jmp_buf env);
extern int pthread_rwlock_rdlock(void);
extern int pthread_rwlock_unlock(void);
extern int foo_exit_flag;
int foo_set_exit_flag(void)
{
int rv, pt_errno;
if ((pt_errno = pthread_rwlock_rdlock()) != 0)
{
do
{
int save_errno = errno;
int buf_size;
jmp_buf env;
if ((buf_size = setjmp(env)) == 0)
{
char *buf = NULL;
foo_log_fn(0, "Error: %s",
foo_msg_fn(buf_size, buf, pt_errno, env));
buf = NULL;
}
else
{
char buf[buf_size];
errno = save_errno;
foo_log_fn(0, "Error: %s",
foo_msg_fn(buf_size, buf, pt_errno, env));
buf[0] = '\0';
}
errno = save_errno;
}
while (0);
return -1;
}
rv = foo_exit_flag;
if ((pt_errno = pthread_rwlock_unlock()) != 0) /* **** */
return -1; /* **** */
return rv;
}
If this is compiled without optimization, no warnings are emitted. If
any optimization is turned on, I get a warning that pt_errno might be
clobbered by longjmp. Interestingly, if the second usage of pt_errno
(the lines marked with "/* **** */") is commented out, no warning is
emitted, even with optimization turned on.
The description of longjmp on The Open Group's web site states:
All accessible objects have values, and all other components of the
abstract machine have state (for example, floating-point status flags
and open files), as of the time longjmp() was called, except that
values of objects of automatic storage duration are unspecified if
they meet the following conditions:
* They are local to the function containing the corresponding
setjmp() invocation.
* They do not have volatile-qualified type.
* They are changed between the setjmp() invocation and longjmp()
call.
pt_errno definitely meets the first two criteria, but it just as
definitely doesn't meet the third. So is this:
1. buggy code,
2. code that the GCC optimizer just can't handle, or
3. a spurious warning?
If it's #1, what's the bug? If it's #3, is there a code change to
suppress it or a switch to turn this particular warning off?
If you got all the way down here, thanks!
-- ======================================================================== Ian Pilcher i.pilcher@comcast.net ========================================================================
- Next message: Jan Panteltje: "Re: Microsoft patents 'isnot' operator"
- Previous message: Leon Mergen: "Re: Microsoft patents 'isnot' operator"
- Next in thread: Thomas Richter: "Re: Bogus longjmp clobber warning?"
- Reply: Thomas Richter: "Re: Bogus longjmp clobber warning?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|