--- An sbuf configured with SBUF_AUTOEXTEND will call malloc with M_WAITOK when --- a write to the buffer causes it to overflow. We therefore can't hold the CC --- list RW lock over a call to sbuf_printf() for an sbuf configured with --- SBUF_AUTOEXTEND. Switch to a fixed length sbuf of an appropriate size that --- should suffice, except if the sysctl is being processed as one or more new --- algorithms are loaded. If that happens, we accept the race and will fail the --- sysctl gracefully. This should address a WITNESS warning and the potential --- panic that would occur if the sbuf call to malloc did sleep one time. --- --- Sponsored by: FreeBSD Foundation --- Reported by: Nick Hibma --- Reviewed by: ? --- MFC after: 3 weeks --- X-MFC with: r215166 --- Index: sys/netinet/cc/cc.c =================================================================== --- sys/netinet/cc/cc.c (revision 217684) +++ sys/netinet/cc/cc.c (working copy) @@ -128,20 +128,37 @@ cc_list_available(SYSCTL_HANDLER_ARGS) { struct cc_algo *algo; struct sbuf *s; - int err, first; + int err, first, nalgos; - err = 0; + err = nalgos = 0; first = 1; - s = sbuf_new(NULL, NULL, TCP_CA_NAME_MAX, SBUF_AUTOEXTEND); + CC_LIST_RLOCK(); + STAILQ_FOREACH(algo, &cc_list, entries) { + nalgos++; + } + CC_LIST_RUNLOCK(); + + s = sbuf_new(NULL, NULL, nalgos * TCP_CA_NAME_MAX, SBUF_FIXEDLEN); + if (s == NULL) return (ENOMEM); + /* + * It is theoretically possible for the CC list to have grown in size + * since the call to sbuf_new() and therefore for the sbuf to be too + * small. If this were to happen (incredibly unlikely), the sbuf will + * reach an overflow condition, sbuf_printf() will return an error and + * the sysctl will fail gracefully. + */ CC_LIST_RLOCK(); STAILQ_FOREACH(algo, &cc_list, entries) { err = sbuf_printf(s, first ? "%s" : ", %s", algo->name); - if (err) + if (err) { + /* Sbuf overflow condition. */ + err = EOVERFLOW; break; + } first = 0; } CC_LIST_RUNLOCK();