diff options
author | Bernie Innocenti | 2018-05-23 05:02:52 -0500 |
---|---|---|
committer | Bernie Innocenti | 2018-05-25 04:43:12 -0500 |
commit | 0ad41cf3f44e5403132f7f620d8199446cb815b4 (patch) | |
tree | c9c0c28f5444cd9d97bff3d0fc1a457ad1df712e | |
parent | 39cab7cd2538b0f31b3672c71195c47ba3b00de9 (diff) | |
download | platform-system-core-0ad41cf3f44e5403132f7f620d8199446cb815b4.tar.gz platform-system-core-0ad41cf3f44e5403132f7f620d8199446cb815b4.tar.xz platform-system-core-0ad41cf3f44e5403132f7f620d8199446cb815b4.zip |
SocketListener: use poll() instead of select()
FD_SET is limited to 1024 file descriptors in Linux, which causes
processes with too many open files or connections to crash:
FORTIFY: FD_ISSET: file descriptor 1024 >= FD_SETSIZE 128
The fix we used elsewhere is replacing select() with poll(), but in the
case of SocketListener we additionally need to replace the SocketClient
list with a map indexed by fd in order to avoid quadratic behavior on
each poll() wakeup.
Bug: 79838856
Test: device boots and appears to work normally
Change-Id: I19ca4be675e9638104c0e7acf4a4bc62085e8ecd
-rw-r--r-- | libsysutils/include/sysutils/SocketClient.h | 5 | ||||
-rw-r--r-- | libsysutils/include/sysutils/SocketListener.h | 6 | ||||
-rw-r--r-- | libsysutils/src/SocketListener.cpp | 164 |
3 files changed, 68 insertions, 107 deletions
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h index 1004f0611..c657526ee 100644 --- a/libsysutils/include/sysutils/SocketClient.h +++ b/libsysutils/include/sysutils/SocketClient.h | |||
@@ -1,8 +1,6 @@ | |||
1 | #ifndef _SOCKET_CLIENT_H | 1 | #ifndef _SOCKET_CLIENT_H |
2 | #define _SOCKET_CLIENT_H | 2 | #define _SOCKET_CLIENT_H |
3 | 3 | ||
4 | #include "List.h" | ||
5 | |||
6 | #include <pthread.h> | 4 | #include <pthread.h> |
7 | #include <cutils/atomic.h> | 5 | #include <cutils/atomic.h> |
8 | #include <sys/types.h> | 6 | #include <sys/types.h> |
@@ -35,7 +33,7 @@ public: | |||
35 | SocketClient(int sock, bool owned, bool useCmdNum); | 33 | SocketClient(int sock, bool owned, bool useCmdNum); |
36 | virtual ~SocketClient(); | 34 | virtual ~SocketClient(); |
37 | 35 | ||
38 | int getSocket() { return mSocket; } | 36 | int getSocket() const { return mSocket; } |
39 | pid_t getPid() const { return mPid; } | 37 | pid_t getPid() const { return mPid; } |
40 | uid_t getUid() const { return mUid; } | 38 | uid_t getUid() const { return mUid; } |
41 | gid_t getGid() const { return mGid; } | 39 | gid_t getGid() const { return mGid; } |
@@ -84,5 +82,4 @@ private: | |||
84 | int sendDataLockedv(struct iovec *iov, int iovcnt); | 82 | int sendDataLockedv(struct iovec *iov, int iovcnt); |
85 | }; | 83 | }; |
86 | 84 | ||
87 | typedef android::sysutils::List<SocketClient *> SocketClientCollection; | ||
88 | #endif | 85 | #endif |
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h index bc93b8635..56f247866 100644 --- a/libsysutils/include/sysutils/SocketListener.h +++ b/libsysutils/include/sysutils/SocketListener.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2008-2014 The Android Open Source Project | 2 | * Copyright (C) 2008 The Android Open Source Project |
3 | * | 3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
@@ -18,6 +18,8 @@ | |||
18 | 18 | ||
19 | #include <pthread.h> | 19 | #include <pthread.h> |
20 | 20 | ||
21 | #include <unordered_map> | ||
22 | |||
21 | #include <sysutils/SocketClient.h> | 23 | #include <sysutils/SocketClient.h> |
22 | #include "SocketClientCommand.h" | 24 | #include "SocketClientCommand.h" |
23 | 25 | ||
@@ -25,7 +27,7 @@ class SocketListener { | |||
25 | bool mListen; | 27 | bool mListen; |
26 | const char *mSocketName; | 28 | const char *mSocketName; |
27 | int mSock; | 29 | int mSock; |
28 | SocketClientCollection *mClients; | 30 | std::unordered_map<int, SocketClient*> mClients; |
29 | pthread_mutex_t mClientsLock; | 31 | pthread_mutex_t mClientsLock; |
30 | int mCtrlPipe[2]; | 32 | int mCtrlPipe[2]; |
31 | pthread_t mThread; | 33 | pthread_t mThread; |
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp index 3f8f3db8f..128a27a3a 100644 --- a/libsysutils/src/SocketListener.cpp +++ b/libsysutils/src/SocketListener.cpp | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2008-2014 The Android Open Source Project | 2 | * Copyright (C) 2008 The Android Open Source Project |
3 | * | 3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | 5 | * you may not use this file except in compliance with the License. |
@@ -19,13 +19,15 @@ | |||
19 | #include <errno.h> | 19 | #include <errno.h> |
20 | #include <stdio.h> | 20 | #include <stdio.h> |
21 | #include <stdlib.h> | 21 | #include <stdlib.h> |
22 | #include <sys/select.h> | 22 | #include <sys/poll.h> |
23 | #include <sys/socket.h> | 23 | #include <sys/socket.h> |
24 | #include <sys/time.h> | 24 | #include <sys/time.h> |
25 | #include <sys/types.h> | 25 | #include <sys/types.h> |
26 | #include <sys/un.h> | 26 | #include <sys/un.h> |
27 | #include <unistd.h> | 27 | #include <unistd.h> |
28 | 28 | ||
29 | #include <vector> | ||
30 | |||
29 | #include <cutils/sockets.h> | 31 | #include <cutils/sockets.h> |
30 | #include <log/log.h> | 32 | #include <log/log.h> |
31 | #include <sysutils/SocketListener.h> | 33 | #include <sysutils/SocketListener.h> |
@@ -52,7 +54,6 @@ void SocketListener::init(const char *socketName, int socketFd, bool listen, boo | |||
52 | mSock = socketFd; | 54 | mSock = socketFd; |
53 | mUseCmdNum = useCmdNum; | 55 | mUseCmdNum = useCmdNum; |
54 | pthread_mutex_init(&mClientsLock, NULL); | 56 | pthread_mutex_init(&mClientsLock, NULL); |
55 | mClients = new SocketClientCollection(); | ||
56 | } | 57 | } |
57 | 58 | ||
58 | SocketListener::~SocketListener() { | 59 | SocketListener::~SocketListener() { |
@@ -63,12 +64,9 @@ SocketListener::~SocketListener() { | |||
63 | close(mCtrlPipe[0]); | 64 | close(mCtrlPipe[0]); |
64 | close(mCtrlPipe[1]); | 65 | close(mCtrlPipe[1]); |
65 | } | 66 | } |
66 | SocketClientCollection::iterator it; | 67 | for (auto pair : mClients) { |
67 | for (it = mClients->begin(); it != mClients->end();) { | 68 | pair.second->decRef(); |
68 | (*it)->decRef(); | ||
69 | it = mClients->erase(it); | ||
70 | } | 69 | } |
71 | delete mClients; | ||
72 | } | 70 | } |
73 | 71 | ||
74 | int SocketListener::startListener() { | 72 | int SocketListener::startListener() { |
@@ -95,7 +93,7 @@ int SocketListener::startListener(int backlog) { | |||
95 | SLOGE("Unable to listen on socket (%s)", strerror(errno)); | 93 | SLOGE("Unable to listen on socket (%s)", strerror(errno)); |
96 | return -1; | 94 | return -1; |
97 | } else if (!mListen) | 95 | } else if (!mListen) |
98 | mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); | 96 | mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum); |
99 | 97 | ||
100 | if (pipe(mCtrlPipe)) { | 98 | if (pipe(mCtrlPipe)) { |
101 | SLOGE("pipe failed (%s)", strerror(errno)); | 99 | SLOGE("pipe failed (%s)", strerror(errno)); |
@@ -135,11 +133,10 @@ int SocketListener::stopListener() { | |||
135 | mSock = -1; | 133 | mSock = -1; |
136 | } | 134 | } |
137 | 135 | ||
138 | SocketClientCollection::iterator it; | 136 | for (auto pair : mClients) { |
139 | for (it = mClients->begin(); it != mClients->end();) { | 137 | delete pair.second; |
140 | delete (*it); | ||
141 | it = mClients->erase(it); | ||
142 | } | 138 | } |
139 | mClients.clear(); | ||
143 | return 0; | 140 | return 0; |
144 | } | 141 | } |
145 | 142 | ||
@@ -152,47 +149,30 @@ void *SocketListener::threadStart(void *obj) { | |||
152 | } | 149 | } |
153 | 150 | ||
154 | void SocketListener::runListener() { | 151 | void SocketListener::runListener() { |
155 | 152 | while (true) { | |
156 | SocketClientCollection pendingList; | 153 | std::vector<pollfd> fds; |
157 | |||
158 | while(1) { | ||
159 | SocketClientCollection::iterator it; | ||
160 | fd_set read_fds; | ||
161 | int rc = 0; | ||
162 | int max = -1; | ||
163 | |||
164 | FD_ZERO(&read_fds); | ||
165 | |||
166 | if (mListen) { | ||
167 | max = mSock; | ||
168 | FD_SET(mSock, &read_fds); | ||
169 | } | ||
170 | |||
171 | FD_SET(mCtrlPipe[0], &read_fds); | ||
172 | if (mCtrlPipe[0] > max) | ||
173 | max = mCtrlPipe[0]; | ||
174 | 154 | ||
175 | pthread_mutex_lock(&mClientsLock); | 155 | pthread_mutex_lock(&mClientsLock); |
176 | for (it = mClients->begin(); it != mClients->end(); ++it) { | 156 | fds.reserve(2 + mClients.size()); |
157 | fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN}); | ||
158 | if (mListen) fds.push_back({.fd = mSock, .events = POLLIN}); | ||
159 | for (auto pair : mClients) { | ||
177 | // NB: calling out to an other object with mClientsLock held (safe) | 160 | // NB: calling out to an other object with mClientsLock held (safe) |
178 | int fd = (*it)->getSocket(); | 161 | const int fd = pair.second->getSocket(); |
179 | FD_SET(fd, &read_fds); | 162 | if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first); |
180 | if (fd > max) { | 163 | fds.push_back({.fd = fd, .events = POLLIN}); |
181 | max = fd; | ||
182 | } | ||
183 | } | 164 | } |
184 | pthread_mutex_unlock(&mClientsLock); | 165 | pthread_mutex_unlock(&mClientsLock); |
185 | SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); | 166 | |
186 | if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { | 167 | SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName); |
187 | if (errno == EINTR) | 168 | int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1)); |
188 | continue; | 169 | if (rc < 0) { |
189 | SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max); | 170 | SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen); |
190 | sleep(1); | 171 | sleep(1); |
191 | continue; | 172 | continue; |
192 | } else if (!rc) | 173 | } |
193 | continue; | ||
194 | 174 | ||
195 | if (FD_ISSET(mCtrlPipe[0], &read_fds)) { | 175 | if (fds[0].revents & (POLLIN | POLLERR)) { |
196 | char c = CtrlPipe_Shutdown; | 176 | char c = CtrlPipe_Shutdown; |
197 | TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); | 177 | TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); |
198 | if (c == CtrlPipe_Shutdown) { | 178 | if (c == CtrlPipe_Shutdown) { |
@@ -200,7 +180,7 @@ void SocketListener::runListener() { | |||
200 | } | 180 | } |
201 | continue; | 181 | continue; |
202 | } | 182 | } |
203 | if (mListen && FD_ISSET(mSock, &read_fds)) { | 183 | if (mListen && (fds[1].revents & (POLLIN | POLLERR))) { |
204 | int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC)); | 184 | int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC)); |
205 | if (c < 0) { | 185 | if (c < 0) { |
206 | SLOGE("accept failed (%s)", strerror(errno)); | 186 | SLOGE("accept failed (%s)", strerror(errno)); |
@@ -208,32 +188,33 @@ void SocketListener::runListener() { | |||
208 | continue; | 188 | continue; |
209 | } | 189 | } |
210 | pthread_mutex_lock(&mClientsLock); | 190 | pthread_mutex_lock(&mClientsLock); |
211 | mClients->push_back(new SocketClient(c, true, mUseCmdNum)); | 191 | mClients[c] = new SocketClient(c, true, mUseCmdNum); |
212 | pthread_mutex_unlock(&mClientsLock); | 192 | pthread_mutex_unlock(&mClientsLock); |
213 | } | 193 | } |
214 | 194 | ||
215 | /* Add all active clients to the pending list first */ | 195 | // Add all active clients to the pending list first, so we can release |
216 | pendingList.clear(); | 196 | // the lock before invoking the callbacks. |
197 | std::vector<SocketClient*> pending; | ||
217 | pthread_mutex_lock(&mClientsLock); | 198 | pthread_mutex_lock(&mClientsLock); |
218 | for (it = mClients->begin(); it != mClients->end(); ++it) { | 199 | const int size = fds.size(); |
219 | SocketClient* c = *it; | 200 | for (int i = mListen ? 2 : 1; i < size; ++i) { |
220 | // NB: calling out to an other object with mClientsLock held (safe) | 201 | const struct pollfd& p = fds[i]; |
221 | int fd = c->getSocket(); | 202 | if (p.events & (POLLIN | POLLERR)) { |
222 | if (FD_ISSET(fd, &read_fds)) { | 203 | auto it = mClients.find(p.fd); |
223 | pendingList.push_back(c); | 204 | if (it == mClients.end()) { |
205 | SLOGE("fd vanished: %d", p.fd); | ||
206 | continue; | ||
207 | } | ||
208 | SocketClient* c = it->second; | ||
209 | pending.push_back(c); | ||
224 | c->incRef(); | 210 | c->incRef(); |
225 | } | 211 | } |
226 | } | 212 | } |
227 | pthread_mutex_unlock(&mClientsLock); | 213 | pthread_mutex_unlock(&mClientsLock); |
228 | 214 | ||
229 | /* Process the pending list, since it is owned by the thread, | 215 | for (SocketClient* c : pending) { |
230 | * there is no need to lock it */ | 216 | // Process it, if false is returned, remove from the map |
231 | while (!pendingList.empty()) { | 217 | SLOGV("processing fd %d", c->getSocket()); |
232 | /* Pop the first item from the list */ | ||
233 | it = pendingList.begin(); | ||
234 | SocketClient* c = *it; | ||
235 | pendingList.erase(it); | ||
236 | /* Process it, if false is returned, remove from list */ | ||
237 | if (!onDataAvailable(c)) { | 218 | if (!onDataAvailable(c)) { |
238 | release(c, false); | 219 | release(c, false); |
239 | } | 220 | } |
@@ -246,17 +227,10 @@ bool SocketListener::release(SocketClient* c, bool wakeup) { | |||
246 | bool ret = false; | 227 | bool ret = false; |
247 | /* if our sockets are connection-based, remove and destroy it */ | 228 | /* if our sockets are connection-based, remove and destroy it */ |
248 | if (mListen && c) { | 229 | if (mListen && c) { |
249 | /* Remove the client from our array */ | 230 | /* Remove the client from our map */ |
250 | SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); | 231 | SLOGV("going to zap %d for %s", c->getSocket(), mSocketName); |
251 | pthread_mutex_lock(&mClientsLock); | 232 | pthread_mutex_lock(&mClientsLock); |
252 | SocketClientCollection::iterator it; | 233 | ret = (mClients.erase(c->getSocket()) != 0); |
253 | for (it = mClients->begin(); it != mClients->end(); ++it) { | ||
254 | if (*it == c) { | ||
255 | mClients->erase(it); | ||
256 | ret = true; | ||
257 | break; | ||
258 | } | ||
259 | } | ||
260 | pthread_mutex_unlock(&mClientsLock); | 234 | pthread_mutex_unlock(&mClientsLock); |
261 | if (ret) { | 235 | if (ret) { |
262 | ret = c->decRef(); | 236 | ret = c->decRef(); |
@@ -270,25 +244,19 @@ bool SocketListener::release(SocketClient* c, bool wakeup) { | |||
270 | } | 244 | } |
271 | 245 | ||
272 | void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { | 246 | void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { |
273 | SocketClientCollection safeList; | 247 | // Add all clients to a separate list first, so we don't have to hold |
274 | 248 | // the lock while processing it. | |
275 | /* Add all active clients to the safe list first */ | 249 | std::vector<SocketClient*> clients; |
276 | safeList.clear(); | ||
277 | pthread_mutex_lock(&mClientsLock); | 250 | pthread_mutex_lock(&mClientsLock); |
278 | SocketClientCollection::iterator i; | 251 | clients.reserve(mClients.size()); |
279 | 252 | for (auto pair : mClients) { | |
280 | for (i = mClients->begin(); i != mClients->end(); ++i) { | 253 | SocketClient* c = pair.second; |
281 | SocketClient* c = *i; | ||
282 | c->incRef(); | 254 | c->incRef(); |
283 | safeList.push_back(c); | 255 | clients.push_back(c); |
284 | } | 256 | } |
285 | pthread_mutex_unlock(&mClientsLock); | 257 | pthread_mutex_unlock(&mClientsLock); |
286 | 258 | ||
287 | while (!safeList.empty()) { | 259 | for (SocketClient* c : clients) { |
288 | /* Pop the first item from the list */ | ||
289 | i = safeList.begin(); | ||
290 | SocketClient* c = *i; | ||
291 | safeList.erase(i); | ||
292 | // broadcasts are unsolicited and should not include a cmd number | 260 | // broadcasts are unsolicited and should not include a cmd number |
293 | if (c->sendMsg(code, msg, addErrno, false)) { | 261 | if (c->sendMsg(code, msg, addErrno, false)) { |
294 | SLOGW("Error sending broadcast (%s)", strerror(errno)); | 262 | SLOGW("Error sending broadcast (%s)", strerror(errno)); |
@@ -298,25 +266,19 @@ void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) { | |||
298 | } | 266 | } |
299 | 267 | ||
300 | void SocketListener::runOnEachSocket(SocketClientCommand *command) { | 268 | void SocketListener::runOnEachSocket(SocketClientCommand *command) { |
301 | SocketClientCollection safeList; | 269 | // Add all clients to a separate list first, so we don't have to hold |
302 | 270 | // the lock while processing it. | |
303 | /* Add all active clients to the safe list first */ | 271 | std::vector<SocketClient*> clients; |
304 | safeList.clear(); | ||
305 | pthread_mutex_lock(&mClientsLock); | 272 | pthread_mutex_lock(&mClientsLock); |
306 | SocketClientCollection::iterator i; | 273 | clients.reserve(mClients.size()); |
307 | 274 | for (auto pair : mClients) { | |
308 | for (i = mClients->begin(); i != mClients->end(); ++i) { | 275 | SocketClient* c = pair.second; |
309 | SocketClient* c = *i; | ||
310 | c->incRef(); | 276 | c->incRef(); |
311 | safeList.push_back(c); | 277 | clients.push_back(c); |
312 | } | 278 | } |
313 | pthread_mutex_unlock(&mClientsLock); | 279 | pthread_mutex_unlock(&mClientsLock); |
314 | 280 | ||
315 | while (!safeList.empty()) { | 281 | for (SocketClient* c : clients) { |
316 | /* Pop the first item from the list */ | ||
317 | i = safeList.begin(); | ||
318 | SocketClient* c = *i; | ||
319 | safeList.erase(i); | ||
320 | command->runSocketCommand(c); | 282 | command->runSocketCommand(c); |
321 | c->decRef(); | 283 | c->decRef(); |
322 | } | 284 | } |