From 660bdfdefcd4e8662e47c8ac9236fdb8df18a934 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 14 Sep 2015 14:54:55 +0900 Subject: Add a crash test for tcp_nuke_addr. Bug: 23663111 Change-Id: I3b79362d6a53b7689168488527059ae506161d5f --- tests/tcp_nuke_addr/Android.mk | 12 +++ tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp | 148 +++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 tests/tcp_nuke_addr/Android.mk create mode 100644 tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp (limited to 'tests/tcp_nuke_addr') diff --git a/tests/tcp_nuke_addr/Android.mk b/tests/tcp_nuke_addr/Android.mk new file mode 100644 index 00000000..2eef6089 --- /dev/null +++ b/tests/tcp_nuke_addr/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := tcp_nuke_addr_test + +LOCAL_C_INCLUDES += frameworks/native/include external/libcxx/include +LOCAL_CPPFLAGS += -std=c++11 -Wall -Werror +LOCAL_SHARED_LIBRARIES := libc++ +LOCAL_SRC_FILES := tcp_nuke_addr_test.cpp +LOCAL_MODULE_TAGS := eng tests + +include $(BUILD_NATIVE_TEST) diff --git a/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp new file mode 100644 index 00000000..d153949e --- /dev/null +++ b/tests/tcp_nuke_addr/tcp_nuke_addr_test.cpp @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include + +#include "utils/RWLock.h" + +#include +#include +#include + +// Defined only in ifc_utils.c, in the kernel, and in the NDK. +#ifndef SIOCKILLADDR +#define SIOCKILLADDR 0x8939 +#endif + +#ifndef TCP_LINGER2 +#define TCP_LINGER2 8 +#endif + +#define KILL_INTERVAL_MS 10 +#define CONNECT_THREADS 1 + +#define PERROR_EXIT(msg) { do { perror((msg)); exit(1); } while (0); }; + + +// Ensures that sockets don't stay in TIME_WAIT state. +void setSoLinger(int s) { + const struct linger l = { + 0, // off + 0, // 0 seconds + }; + if (setsockopt(s, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1) { + PERROR_EXIT("SO_LINGER"); + } + const int nolinger = -1; + if (setsockopt(s, SOL_TCP, TCP_LINGER2, &nolinger, sizeof(nolinger)) == -1) { + PERROR_EXIT("TCP_LINGER2"); + } +} + + +// Binds to a random port on a random loopback address. We don't just use 127.0.0.1 because we don't +// want this test to kill unrelated connections on loopback. +int bindRandomAddr() { + sockaddr_in sin; + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + while (sin.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + arc4random_buf( + ((uint8_t *) &sin.sin_addr.s_addr) + 1, + sizeof(sin.sin_addr.s_addr) - 1); + } + + int listensock; + if ((listensock = socket(AF_INET, SOCK_STREAM, 0)) == -1) PERROR_EXIT("listensock"); + if (bind(listensock, (sockaddr *) &sin, sizeof(sin)) == -1) PERROR_EXIT("bind"); + if (listen(listensock, 10) == -1) PERROR_EXIT("listen"); + + return listensock; +} + + +// Thread that calls SIOCKILLADDR in a loop. +void killSockets(sockaddr_in listenaddr, int intervalMs, android::RWLock *lock) { + ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + listenaddr.sin_port = 0; + strncpy(ifr.ifr_name, "lo", strlen("lo")); + memcpy(&ifr.ifr_addr, &listenaddr, sizeof(listenaddr)); + + int ioctlsock = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctlsock == -1) PERROR_EXIT("ioctlsock"); + while(true) { + lock->writeLock(); + if (ioctl(ioctlsock, SIOCKILLADDR, &ifr) != 0) { + PERROR_EXIT("SIOCKILLADDR failed, did you run 32-bit userspace on a 64-bit kernel?"); + } + lock->unlock(); + std::this_thread::sleep_for(std::chrono::milliseconds(intervalMs)); + } +} + + +// Thread that calls connect() in a loop. +void connectLoop(sockaddr_in listenaddr, int listensock, + android::RWLock *lock, std::atomic *attempts) { + while(true) { + int s = socket(AF_INET, SOCK_STREAM, 0); + setSoLinger(s); + + // Don't call SIOCKILLADDR while connect() is running, or we'll end up with lots of + // connections in state FIN_WAITx or TIME_WAIT, which will then slow down future + // due to SYN retransmits. + lock->readLock(); + if (connect(s, (sockaddr *) &listenaddr, sizeof(listenaddr)) == -1) PERROR_EXIT("connect"); + lock->unlock(); + + send(s, "foo", 3, 0); + int acceptedsock = accept(listensock, NULL, 0); + if (close(acceptedsock) == -1) PERROR_EXIT("close"); + if (close(s) == -1) PERROR_EXIT("close"); + + attempts->fetch_add(1); + } +} + + +// Thread that prints progress every second. +void progressThread(std::atomic *attempts) { + uint32_t elapsed = 0; + uint32_t total, previous = 0; + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + elapsed++; + total = attempts->load(); + printf("%ds: %u cps, total %u\n", elapsed, total-previous, total); + fflush(stdout); + previous = total; + } +} + + +int main() { + int listensock = bindRandomAddr(); + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(listensock, (sockaddr *) &sin, &len) == -1) PERROR_EXIT("getsockname"); + + printf("Using address %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + + android::RWLock lock; + std::atomic attempts; + attempts.store(0); + + std::thread t0(killSockets, sin, KILL_INTERVAL_MS, &lock); + std::thread *connectThreads[CONNECT_THREADS]; + for (size_t i = 0; i < CONNECT_THREADS; i++) { + connectThreads[i] = new std::thread(connectLoop, sin, listensock, &lock, &attempts); + } + std::thread t1(progressThread, &attempts); + t1.join(); + + return 0; +} -- cgit v1.2.3