On Tue, Mar 19, 2019 at 07:30:09PM -0500, William A Rowe Jr wrote:
> According to my observations, apr_time_t should match the APR_TIME_T_FMT
> token in every case. Please inspect that line of httpd code to see how some
> non-apr_time_t value was passed in APR_TIME_T_FMT formatting.

Indeed, this value is not a time_t, it's an apr_int64_t, i.e. long.

The problematic format string is in this bit code from proxy_util.c
starting at line 3176:

                ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959)       
                    "ap_proxy_connect_backend disabling worker for (%s) for %" 
                    APR_TIME_T_FMT "s",
                    worker->s->hostname_ex, apr_time_sec(worker->s->retry)); 

This assumes apr_time_sec returns apr_time_t, but in fact apr_time_sec is
a macro. So the expression returns the type of the variable passed in,
which in this case is apr_interval_time_t.

from mod_proxy.h:

    apr_interval_time_t retry;   /* retry interval */

and from apr_time.h:

  typedef apr_int64_t apr_interval_time_t;

So we need this patch:

--- modules/proxy/proxy_util.c  (revision 1855812)                             
+++ modules/proxy/proxy_util.c  (working copy)                                 
@@ -3175,7 +3175,7 @@ PROXY_DECLARE(int) ap_proxy_connect_backend(const        
                 worker->s->status |= PROXY_WORKER_IN_ERROR;                   
                 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00959)      
                     "ap_proxy_connect_backend disabling worker for (%s) for %"
-                    APR_TIME_T_FMT "s",                                       
+                    APR_INT64_T_FMT "s",                                      
                     worker->s->hostname_ex, apr_time_sec(worker->s->retry));  
             }
         }


But even with this patch, the error remains:
[[[
cc1: warnings being treated as errors
proxy_util.c: In function 'ap_proxy_connect_backend':                          
proxy_util.c:3179: warning: format '%ld' expects type 'long int', but argument 
9 has type 'long long int'
]]]

This was confusing me at first: apr_int64_t is a long, so why does the
compiler see a 'long long' type?

I then realized that httpd's configure script was picking up the ancient
GPLv2 version of gcc which is still installed even though it's rarely used.
I forced my httpd turnk build to use clang and got a more informative warning:

[[[
proxy_util.c:3179:45: warning: format specifies type 'long' but the argument has
      type 'long long' [-Wformat]
                    worker->s->hostname_ex, apr_time_sec(worker->s->retry));   
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    
/tmp/apr/include/apr-2/apr_time.h:63:28: note: expanded from macro             
      'apr_time_sec'
#define apr_time_sec(time) ((time) / APR_USEC_PER_SEC) 
]]]

Turns out the remaining problem lies in the definition of APR_USEC_PER_SEC:

/** number of microseconds per second */
#define APR_USEC_PER_SEC APR_TIME_C(1000000)

Following the chain of definitions, we arrive at a constant suffixed with LL:

/* Mechanisms to properly type numeric literals */                             
#define APR_INT64_C(val) INT64_C(val)

/usr/include/sys/stdint.h:#define       INT64_C(_c)             __CONCAT(_c, LL)
/usr/include/sys/stdint.h:#define       UINT64_C(_c)            __CONCAT(_c, 
ULL)

Yet APR blindly assumes that INT64_C results in constants suffixed with
just one L (long)!

A similar problem exists for time_t, by the way.
OpenBSD defines time_t as 'long long', but apr_time_t is defined as 'long'
in a 64 bit environment. This means that apr_time_t won't have the same type
as time_t on 64 bit OpenBSD architectures. On 32 bit platforms, apr_time_t
and time_t are both defined as 'long long'.
OpenBSD's convention is to use '%lld' for printing time_t, regardless
of the CPU platform -- all platforms use 64-bit time_t since OpenBSD 5.5:
https://www.openbsd.org/lyrics.html#55

Reply via email to