Bogus longjmp clobber warning?

From: Ian Pilcher (i.pilcher_at_comcast.net)
Date: 02/22/05


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
========================================================================


Relevant Pages

  • Re: event id: 1025
    ... functionality of the system, and is not a cause for concern. ... Event Type: Warning ... Event Source: MSExchangeIS Mailbox Store ... Error: -1102 Warning: fail to apply search optimization to folder (FID ...
    (microsoft.public.exchange.admin)
  • Re: large files: when ubiquitous?
    ... >> functions, that isn't necessary in this case, because the database keys ... > gcc produce a warning because the code is wrong. ... it actually does some of that optimization that can fail. ... > compiler that will notice every possible mistake you could ...
    (comp.os.linux.development.system)
  • Re: unreachable code in vector
    ... > been emitted without optimization. ... The warning _can_ be emitted in a debug build if the unreachability ... Doing a quick test with the current MS compiler on this code, ...
    (microsoft.public.vc.language)
  • Re: Small optimization tips - gcc-4.1 ruby1.9 -O3
    ... optimization tips? ... Compile with gcc-4.1 ... Warning, this may be hazardous for your health, but it works for me. ...
    (comp.lang.ruby)
  • Re: Initializing extern
    ... This provokes a warning in gcc, no warnings in Microsoft C, ... the declaration shall have no initializer for the identifier. ... At file scope, I don't see anything wrong with this code. ...
    (comp.std.c)

Loading