On Sat, Nov 12, 2011 at 05:52, unruh <un...@chronyadvocacy.org> wrote: > On 2011-11-12, Dave Hart <h...@ntp.org> wrote: >> Excellent. I assume the stack trace is from ntpd 4.2.6p3. I think >> you've found a bug in your system's libc dtoa() exposed by its >> snprintf(s, " %.2f", ...). I believe you will not be able to >> reproduce the bug using 4.2.7, as that version of ntpd uses >> C99-snprintf [1] if the system snprintf() is not C99-compliant. >> C99-snprintf's rpl_vsnprintf() does not use dtoa(), it hand-rolls the > > That seems like a step backwards. Having a program write its own code > for such elementary routines is a recipie for choas. Rather, if there > is a bug in libc it should be fixed. > [...] > Why was that "roll your own dtoa" added to ntpd?
It appears you misunderstood me. That's ok, I was confused myself, as I'll mention in a separate followup. ntpd does not roll its own dtoa(). ntpd does not roll its own [v]snprintf() either, it uses the system/compiler provided snprintf() _unless_ that snprintf() is not C99-compliant, in which case it uses a 3rd-party implementation of [v]snprintf, namely, C99-snprintf (that's a proper name, as I hoped the footnoted URL in my prior message would make clear). Why? ntpd is highly portable code. It is not simply a component of Linux. It is portable source code that can be built on any reasonably POSIX-like system with an ANSI C compiler, as well as a couple of not-particularly-POSIX-like targets, notably Windows. (ANSI standard C, the first cross-vendor standard for C, is also confusingly referred to variously as ISO C, C89, and C90 -- boy do I get sick of pointing that out.) ANSI C is nearly universally supported, and has been for years. ntpd 4.2.4 and earlier could be built on classic, pre-ANSI C compilers, because until the last few years, there were still interesting systems in wide use that didn't support ANSI C out of the box, and requiring users to integrate gcc for their system would have made it impractical to use up-to-date ntpd. Similarly, today, there remain widely deployed systems which do not support newer C standards. Portability to those systems mandates that if we want to rely on improvements to the C runtime since ANSI, we must provide a fallback implementation of the newer spec. Why do we want a C99-compliant [v]snprintf()? Well, ANSI C snprintf() is just horrible if the buffer is not large enough to hold the requested output. If the requested output fits in the buffer without zero termination, the return value is the number of characters output, excluding zero termination. If the buffer has room for one additional character, the string is zero terminated. In either case the return value is the same. So, you may end up with an unterminated string which will result in later buffer read overrun when it is used, and you can't tell. But wait, there's more. If the output excluding terminator does not fit in the given buffer, the return value is -1, so the caller has no way to determine how large a buffer would be needed. Recognizing the ANSI spec is disastrous and has led to innumerable bugs and exploits, C99 changes snprintf() in a backward-incompatible way, requiring (a) the output is always terminated and (b) the return value is always the number of characters that would be output (excluding terminator) if there were sufficient room. Using ANSI C snprintf() while not inviting bugs and exploits means ensuring every use of snprintf() is followed by explicit code to set the last character of the buffer to zero to terminate it, or alternatively to audit every use of [v]snprintf() to ensure lack of termination can not occur. Neither option is acceptable, so as with a number of other less-than-universally-provided routines (MD5 hash, strdup, strlcpy, strlcat), ntpd source includes fallback implementations. System-provided routines are used if available, avoiding binary bloat from duplication and limiting the use of less-widely-tested code to the systems that need it. C99-snprintf's rpl_vsnprintf() code (distributed in libntp/snprintf.c) does not rely on any other C runtime functions such as dtoa(). If you believe that to be a bad design, please provide a better [v]snprintf() implementation under a BSD/MIT-style license that does not place requirements on NTP's documentation, and I'll be happy to consider using it for the fallback implementation used with Microsoft's C runtime and others lacking C99 snprintf() semantics. Cheers, Dave Hart _______________________________________________ questions mailing list questions@lists.ntp.org http://lists.ntp.org/listinfo/questions