[ntp:questions] Ntpd in uninterruptible sleep?

Dave Hart hart at ntp.org
Sat Nov 12 07:41:53 UTC 2011


On Sat, Nov 12, 2011 at 05:52, unruh <unruh at chronyadvocacy.org> wrote:
> On 2011-11-12, Dave Hart <hart at 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


More information about the questions mailing list