CVE-2020-14354: Use After Free
A possible use-after-free and double-free in c-ares lib version 1.16.0 if aresdestroy() is called prior to aresgetaddrinfo() completing. This flaw possibly allows an attacker to crash the service that uses c-ares lib. The highest threat from this vulnerability is to this service availability.
Other sources
The following code was introduced in c-ares commit dbd4c441 (first released in 1.16.0, which was published on 2020-03-13), as part of the new aresgetaddrinfo() feature:
========================================================= static void endhquery(struct hostquery hquery, int status) { [...] hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai); aresfree(hquery->name); aresfree(hquery); }
static void hostcallback(void arg, int status, int timeouts, unsigned char abuf, int alen) { struct hostquery hquery = (struct hostquery)arg; int addinfostatus = ARESSUCCESS; [...]
if (status == ARESSUCCESS) [...] else if (status == ARESEDESTRUCTION) { endhquery(hquery, status); }
if (!hquery->remaining) { if (addinfostatus != ARESSUCCESS) [...] else if (hquery->ai->nodes) { / at least one query ended with ARESSUCCESS / endhquery(hquery, ARESSUCCESS); } else if (status == ARESENOTFOUND) [...] else { endhquery(hquery, status); } }
/ at this point we keep on waiting for the next query to finish / } =========================================================
In the ARESEDESTRUCTION case, hostcallback() ends up calling endhquery() twice (unless it crashes before the second call), and the second call will, among other things, call a function pointer from freed memory and free the memory a second time.
Here's a reproducer:
========================================================= #include <ares.h> #include <err.h> #include <stdio.h> #include <stdlib.h>
static void gaicb(void arg, int status, int timeouts, struct aresaddrinfo result) { printf(\"gaicb(): %s\ \", aresstrerror(status)); }
int main(void) { if (areslibraryinit(ARESLIBINITALL)) errx(1, \"areslibraryinit\"); areschannel chan; if (aresinit(&chan)) errx(1, \"aresinit\"); aresgetaddrinfo(chan, \"blah\", NULL, NULL, gaicb, NULL); aresdestroy(chan); return 0; } =========================================================
Output (from a test against c-ares from Debian testing):
========================================================= user@vm:~/test/cares-gai-destroy$ valgrind ./cares-gai-destroy-uaf ==5248== Memcheck, a memory error detector ==5248== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==5248== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info ==5248== Command: ./cares-gai-destroy-uaf ==5248== gaicb(): Channel is being destroyed ==5248== Invalid read of size 4 ==5248== at 0x485F56A: hostcallback (aresgetaddrinfo.c:553) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x4a4ae00 is 80 bytes inside a block of size 88 free'd ==5248== at 0x48369AB: free (vgreplacemalloc.c:530) ==5248== by 0x485F126: endhquery (aresgetaddrinfo.c:429) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Block was alloc'd at ==5248== at 0x483577F: malloc (vgreplacemalloc.c:299) ==5248== by 0x485F951: aresgetaddrinfo (aresgetaddrinfo.c:650) ==5248== by 0x109247: main (cares-gai-destroy-uaf.c:17) ==5248== ==5248== Invalid read of size 4 ==5248== at 0x485F4CD: hostcallback (aresgetaddrinfo.c:542) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x4a4ae00 is 80 bytes inside a block of size 88 free'd ==5248== at 0x48369AB: free (vgreplacemalloc.c:530) ==5248== by 0x485F126: endhquery (aresgetaddrinfo.c:429) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Block was alloc'd at ==5248== at 0x483577F: malloc (vgreplacemalloc.c:299) ==5248== by 0x485F951: aresgetaddrinfo (aresgetaddrinfo.c:650) ==5248== by 0x109247: main (cares-gai-destroy-uaf.c:17) ==5248== ==5248== Invalid read of size 4 ==5248== at 0x485F4D0: hostcallback (aresgetaddrinfo.c:541) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x4a4adec is 60 bytes inside a block of size 88 free'd ==5248== at 0x48369AB: free (vgreplacemalloc.c:530) ==5248== by 0x485F126: endhquery (aresgetaddrinfo.c:429) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Block was alloc'd at ==5248== at 0x483577F: malloc (vgreplacemalloc.c:299) ==5248== by 0x485F951: aresgetaddrinfo (aresgetaddrinfo.c:650) ==5248== by 0x109247: main (cares-gai-destroy-uaf.c:17) ==5248== ==5248== Invalid write of size 4 ==5248== at 0x485F4D6: hostcallback (aresgetaddrinfo.c:542) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x4a4ae00 is 80 bytes inside a block of size 88 free'd ==5248== at 0x48369AB: free (vgreplacemalloc.c:530) ==5248== by 0x485F126: endhquery (aresgetaddrinfo.c:429) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Block was alloc'd at ==5248== at 0x483577F: malloc (vgreplacemalloc.c:299) ==5248== by 0x485F951: aresgetaddrinfo (aresgetaddrinfo.c:650) ==5248== by 0x109247: main (cares-gai-destroy-uaf.c:17) ==5248== ==5248== Invalid read of size 8 ==5248== at 0x485F0BD: endhquery (aresgetaddrinfo.c:394) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x4a4adf8 is 72 bytes inside a block of size 88 free'd ==5248== at 0x48369AB: free (vgreplacemalloc.c:530) ==5248== by 0x485F126: endhquery (aresgetaddrinfo.c:429) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Block was alloc'd at ==5248== at 0x483577F: malloc (vgreplacemalloc.c:299) ==5248== by 0x485F951: aresgetaddrinfo (aresgetaddrinfo.c:650) ==5248== by 0x109247: main (cares-gai-destroy-uaf.c:17) ==5248== ==5248== Invalid read of size 8 ==5248== at 0x485F05D: aresfreeaddrinfo (aresfreeaddrinfo.c:54) ==5248== by 0x485F167: endhquery (aresgetaddrinfo.c:423) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==5248== ==5248== ==5248== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==5248== Access not within mapped region at address 0x0 ==5248== at 0x485F05D: aresfreeaddrinfo (aresfreeaddrinfo.c:54) ==5248== by 0x485F167: endhquery (aresgetaddrinfo.c:423) ==5248== by 0x485F569: hostcallback (aresgetaddrinfo.c:550) ==5248== by 0x486869F: qcallback (aresquery.c:183) ==5248== by 0x485E8D0: aresdestroy (aresdestroy.c:58) ==5248== by 0x109253: main (cares-gai-destroy-uaf.c:18) ==5248== If you believe this happened as a result of a stack ==5248== overflow in your program's main thread (unlikely but ==5248== possible), you can try to increase the size of the ==5248== main thread stack using the --main-stacksize= flag. ==5248== The main thread stack size used in this run was 8388608. ==5248== ==5248== HEAP SUMMARY: ==5248== in use at exit: 74,643 bytes in 7 blocks ==5248== total heap usage: 29 allocs, 22 frees, 95,011 bytes allocated ==5248== ==5248== LEAK SUMMARY: ==5248== definitely lost: 0 bytes in 0 blocks ==5248== indirectly lost: 0 bytes in 0 blocks ==5248== possibly lost: 0 bytes in 0 blocks ==5248== still reachable: 74,643 bytes in 7 blocks ==5248== suppressed: 0 bytes in 0 blocks ==5248== Rerun with --leak-check=full to see details of leaked memory ==5248== ==5248== For counts of detected and suppressed errors, rerun with: -v ==5248== ERROR SUMMARY: 7 errors from 6 contexts (suppressed: 0 from 0) Segmentation fault =========================================================
References:
https://packetstormsecurity.com/files/158755/GS20200804145053.txt
— Red Hat
Affected Software
Remediation
Patch Available
Event History
Frequently Asked Questions
What is CVE-2020-14354?
CVE-2020-14354 is a vulnerability in the c-ares library (version 1.16.0) that can lead to a use-after-free and double-free condition if the ares_destroy() function is called before ares_getaddrinfo() completes.
How does CVE-2020-14354 impact service availability?
CVE-2020-14354 can cause the service that uses the c-ares library to crash, resulting in a potential denial-of-service (DoS) situation.
What is the severity of CVE-2020-14354?
The severity of CVE-2020-14354 is medium, with a severity value of 3.3.
Which software versions are affected by CVE-2020-14354?
The c-ares library version 1.16.0 is affected by CVE-2020-14354. Fedora version 33 is also affected.
How can I fix CVE-2020-14354?
To fix CVE-2020-14354, update your c-ares library to version 1.16.1 or higher.