Hyrum K. Wright
Thu, 11 Mar 2010 08:53:44 -0800
On Mar 10, 2010, at 5:50 PM, William A. Rowe Jr. wrote: > On 3/10/2010 4:45 PM, Hyrum K. Wright wrote: >> >> Digging deeper, it appears to be an error in apr_vformatter() when parsing >> the format '%lld'. I'm running the tests on Mac OS X where APR_OFF_FMT_T is >> defined as lld, so this format occurs frequently. Consequently, I'm also >> seeing a failure in testfmt at line 63, where the parser is attempting to >> parse APR_OFF_FMT_T, but failing. Can anybody else replicate this bug? > > Yes, this function isn't maintained on Mac OS/X. Which is something > of a surprise, given the number of Mac fans around here! > > The defintions are very clear, %d must handle an (int) sized object, > %ld must handle a (long) sized object, and %lld must handle any > long long int sized object. Patches welcome. > > I plan to patch win32 to accept %I64[ld] because long long type was added > with VS 7.1, while %ll was not added until the more recent Visual Studio 8. > I64d goes further back in time to 6.0 or prior, and is how we specified > the format tag for APR_OFF_T_FMT etc.
Digging even further. It appears that the parser is attempting to match APR_INT64_T_FMT first, which may be of variable size. On OS X, this is defined as 'ld', but the parser mistakenly matches APR_OFF_T_FMT, which is defined as 'lld' for this case. Attached is a patch which adds another block which preemptively matches APR_OFF_T_FMT. I've tested it on OS X and a 64-bit Linux system. -Hyrum
Index: strings/apr_snprintf.c
===================================================================
--- strings/apr_snprintf.c (revision 921388)
+++ strings/apr_snprintf.c (working copy)
@@ -810,10 +810,23 @@ APR_DECLARE(int) apr_vformatter(int (*flush_func)(
adjust_precision = adjust_width = NO;
/*
- * Modifier check. Note that if APR_INT64_T_FMT is "d",
- * the first if condition is never true.
+ * Modifier check. Note that if APR_OFF_T_FMT is "d",
+ * the first if condition is never true, and if APR_INT64_T_FMT
+ * is "d", the second if condition is never true.
*/
- if ((sizeof(APR_INT64_T_FMT) == 4 &&
+ if ((sizeof(APR_OFF_T_FMT) == 4 &&
+ fmt[0] == APR_OFF_T_FMT[0] &&
+ fmt[1] == APR_OFF_T_FMT[1]) ||
+ (sizeof(APR_OFF_T_FMT) == 3 &&
+ fmt[0] == APR_OFF_T_FMT[0]) ||
+ (sizeof(APR_OFF_T_FMT) > 4 &&
+ strncmp(fmt, APR_OFF_T_FMT,
+ sizeof(APR_OFF_T_FMT) - 2) == 0)) {
+ /* Need to account for trailing 'd' and null in sizeof() */
+ var_type = IS_QUAD;
+ fmt += (sizeof(APR_OFF_T_FMT) - 2);
+ }
+ else if ((sizeof(APR_INT64_T_FMT) == 4 &&
fmt[0] == APR_INT64_T_FMT[0] &&
fmt[1] == APR_INT64_T_FMT[1]) ||
(sizeof(APR_INT64_T_FMT) == 3 &&