diff options
author | Cy Schubert <cy@FreeBSD.org> | 2020-06-24 01:51:05 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2020-06-24 01:51:05 +0000 |
commit | 767173cec2b2041e1f847bc8896092f9c1481242 (patch) | |
tree | f13382cc9f11300231085767f92ffc15a98f8516 /contrib/ntp/libntp | |
parent | 28e002cd3e8a3c40c34a41f8b1ac624d334e4b9a (diff) | |
parent | 3914721463f70500ecc1f59312b122d8788465cf (diff) |
MFV r362565:
Update 4.2.8p14 --> 4.2.8p15
Summary: Systems that use a CMAC algorithm in ntp.keys will not release
a bit of memory on each packet that uses a CMAC keyid, eventually causing
ntpd to run out of memory and fail. The CMAC cleanup from
https://bugs.ntp.org/3447, part of ntp-4.2.8p11, introduced a bug whereby
the CMAC data structure was no longer completely removed.
MFC after: 3 days
Security: NTP Bug 3661
Notes
Notes:
svn path=/head/; revision=362568
Diffstat (limited to 'contrib/ntp/libntp')
-rw-r--r-- | contrib/ntp/libntp/a_md5encrypt.c | 2 | ||||
-rw-r--r-- | contrib/ntp/libntp/decodenetnum.c | 200 | ||||
-rw-r--r-- | contrib/ntp/libntp/recvbuff.c | 204 | ||||
-rw-r--r-- | contrib/ntp/libntp/strdup.c | 34 | ||||
-rw-r--r-- | contrib/ntp/libntp/timexsup.c | 7 |
5 files changed, 288 insertions, 159 deletions
diff --git a/contrib/ntp/libntp/a_md5encrypt.c b/contrib/ntp/libntp/a_md5encrypt.c index 8c046f4e93c9..57100de3a86e 100644 --- a/contrib/ntp/libntp/a_md5encrypt.c +++ b/contrib/ntp/libntp/a_md5encrypt.c @@ -93,7 +93,7 @@ make_mac( } cmac_fail: if (ctx) - CMAC_CTX_cleanup(ctx); + CMAC_CTX_free(ctx); } else # endif /*ENABLE_CMAC*/ diff --git a/contrib/ntp/libntp/decodenetnum.c b/contrib/ntp/libntp/decodenetnum.c index 35e839aafb09..8ff67625202c 100644 --- a/contrib/ntp/libntp/decodenetnum.c +++ b/contrib/ntp/libntp/decodenetnum.c @@ -13,106 +13,152 @@ #include "ntp.h" #include "ntp_stdlib.h" -#include "ntp_assert.h" -#define PORTSTR(x) _PORTSTR(x) -#define _PORTSTR(x) #x -static int -isnumstr( - const char *s +/* If the given string position points to a decimal digit, parse the + * number. If this is not possible, or the parsing did not consume the + * whole string, or if the result exceeds the maximum value, return the + * default value. + */ +static unsigned long +_num_or_dflt( + char * sval, + unsigned long maxval, + unsigned long defval ) { - while (*s >= '0' && *s <= '9') - ++s; - return !*s; + char * ep; + unsigned long num; + + if (!(sval && isdigit(*(unsigned char*)sval))) + return defval; + + num = strtoul(sval, &ep, 10); + if (!*ep && num <= maxval) + return num; + + return defval; +} + +/* If the given string position is not NULL and does not point to the + * terminator, replace the character with NUL and advance the pointer. + * Return the resulting position. + */ +static inline char* +_chop( + char * sp) +{ + if (sp && *sp) + *sp++ = '\0'; + return sp; +} + +/* If the given string position points to the given char, advance the + * pointer and return the result. Otherwise, return NULL. + */ +static inline char* +_skip( + char * sp, + int ch) +{ + if (sp && *(unsigned char*)sp == ch) + return (sp + 1); + return NULL; } /* * decodenetnum convert text IP address and port to sockaddr_u * - * Returns 0 for failure, 1 for success. + * Returns FALSE (->0) for failure, TRUE (->1) for success. */ int decodenetnum( const char *num, - sockaddr_u *netnum + sockaddr_u *net ) { - static const char * const servicename = "ntp"; - static const char * const serviceport = PORTSTR(NTP_PORT); + /* Building a parser is more fun in Haskell, but here we go... + * + * This works through 'inet_pton()' taking the brunt of the + * work, after some string manipulations to split off URI + * brackets, ports and scope identifiers. The heuristics are + * simple but must hold for all _VALID_ addresses. inet_pton() + * will croak on bad ones later, but replicating the whole + * parser logic to detect errors is wasteful. + */ - struct addrinfo hints, *ai = NULL; - int err; - const char *host_str; - const char *port_str; - char *pp; - char *np; - char nbuf[80]; - - REQUIRE(num != NULL); - - if (strlen(num) >= sizeof(nbuf)) { - printf("length error\n"); + sockaddr_u netnum; + char buf[64]; /* working copy of input */ + char *haddr=buf; + unsigned int port=NTP_PORT, scope=0; + unsigned short afam=AF_UNSPEC; + + /* copy input to working buffer with length check */ + if (strlcpy(buf, num, sizeof(buf)) >= sizeof(buf)) return FALSE; - } - port_str = servicename; - if ('[' != num[0]) { - /* - * to distinguish IPv6 embedded colons from a port - * specification on an IPv4 address, assume all - * legal IPv6 addresses have at least two colons. - */ - pp = strchr(num, ':'); - if (NULL == pp) - host_str = num; /* no colons */ - else if (NULL != strchr(pp + 1, ':')) - host_str = num; /* two or more colons */ - else { /* one colon */ - strlcpy(nbuf, num, sizeof(nbuf)); - host_str = nbuf; - pp = strchr(nbuf, ':'); - *pp = '\0'; - port_str = pp + 1; + /* Identify address family and possibly the port, if given. If + * this results in AF_UNSPEC, we will fail in the next step. + */ + if (*haddr == '[') { + char * endp = strchr(++haddr, ']'); + if (endp) { + port = _num_or_dflt(_skip(_chop(endp), ':'), + 0xFFFFu, port); + afam = strchr(haddr, ':') ? AF_INET6 : AF_INET; } } else { - host_str = np = nbuf; - while (*++num && ']' != *num) - *np++ = *num; - *np = 0; - if (']' == num[0] && ':' == num[1] && '\0' != num[2]) - port_str = &num[2]; + char *col = strchr(haddr, ':'); + char *dot = strchr(haddr, '.'); + if (col == dot) { + /* no dot, no colon: bad! */ + afam = AF_UNSPEC; + } else if (!col) { + /* no colon, only dot: IPv4! */ + afam = AF_INET; + } else if (!dot || col < dot) { + /* no dot or 1st colon before 1st dot: IPv6! */ + afam = AF_INET6; + } else { + /* 1st dot before 1st colon: must be IPv4 with port */ + afam = AF_INET; + port = _num_or_dflt(_chop(col), 0xFFFFu, port); + } } - if ( ! *host_str) + + /* Since we don't know about additional members in the address + * structures, we wipe the result buffer thoroughly: + */ + memset(&netnum, 0, sizeof(netnum)); + + /* For AF_INET6, evaluate and remove any scope suffix. Have + * inet_pton() do the real work for AF_INET and AF_INET6, bail + * out otherwise: + */ + switch (afam) { + case AF_INET: + if (inet_pton(afam, haddr, &netnum.sa4.sin_addr) <= 0) + return FALSE; + netnum.sa4.sin_port = htons((unsigned short)port); + break; + + case AF_INET6: + scope = _num_or_dflt(_chop(strchr(haddr, '%')), 0xFFFFFFFFu, scope); + if (inet_pton(afam, haddr, &netnum.sa6.sin6_addr) <= 0) + return FALSE; + netnum.sa6.sin6_port = htons((unsigned short)port); + netnum.sa6.sin6_scope_id = scope; + break; + + case AF_UNSPEC: + default: return FALSE; - if ( ! *port_str) - port_str = servicename; - - ZERO(hints); - hints.ai_flags |= Z_AI_NUMERICHOST; - if (isnumstr(port_str)) - hints.ai_flags |= Z_AI_NUMERICSERV; - err = getaddrinfo(host_str, port_str, &hints, &ai); - /* retry with default service name if the service lookup failed */ - if (err == EAI_SERVICE && strcmp(port_str, servicename)) { - hints.ai_flags &= ~Z_AI_NUMERICSERV; - port_str = servicename; - err = getaddrinfo(host_str, port_str, &hints, &ai); } - /* retry another time with default service port if the service lookup failed */ - if (err == EAI_SERVICE && strcmp(port_str, serviceport)) { - hints.ai_flags |= Z_AI_NUMERICSERV; - port_str = serviceport; - err = getaddrinfo(host_str, port_str, &hints, &ai); - } - if (err != 0) - return FALSE; - - INSIST(ai->ai_addrlen <= sizeof(*netnum)); - ZERO(*netnum); - memcpy(netnum, ai->ai_addr, ai->ai_addrlen); - freeaddrinfo(ai); + /* Collect the remaining pieces and feed the output, which was + * not touched so far: + */ + netnum.sa.sa_family = afam; + memcpy(net, &netnum, sizeof(netnum)); return TRUE; } diff --git a/contrib/ntp/libntp/recvbuff.c b/contrib/ntp/libntp/recvbuff.c index 573fdb2f9209..5855ec2147ac 100644 --- a/contrib/ntp/libntp/recvbuff.c +++ b/contrib/ntp/libntp/recvbuff.c @@ -11,6 +11,15 @@ #include "recvbuff.h" #include "iosignal.h" +#if (RECV_INC & (RECV_INC-1)) +# error RECV_INC not a power of 2! +#endif +#if (RECV_BATCH & (RECV_BATCH - 1)) +#error RECV_BATCH not a power of 2! +#endif +#if (RECV_BATCH < RECV_INC) +#error RECV_BATCH must be >= RECV_INC! +#endif /* * Memory allocation @@ -21,6 +30,8 @@ static u_long volatile total_recvbufs; /* total recvbufs currently in use */ static u_long volatile lowater_adds; /* number of times we have added memory */ static u_long volatile buffer_shortfall;/* number of missed free receive buffers between replenishments */ +static u_long limit_recvbufs; /* maximum total of receive buffers */ +static u_long emerg_recvbufs; /* emergency/urgent buffers to keep */ static DECL_FIFO_ANCHOR(recvbuf_t) full_recv_fifo; static recvbuf_t * free_recv_list; @@ -33,11 +44,16 @@ static recvbuf_t * free_recv_list; * short a time as possible */ static CRITICAL_SECTION RecvLock; -# define LOCK() EnterCriticalSection(&RecvLock) -# define UNLOCK() LeaveCriticalSection(&RecvLock) +static CRITICAL_SECTION FreeLock; +# define LOCK_R() EnterCriticalSection(&RecvLock) +# define UNLOCK_R() LeaveCriticalSection(&RecvLock) +# define LOCK_F() EnterCriticalSection(&FreeLock) +# define UNLOCK_F() LeaveCriticalSection(&FreeLock) #else -# define LOCK() do {} while (FALSE) -# define UNLOCK() do {} while (FALSE) +# define LOCK_R() do {} while (FALSE) +# define UNLOCK_R() do {} while (FALSE) +# define LOCK_F() do {} while (FALSE) +# define UNLOCK_F() do {} while (FALSE) #endif #ifdef DEBUG @@ -76,33 +92,52 @@ initialise_buffer(recvbuf_t *buff) } static void -create_buffers(int nbufs) +create_buffers( + size_t nbufs) { +# ifndef DEBUG + static const u_int chunk = RECV_INC; +# else + /* Allocate each buffer individually so they can be free()d + * during ntpd shutdown on DEBUG builds to keep them out of heap + * leak reports. + */ + static const u_int chunk = 1; +# endif + register recvbuf_t *bufp; - int i, abuf; + u_int i; + size_t abuf; + if (limit_recvbufs <= total_recvbufs) + return; + abuf = nbufs + buffer_shortfall; buffer_shortfall = 0; -#ifndef DEBUG - bufp = eallocarray(abuf, sizeof(*bufp)); -#endif - - for (i = 0; i < abuf; i++) { -#ifdef DEBUG - /* - * Allocate each buffer individually so they can be - * free()d during ntpd shutdown on DEBUG builds to - * keep them out of heap leak reports. - */ - bufp = emalloc_zero(sizeof(*bufp)); -#endif - LINK_SLIST(free_recv_list, bufp, link); - bufp++; - free_recvbufs++; - total_recvbufs++; + if (abuf < nbufs || abuf > RECV_BATCH) + abuf = RECV_BATCH; /* clamp on overflow */ + else + abuf += (~abuf + 1) & (RECV_INC - 1); /* round up */ + + if (abuf > (limit_recvbufs - total_recvbufs)) + abuf = limit_recvbufs - total_recvbufs; + abuf += (~abuf + 1) & (chunk - 1); /* round up */ + + while (abuf) { + bufp = calloc(chunk, sizeof(*bufp)); + if (!bufp) { + limit_recvbufs = total_recvbufs; + break; + } + for (i = chunk; i; --i,++bufp) { + LINK_SLIST(free_recv_list, bufp, link); + } + free_recvbufs += chunk; + total_recvbufs += chunk; + abuf -= chunk; } - lowater_adds++; + ++lowater_adds; } void @@ -115,15 +150,19 @@ init_recvbuff(int nbufs) free_recvbufs = total_recvbufs = 0; full_recvbufs = lowater_adds = 0; + limit_recvbufs = RECV_TOOMANY; + emerg_recvbufs = RECV_CLOCK; + create_buffers(nbufs); -#if defined(SYS_WINNT) +# if defined(SYS_WINNT) InitializeCriticalSection(&RecvLock); -#endif + InitializeCriticalSection(&FreeLock); +# endif -#ifdef DEBUG +# ifdef DEBUG atexit(&uninit_recvbuff); -#endif +# endif } @@ -146,6 +185,10 @@ uninit_recvbuff(void) break; free(rbunlinked); } +# if defined(SYS_WINNT) + DeleteCriticalSection(&FreeLock); + DeleteCriticalSection(&RecvLock); +# endif } #endif /* DEBUG */ @@ -157,13 +200,14 @@ void freerecvbuf(recvbuf_t *rb) { if (rb) { - LOCK(); - rb->used--; - if (rb->used != 0) + if (--rb->used != 0) { msyslog(LOG_ERR, "******** freerecvbuff non-zero usage: %d *******", rb->used); + rb->used = 0; + } + LOCK_F(); LINK_SLIST(free_recv_list, rb, link); - free_recvbufs++; - UNLOCK(); + ++free_recvbufs; + UNLOCK_F(); } } @@ -175,28 +219,34 @@ add_full_recv_buffer(recvbuf_t *rb) msyslog(LOG_ERR, "add_full_recv_buffer received NULL buffer"); return; } - LOCK(); + LOCK_R(); LINK_FIFO(full_recv_fifo, rb, link); - full_recvbufs++; - UNLOCK(); + ++full_recvbufs; + UNLOCK_R(); } recvbuf_t * -get_free_recv_buffer(void) +get_free_recv_buffer( + int /*BOOL*/ urgent + ) { - recvbuf_t *buffer; + recvbuf_t *buffer = NULL; - LOCK(); - UNLINK_HEAD_SLIST(buffer, free_recv_list, link); + LOCK_F(); + if (free_recvbufs > (urgent ? emerg_recvbufs : 0)) { + UNLINK_HEAD_SLIST(buffer, free_recv_list, link); + } + if (buffer != NULL) { - free_recvbufs--; + if (free_recvbufs) + --free_recvbufs; initialise_buffer(buffer); - buffer->used++; + ++buffer->used; } else { - buffer_shortfall++; + ++buffer_shortfall; } - UNLOCK(); + UNLOCK_F(); return buffer; } @@ -204,17 +254,15 @@ get_free_recv_buffer(void) #ifdef HAVE_IO_COMPLETION_PORT recvbuf_t * -get_free_recv_buffer_alloc(void) +get_free_recv_buffer_alloc( + int /*BOOL*/ urgent + ) { - recvbuf_t *buffer; - - buffer = get_free_recv_buffer(); - if (NULL == buffer) { + LOCK_F(); + if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) create_buffers(RECV_INC); - buffer = get_free_recv_buffer(); - } - ENSURE(buffer != NULL); - return (buffer); + UNLOCK_F(); + return get_free_recv_buffer(urgent); } #endif @@ -224,30 +272,26 @@ get_full_recv_buffer(void) { recvbuf_t * rbuf; - LOCK(); - /* - * make sure there are free buffers when we - * wander off to do lengthy packet processing with - * any buffer we grab from the full list. + * make sure there are free buffers when we wander off to do + * lengthy packet processing with any buffer we grab from the + * full list. * - * fixes malloc() interrupted by SIGIO risk - * (Bug 889) + * fixes malloc() interrupted by SIGIO risk (Bug 889) */ - if (NULL == free_recv_list || buffer_shortfall > 0) { - /* - * try to get us some more buffers - */ + LOCK_F(); + if (free_recvbufs <= emerg_recvbufs || buffer_shortfall > 0) create_buffers(RECV_INC); - } + UNLOCK_F(); /* * try to grab a full buffer */ + LOCK_R(); UNLINK_FIFO(rbuf, full_recv_fifo, link); - if (rbuf != NULL) - full_recvbufs--; - UNLOCK(); + if (rbuf != NULL && full_recvbufs) + --full_recvbufs; + UNLOCK_R(); return rbuf; } @@ -265,12 +309,18 @@ purge_recv_buffers_for_fd( recvbuf_t *rbufp; recvbuf_t *next; recvbuf_t *punlinked; + recvbuf_t *freelist = NULL; - LOCK(); + /* We want to hold only one lock at a time. So we do a scan on + * the full buffer queue, collecting items as we go, and when + * done we spool the the collected items to 'freerecvbuf()'. + */ + LOCK_R(); for (rbufp = HEAD_FIFO(full_recv_fifo); rbufp != NULL; - rbufp = next) { + rbufp = next) + { next = rbufp->link; # ifdef HAVE_IO_COMPLETION_PORT if (rbufp->dstadr == NULL && rbufp->fd == fd) @@ -281,12 +331,20 @@ purge_recv_buffers_for_fd( UNLINK_MID_FIFO(punlinked, full_recv_fifo, rbufp, link, recvbuf_t); INSIST(punlinked == rbufp); - full_recvbufs--; - freerecvbuf(rbufp); + if (full_recvbufs) + --full_recvbufs; + rbufp->link = freelist; + freelist = rbufp; } } - UNLOCK(); + UNLOCK_R(); + + while (freelist) { + next = freelist->link; + freerecvbuf(freelist); + freelist = next; + } } diff --git a/contrib/ntp/libntp/strdup.c b/contrib/ntp/libntp/strdup.c index 62d5a16d433c..8af9ff81b39c 100644 --- a/contrib/ntp/libntp/strdup.c +++ b/contrib/ntp/libntp/strdup.c @@ -1,13 +1,15 @@ #include <config.h> #include <ntp_assert.h> -#include "ntp_malloc.h" #include <string.h> +#include "ntp_malloc.h" +#include "l_stdlib.h" -#ifndef HAVE_STRDUP +#define STRDUP_EMPTY_UNIT +#ifndef HAVE_STRDUP +# undef STRDUP_EMPTY_UNIT char *strdup(const char *s); - char * strdup( const char *s @@ -24,6 +26,30 @@ strdup( return cp; } -#else +#endif + +#ifndef HAVE_MEMCHR +# undef STRDUP_EMPTY_UNIT +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *p = s; + while (n && *p != c) { + --n; + ++p; + } + return n ? (char*)p : NULL; +} +#endif + +#ifndef HAVE_STRNLEN +# undef STRDUP_EMPTY_UNIT +size_t strnlen(const char *s, size_t n) +{ + const char *e = memchr(s, 0, n); + return e ? (size_t)(e - s) : n; +} +#endif + +#ifdef STRDUP_EMPTY_UNIT int strdup_c_nonempty_compilation_unit; #endif diff --git a/contrib/ntp/libntp/timexsup.c b/contrib/ntp/libntp/timexsup.c index 498961f3b3c7..979a7c4aea8e 100644 --- a/contrib/ntp/libntp/timexsup.c +++ b/contrib/ntp/libntp/timexsup.c @@ -27,13 +27,13 @@ clamp_rounded( dval = floor(dval + 0.5); /* clamp / saturate */ - if (dval >= LONG_MAX) + if (dval >= (double)LONG_MAX) return LONG_MAX; - if (dval <= LONG_MIN) + if (dval <= (double)LONG_MIN) return LONG_MIN; return (long)dval; - } + double dbl_from_var_long( long lval, @@ -80,4 +80,3 @@ usec_long_from_dbl( { return clamp_rounded(dval * 1e+6); } - |