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