diff options
Diffstat (limited to 'libcutils')
| -rw-r--r-- | libcutils/Android.mk | 24 | ||||
| -rw-r--r-- | libcutils/abort_socket.c | 293 | ||||
| -rw-r--r-- | libcutils/strdup16to8.c | 108 |
3 files changed, 394 insertions, 31 deletions
diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 18d0ee3..087d652 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -20,7 +20,7 @@ commonSources := \ array.c \ hashmap.c \ atomic.c \ - native_handle.c \ + native_handle.c \ buffer.c \ socket_inaddr_any_server.c \ socket_local_client.c \ @@ -38,38 +38,44 @@ commonSources := \ properties.c \ threads.c +commonHostSources := \ + ashmem-host.c + # some files must not be compiled when building against Mingw # they correspond to features not used by our host development tools # which are also hard or even impossible to port to native Win32 -WITH_MINGW := +WINDOWS_HOST_ONLY := ifeq ($(HOST_OS),windows) ifeq ($(strip $(USE_CYGWIN)),) - WITH_MINGW := 1 + WINDOWS_HOST_ONLY := 1 endif endif # USE_MINGW is defined when we build against Mingw on Linux ifneq ($(strip $(USE_MINGW)),) - WITH_MINGW := 1 + WINDOWS_HOST_ONLY := 1 endif -ifeq ($(WITH_MINGW),1) +ifeq ($(WINDOWS_HOST_ONLY),1) commonSources += \ uio.c else commonSources += \ + abort_socket.c \ mspace.c \ selector.c \ tztime.c \ - tzstrftime.c \ adb_networking.c \ - zygote.c + zygote.c + + commonHostSources += \ + tzstrftime.c endif # Static library for host # ======================================================== LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) ashmem-host.c +LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := liblog include $(BUILD_HOST_STATIC_LIBRARY) @@ -81,7 +87,7 @@ ifeq ($(TARGET_SIMULATOR),true) # ======================================================== include $(CLEAR_VARS) LOCAL_MODULE := libcutils -LOCAL_SRC_FILES := $(commonSources) memory.c dlmalloc_stubs.c ashmem-host.c +LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) memory.c dlmalloc_stubs.c LOCAL_LDLIBS := -lpthread LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_SHARED_LIBRARY) diff --git a/libcutils/abort_socket.c b/libcutils/abort_socket.c new file mode 100644 index 0000000..6a5e5e4 --- /dev/null +++ b/libcutils/abort_socket.c @@ -0,0 +1,293 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/socket.h> +#include <sys/poll.h> + +#include "cutils/abort_socket.h" + +struct asocket *asocket_init(int fd) { + int abort_fd[2]; + int flags; + struct asocket *s; + + /* set primary socket to non-blocking */ + flags = fcntl(fd, F_GETFL); + if (flags == -1) + return NULL; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) + return NULL; + + /* create pipe with non-blocking write, so that asocket_close() cannot + block */ + if (pipe(abort_fd)) + return NULL; + flags = fcntl(abort_fd[1], F_GETFL); + if (flags == -1) + return NULL; + if (fcntl(abort_fd[1], F_SETFL, flags | O_NONBLOCK)) + return NULL; + + s = malloc(sizeof(struct asocket)); + if (!s) + return NULL; + + s->fd = fd; + s->abort_fd[0] = abort_fd[0]; + s->abort_fd[1] = abort_fd[1]; + + return s; +} + +int asocket_connect(struct asocket *s, const struct sockaddr *addr, + socklen_t addrlen, int timeout) { + + int ret; + + do { + ret = connect(s->fd, addr, addrlen); + } while (ret && errno == EINTR); + + if (ret && errno == EINPROGRESS) { + /* ready to poll() */ + socklen_t retlen; + struct pollfd pfd[2]; + + pfd[0].fd = s->fd; + pfd[0].events = POLLOUT; + pfd[0].revents = 0; + pfd[1].fd = s->abort_fd[0]; + pfd[1].events = POLLIN; + pfd[1].revents = 0; + + do { + ret = poll(pfd, 2, timeout); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -1; + else if (ret == 0) { + /* timeout */ + errno = ETIMEDOUT; + return -1; + } + + if (pfd[1].revents) { + /* abort due to asocket_abort() */ + errno = ECANCELED; + return -1; + } + + if (pfd[0].revents) { + if (pfd[0].revents & POLLOUT) { + /* connect call complete, read return code */ + retlen = sizeof(ret); + if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen)) + return -1; + /* got connect() return code */ + if (ret) { + errno = ret; + } + } else { + /* some error event on this fd */ + errno = ECONNABORTED; + return -1; + } + } + } + + return ret; +} + +int asocket_accept(struct asocket *s, struct sockaddr *addr, + socklen_t *addrlen, int timeout) { + + int ret; + struct pollfd pfd[2]; + + pfd[0].fd = s->fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = s->abort_fd[0]; + pfd[1].events = POLLIN; + pfd[1].revents = 0; + + do { + ret = poll(pfd, 2, timeout); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -1; + else if (ret == 0) { + /* timeout */ + errno = ETIMEDOUT; + return -1; + } + + if (pfd[1].revents) { + /* abort due to asocket_abort() */ + errno = ECANCELED; + return -1; + } + + if (pfd[0].revents) { + if (pfd[0].revents & POLLIN) { + /* ready to accept() without blocking */ + do { + ret = accept(s->fd, addr, addrlen); + } while (ret < 0 && errno == EINTR); + } else { + /* some error event on this fd */ + errno = ECONNABORTED; + return -1; + } + } + + return ret; +} + +int asocket_read(struct asocket *s, void *buf, size_t count, int timeout) { + int ret; + struct pollfd pfd[2]; + + pfd[0].fd = s->fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = s->abort_fd[0]; + pfd[1].events = POLLIN; + pfd[1].revents = 0; + + do { + ret = poll(pfd, 2, timeout); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -1; + else if (ret == 0) { + /* timeout */ + errno = ETIMEDOUT; + return -1; + } + + if (pfd[1].revents) { + /* abort due to asocket_abort() */ + errno = ECANCELED; + return -1; + } + + if (pfd[0].revents) { + if (pfd[0].revents & POLLIN) { + /* ready to read() without blocking */ + do { + ret = read(s->fd, buf, count); + } while (ret < 0 && errno == EINTR); + } else { + /* some error event on this fd */ + errno = ECONNABORTED; + return -1; + } + } + + return ret; +} + +int asocket_write(struct asocket *s, const void *buf, size_t count, + int timeout) { + int ret; + struct pollfd pfd[2]; + + pfd[0].fd = s->fd; + pfd[0].events = POLLOUT; + pfd[0].revents = 0; + pfd[1].fd = s->abort_fd[0]; + pfd[1].events = POLLIN; + pfd[1].revents = 0; + + do { + ret = poll(pfd, 2, timeout); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) + return -1; + else if (ret == 0) { + /* timeout */ + errno = ETIMEDOUT; + return -1; + } + + if (pfd[1].revents) { + /* abort due to asocket_abort() */ + errno = ECANCELED; + return -1; + } + + if (pfd[0].revents) { + if (pfd[0].revents & POLLOUT) { + /* ready to write() without blocking */ + do { + ret = write(s->fd, buf, count); + } while (ret < 0 && errno == EINTR); + } else { + /* some error event on this fd */ + errno = ECONNABORTED; + return -1; + } + } + + return ret; +} + +void asocket_abort(struct asocket *s) { + int ret; + char buf = 0; + + /* Prevent further use of fd, without yet releasing the fd */ + shutdown(s->fd, SHUT_RDWR); + + /* wake up calls blocked at poll() */ + do { + ret = write(s->abort_fd[1], &buf, 1); + } while (ret < 0 && errno == EINTR); +} + +void asocket_destroy(struct asocket *s) { + struct asocket s_copy = *s; + + /* Clients should *not* be using these fd's after calling + asocket_destroy(), but in case they do, set to -1 so they cannot use a + stale fd */ + s->fd = -1; + s->abort_fd[0] = -1; + s->abort_fd[1] = -1; + + /* Call asocket_abort() in case there are still threads blocked on this + socket. Clients should not rely on this behavior - it is racy because we + are about to close() these sockets - clients should instead make sure + all threads are done with the socket before calling asocket_destory(). + */ + asocket_abort(&s_copy); + + /* enough safety checks, close and release memory */ + close(s_copy.abort_fd[1]); + close(s_copy.abort_fd[0]); + close(s_copy.fd); + + free(s); +} diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c index fadaabe..1a8ba86 100644 --- a/libcutils/strdup16to8.c +++ b/libcutils/strdup16to8.c @@ -15,6 +15,8 @@ ** limitations under the License. */ +#include <limits.h> /* for SIZE_MAX */ + #include <cutils/jstring.h> #include <assert.h> #include <stdlib.h> @@ -26,19 +28,67 @@ */ extern size_t strnlen16to8(const char16_t* utf16Str, size_t len) { - size_t utf8Len = 0; - - while (len--) { - unsigned int uic = *utf16Str++; - - if (uic > 0x07ff) - utf8Len += 3; - else if (uic > 0x7f || uic == 0) - utf8Len += 2; - else - utf8Len++; - } - return utf8Len; + size_t utf8Len = 0; + + /* A small note on integer overflow. The result can + * potentially be as big as 3*len, which will overflow + * for len > SIZE_MAX/3. + * + * Moreover, the result of a strnlen16to8 is typically used + * to allocate a destination buffer to strncpy16to8 which + * requires one more byte to terminate the UTF-8 copy, and + * this is generally done by careless users by incrementing + * the result without checking for integer overflows, e.g.: + * + * dst = malloc(strnlen16to8(utf16,len)+1) + * + * Due to this, the following code will try to detect + * overflows, and never return more than (SIZE_MAX-1) + * when it detects one. A careless user will try to malloc + * SIZE_MAX bytes, which will return NULL which can at least + * be detected appropriately. + * + * As far as I know, this function is only used by strndup16(), + * but better be safe than sorry. + */ + + /* Fast path for the usual case where 3*len is < SIZE_MAX-1. + */ + if (len < (SIZE_MAX-1)/3) { + while (len--) { + unsigned int uic = *utf16Str++; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + } + return utf8Len; + } + + /* The slower but paranoid version */ + while (len--) { + unsigned int uic = *utf16Str++; + size_t utf8Cur = utf8Len; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + + if (utf8Len < utf8Cur) /* overflow detected */ + return SIZE_MAX-1; + } + + /* don't return SIZE_MAX to avoid common user bug */ + if (utf8Len == SIZE_MAX) + utf8Len = SIZE_MAX-1; + + return utf8Len; } @@ -50,7 +100,7 @@ extern size_t strnlen16to8(const char16_t* utf16Str, size_t len) * * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1, * not just "len". - * + * * Please note, a terminated \0 is always added, so your result will always * be "strlen16to8() + 1" bytes long. */ @@ -58,6 +108,10 @@ extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) { char* utf8cur = utf8Str; + /* Note on overflows: We assume the user did check the result of + * strnlen16to8() properly or at a minimum checked the result of + * its malloc(SIZE_MAX) in case of overflow. + */ while (len--) { unsigned int uic = *utf16Str++; @@ -73,8 +127,8 @@ extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) if (uic == 0) { break; - } - } + } + } } *utf8cur = '\0'; @@ -85,20 +139,30 @@ extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) /** * Convert a UTF-16 string to UTF-8. * - * Make sure you allocate "dest" with the result of strblen16to8(), - * not just "strlen16()". */ char * strndup16to8 (const char16_t* s, size_t n) { - char *ret; + char* ret; + size_t len; if (s == NULL) { return NULL; } - ret = malloc(strnlen16to8(s, n) + 1); + len = strnlen16to8(s, n); + + /* We are paranoid, and we check for SIZE_MAX-1 + * too since it is an overflow value for our + * strnlen16to8 implementation. + */ + if (len >= SIZE_MAX-1) + return NULL; + + ret = malloc(len + 1); + if (ret == NULL) + return NULL; strncpy16to8 (ret, s, n); - - return ret; + + return ret; } |
