diff --git a/include/gbinder_reader.h b/include/gbinder_reader.h index 4866299..a98561a 100644 --- a/include/gbinder_reader.h +++ b/include/gbinder_reader.h @@ -95,6 +95,15 @@ gbinder_reader_read_double( GBinderReader* reader, gdouble* value); +int +gbinder_reader_read_fd( + GBinderReader* reader); /* Since 1.0.18 */ + +int +gbinder_reader_read_dup_fd( + GBinderReader* reader) /* Since 1.0.18 */ + G_GNUC_WARN_UNUSED_RESULT; + gboolean gbinder_reader_read_nullable_object( GBinderReader* reader, diff --git a/include/gbinder_writer.h b/include/gbinder_writer.h index 57d669f..63ab0f6 100644 --- a/include/gbinder_writer.h +++ b/include/gbinder_writer.h @@ -114,6 +114,11 @@ gbinder_writer_append_bytes( const void* data, gsize size); +void +gbinder_writer_append_fd( + GBinderWriter* writer, + int fd); /* Since 1.0.18 */ + guint gbinder_writer_append_buffer_object_with_parent( GBinderWriter* writer, diff --git a/src/gbinder_cleanup.c b/src/gbinder_cleanup.c index 825ffaa..9e82446 100644 --- a/src/gbinder_cleanup.c +++ b/src/gbinder_cleanup.c @@ -13,9 +13,9 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Jolla Ltd nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -48,11 +48,33 @@ struct gbinder_cleanup { G_STATIC_ASSERT(sizeof(GBinderCleanup) == sizeof(GArray)); #define ELEMENT_SIZE (sizeof(GBinderCleanupItem)) +static +void +gbinder_cleanup_destroy_func( + gpointer data) +{ + GBinderCleanupItem* item = data; + + item->destroy(item->pointer); +} + static GBinderCleanup* gbinder_cleanup_new() { - return (GBinderCleanup*)g_array_sized_new(FALSE, FALSE, ELEMENT_SIZE, 0); + GArray* array = g_array_sized_new(FALSE, FALSE, ELEMENT_SIZE, 0); + + g_array_set_clear_func(array, gbinder_cleanup_destroy_func); + return (GBinderCleanup*)array; +} + +void +gbinder_cleanup_reset( + GBinderCleanup* self) +{ + if (G_LIKELY(self)) { + g_array_set_size((GArray*)self, 0); + } } void @@ -60,11 +82,6 @@ gbinder_cleanup_free( GBinderCleanup* self) { if (G_LIKELY(self)) { - guint i; - - for (i = 0; i < self->count; i++) { - self->items[i].destroy(self->items[i].pointer); - } g_array_free((GArray*)self, TRUE); } } diff --git a/src/gbinder_cleanup.h b/src/gbinder_cleanup.h index c952553..ac44c31 100644 --- a/src/gbinder_cleanup.h +++ b/src/gbinder_cleanup.h @@ -13,9 +13,9 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Jolla Ltd nor the names of its contributors may - * be used to endorse or promote products derived from this software - * without specific prior written permission. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE @@ -39,6 +39,10 @@ void gbinder_cleanup_free( GBinderCleanup* cleanup); +void +gbinder_cleanup_reset( + GBinderCleanup* cleanup); + GBinderCleanup* gbinder_cleanup_add( GBinderCleanup* cleanup, diff --git a/src/gbinder_io.c b/src/gbinder_io.c index c5c4c67..b6173fe 100644 --- a/src/gbinder_io.c +++ b/src/gbinder_io.c @@ -153,6 +153,21 @@ GBINDER_IO_FN(encode_remote_object)( return sizeof(*dest); } +static +guint +GBINDER_IO_FN(encode_fd_object)( + void* out, + int fd) +{ + struct flat_binder_object* dest = out; + + memset(dest, 0, sizeof(*dest)); + dest->hdr.type = BINDER_TYPE_FD; + dest->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + dest->handle = fd; + return sizeof(*dest); +} + /* Encodes binder_buffer_object */ static guint @@ -488,6 +503,7 @@ const GBinderIo GBINDER_IO_PREFIX = { .encode_pointer = GBINDER_IO_FN(encode_pointer), .encode_local_object = GBINDER_IO_FN(encode_local_object), .encode_remote_object = GBINDER_IO_FN(encode_remote_object), + .encode_fd_object = GBINDER_IO_FN(encode_fd_object), .encode_buffer_object = GBINDER_IO_FN(encode_buffer_object), .encode_death_notification = GBINDER_IO_FN(encode_death_notification), .encode_transaction = GBINDER_IO_FN(encode_transaction), diff --git a/src/gbinder_io.h b/src/gbinder_io.h index edcf393..02b9c62 100644 --- a/src/gbinder_io.h +++ b/src/gbinder_io.h @@ -131,6 +131,7 @@ struct gbinder_io { #define GBINDER_MAX_BINDER_OBJECT_SIZE (24) guint (*encode_local_object)(void* out, GBinderLocalObject* obj); guint (*encode_remote_object)(void* out, GBinderRemoteObject* obj); + guint (*encode_fd_object)(void* out, int fd); /* Encode binder_buffer_object */ #define GBINDER_MAX_BUFFER_OBJECT_SIZE (40) diff --git a/src/gbinder_reader.c b/src/gbinder_reader.c index 5d4745b..d3013f7 100644 --- a/src/gbinder_reader.c +++ b/src/gbinder_reader.c @@ -38,6 +38,9 @@ #include +#include +#include + typedef struct gbinder_reader_priv { const guint8* start; const guint8* end; @@ -231,16 +234,67 @@ gbinder_reader_read_double( } } +static +inline +gboolean +gbinder_reader_can_read_object( + GBinderReaderPriv* p) +{ + const GBinderReaderData* data = p->data; + + return data && data->reg && + p->objects && p->objects[0] && + p->ptr == p->objects[0]; +} + +int +gbinder_reader_read_fd( + GBinderReader* reader) /* Since 1.0.18 */ +{ + GBinderReaderPriv* p = gbinder_reader_cast(reader); + + if (gbinder_reader_can_read_object(p)) { + int fd; + const guint eaten = p->data->reg->io->decode_fd_object(p->ptr, + gbinder_reader_bytes_remaining(reader), &fd); + + if (eaten) { + GASSERT(fd >= 0); + p->ptr += eaten; + p->objects++; + return fd; + } + } + return -1; +} + +int +gbinder_reader_read_dup_fd( + GBinderReader* reader) /* Since 1.0.18 */ +{ + const int fd = gbinder_reader_read_fd(reader); + + if (fd >= 0) { + const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + + if (dupfd >= 0) { + return dupfd; + } else { + GWARN("Error dupping fd %d: %s", fd, strerror(errno)); + } + } + return -1; +} + gboolean gbinder_reader_read_nullable_object( GBinderReader* reader, GBinderRemoteObject** out) { GBinderReaderPriv* p = gbinder_reader_cast(reader); - const GBinderReaderData* data = p->data; - if (data && data->reg && p->objects && p->objects[0] && - p->ptr == p->objects[0]) { + if (gbinder_reader_can_read_object(p)) { + const GBinderReaderData* data = p->data; const guint eaten = data->reg->io->decode_binder_object(p->ptr, gbinder_reader_bytes_remaining(reader), data->reg, out); @@ -271,10 +325,9 @@ gbinder_reader_read_buffer_impl( GBinderBuffer** out) { GBinderReaderPriv* p = gbinder_reader_cast(reader); - const GBinderReaderData* data = p->data; - if (data && data->reg && p->objects && p->objects[0] && - p->ptr == p->objects[0]) { + if (gbinder_reader_can_read_object(p)) { + const GBinderReaderData* data = p->data; GBinderBuffer* buf = data->buffer; const GBinderIo* io = data->reg->io; const gsize offset = p->ptr - (guint8*)buf->data; diff --git a/src/gbinder_writer.c b/src/gbinder_writer.c index 30ad985..6590e97 100644 --- a/src/gbinder_writer.c +++ b/src/gbinder_writer.c @@ -39,7 +39,10 @@ #include #include +#include #include +#include +#include typedef struct gbinder_writer_priv { GBinderWriterData* data; @@ -74,6 +77,7 @@ gbinder_writer_data_set_contents( g_byte_array_set_size(data->bytes, 0); gutil_int_array_set_count(data->offsets, 0); data->buffers_size = 0; + gbinder_cleanup_reset(data->cleanup); g_byte_array_append(data->bytes, bufdata, bufsize); if (contents) { @@ -521,6 +525,60 @@ gbinder_writer_data_prepare( return data->offsets->count; } +static +void +gbinder_writer_data_close_fd( + gpointer data) +{ + const int fd = GPOINTER_TO_INT(data); + + if (close(fd) < 0) { + GWARN("Error closing fd %d: %s", fd, strerror(errno)); + } +} + +static +void +gbinder_writer_data_append_fd( + GBinderWriterData* data, + int fd) +{ + GByteArray* buf = data->bytes; + const guint offset = buf->len; + /* Duplicate the descriptor so that caller can do whatever with + * the one it passed in. */ + const int dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + guint written; + + /* Preallocate enough space */ + g_byte_array_set_size(buf, offset + GBINDER_MAX_BINDER_OBJECT_SIZE); + /* Write the original fd if we failed to dup it */ + if (dupfd < 0) { + GWARN("Error dupping fd %d: %s", fd, strerror(errno)); + written = data->io->encode_fd_object(buf->data + offset, fd); + } else { + written = data->io->encode_fd_object(buf->data + offset, dupfd); + data->cleanup = gbinder_cleanup_add(data->cleanup, + gbinder_writer_data_close_fd, GINT_TO_POINTER(dupfd)); + } + /* Fix the data size */ + g_byte_array_set_size(buf, offset + written); + /* Record the offset */ + gbinder_writer_data_record_offset(data, offset); +} + +void +gbinder_writer_append_fd( + GBinderWriter* self, + int fd) /* Since 1.0.18 */ +{ + GBinderWriterData* data = gbinder_writer_data(self); + + if (G_LIKELY(data)) { + gbinder_writer_data_append_fd(data, fd); + } +} + guint gbinder_writer_append_buffer_object_with_parent( GBinderWriter* self, diff --git a/test/binder-service/binder-service.c b/test/binder-service/binder-service.c index 87ca70e..9d21f0e 100644 --- a/test/binder-service/binder-service.c +++ b/test/binder-service/binder-service.c @@ -36,6 +36,9 @@ #include +#define BINDER_TRANSACTION(c2,c3,c4) GBINDER_FOURCC('_',c2,c3,c4) +#define BINDER_DUMP_TRANSACTION BINDER_TRANSACTION('D','M','P') + #define RET_OK (0) #define RET_NOTFOUND (1) #define RET_INVARG (2) @@ -83,12 +86,15 @@ app_reply( int* status, void* user_data) { + App* app = user_data; + GBinderReader reader; + + gbinder_remote_request_init_reader(req, &reader); if (code == GBINDER_FIRST_CALL_TRANSACTION) { - App* app = user_data; const char* iface = gbinder_remote_request_interface(req); if (!g_strcmp0(iface, app->opt->iface)) { - char* str = gbinder_remote_request_read_string16(req); + char* str = gbinder_reader_read_string16(&reader); GBinderLocalReply* reply = gbinder_local_object_new_reply(obj); GVERBOSE("\"%s\" %u", iface, code); @@ -100,6 +106,17 @@ app_reply( } else { GDEBUG("Unexpected interface \"%s\"", iface); } + } else if (code == BINDER_DUMP_TRANSACTION) { + int fd = gbinder_reader_read_fd(&reader); + const char* dump = "Sorry, I've got nothing to dump...\n"; + const gssize dump_len = strlen(dump); + + GDEBUG("Dump request from %d", gbinder_remote_request_sender_pid(req)); + if (write(fd, dump, dump_len) != dump_len) { + GERR("Failed to write dump: %s", strerror(errno)); + } + *status = 0; + return NULL; } *status = -1; return NULL; diff --git a/unit/Makefile b/unit/Makefile index a1d8218..a5b9b0f 100644 --- a/unit/Makefile +++ b/unit/Makefile @@ -3,6 +3,7 @@ all: %: @$(MAKE) -C unit_buffer $* + @$(MAKE) -C unit_cleanup $* @$(MAKE) -C unit_client $* @$(MAKE) -C unit_driver $* @$(MAKE) -C unit_ipc $* diff --git a/unit/coverage/run b/unit/coverage/run index da332ce..c32a2aa 100755 --- a/unit/coverage/run +++ b/unit/coverage/run @@ -5,6 +5,7 @@ TESTS="\ unit_buffer \ +unit_cleanup \ unit_client \ unit_driver \ unit_ipc \ diff --git a/unit/unit_cleanup/Makefile b/unit/unit_cleanup/Makefile new file mode 100644 index 0000000..981b220 --- /dev/null +++ b/unit/unit_cleanup/Makefile @@ -0,0 +1,5 @@ +# -*- Mode: makefile-gmake -*- + +EXE = unit_cleanup + +include ../common/Makefile diff --git a/unit/unit_cleanup/unit_cleanup.c b/unit/unit_cleanup/unit_cleanup.c new file mode 100644 index 0000000..572aea2 --- /dev/null +++ b/unit/unit_cleanup/unit_cleanup.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2018 Jolla Ltd. + * Copyright (C) 2018 Slava Monich + * + * You may use this file under the terms of BSD license as follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_binder.h" + +#include "gbinder_cleanup.h" + +static TestOpt test_opt; + +static +void +test_cleanup_inc( + gpointer data) +{ + (*((int*)data))++; +} + +/*==========================================================================* + * null + *==========================================================================*/ + +static +void +test_null( + void) +{ + g_assert(!gbinder_cleanup_add(NULL, NULL, NULL)); + gbinder_cleanup_free(NULL); + gbinder_cleanup_reset(NULL); +} + +/*==========================================================================* + * basic + *==========================================================================*/ + +static +void +test_basic( + void) +{ + int n1 = 0, n2 =0; + GBinderCleanup* cleanup = gbinder_cleanup_add(NULL, test_cleanup_inc, &n1); + + g_assert(cleanup); + g_assert(gbinder_cleanup_add(cleanup, test_cleanup_inc, &n2) == cleanup); + gbinder_cleanup_free(cleanup); + g_assert(n1 == 1); + g_assert(n2 == 1); +} + +/*==========================================================================* + * reset + *==========================================================================*/ + +static +void +test_reset( + void) +{ + int n1 = 0, n2 =0; + GBinderCleanup* cleanup = gbinder_cleanup_add(NULL, test_cleanup_inc, &n1); + + g_assert(cleanup); + g_assert(gbinder_cleanup_add(cleanup, test_cleanup_inc, &n2) == cleanup); + gbinder_cleanup_reset(cleanup); + g_assert(n1 == 1); + g_assert(n2 == 1); + + gbinder_cleanup_free(cleanup); + g_assert(n1 == 1); + g_assert(n2 == 1); +} + +/*==========================================================================* + * Common + *==========================================================================*/ + +#define TEST_PREFIX "/cleanup/" +#define TEST_(t) TEST_PREFIX t + +int main(int argc, char* argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func(TEST_("null"), test_null); + g_test_add_func(TEST_("basic"), test_basic); + g_test_add_func(TEST_("reset"), test_reset); + test_init(&test_opt, argc, argv); + return g_test_run(); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/unit/unit_reader/unit_reader.c b/unit/unit_reader/unit_reader.c index 0271e16..66a60f6 100644 --- a/unit/unit_reader/unit_reader.c +++ b/unit/unit_reader/unit_reader.c @@ -39,6 +39,9 @@ #include "gbinder_remote_object_p.h" #include "gbinder_io.h" +#include +#include + static TestOpt test_opt; typedef struct binder_buffer_object_64 { @@ -53,9 +56,12 @@ typedef struct binder_buffer_object_64 { guint64 parent_offset; } BinderObject64; -#define BINDER_TYPE_HANDLE GBINDER_FOURCC('s','h','*',0x85) -#define BINDER_TYPE_PTR GBINDER_FOURCC('p','t','*',0x85) +#define BINDER_TYPE_(c1,c2,c3) GBINDER_FOURCC(c1,c2,c3,0x85) +#define BINDER_TYPE_HANDLE BINDER_TYPE_('s','h','*') +#define BINDER_TYPE_PTR BINDER_TYPE_('p','t','*') +#define BINDER_TYPE_FD BINDER_TYPE_('f', 'd', '*') #define BINDER_BUFFER_FLAG_HAS_PARENT 0x01 +#define BINDER_FLAG_ACCEPTS_FDS 0x100 #define BUFFER_OBJECT_SIZE_64 (GBINDER_MAX_BUFFER_OBJECT_SIZE) G_STATIC_ASSERT(sizeof(BinderObject64) == BUFFER_OBJECT_SIZE_64); @@ -933,6 +939,256 @@ test_hidl_string_err( gbinder_ipc_unref(ipc); } +/*==========================================================================* + * fd_ok + *==========================================================================*/ + +static +void +test_fd_ok( + void) +{ + /* Using 64-bit I/O */ + const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_FD), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), + TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), + TEST_INT64_BYTES(0) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + data.objects = g_new(void*, 2); + data.objects[0] = buf->data; + data.objects[1] = NULL; + gbinder_reader_init(&reader, &data, 0, buf->size); + + g_assert(gbinder_reader_read_fd(&reader) == fd); + gbinder_driver_close_fds(ipc->driver, data.objects, + (guint8*)buf->data + buf->size); + /* The above call must have closed the descriptor */ + g_assert(close(fd) < 0); + + g_free(data.objects); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + +/*==========================================================================* + * fd_shortbuf + *==========================================================================*/ + +static +void +test_fd_shortbuf( + void) +{ + /* Using 64-bit I/O */ + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_FD), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + gbinder_reader_init(&reader, &data, 0, buf->size); + + g_assert(gbinder_reader_read_fd(&reader) < 0); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + +/*==========================================================================* + * fd_badtype + *==========================================================================*/ + +static +void +test_fd_badtype( + void) +{ + /* Using 64-bit I/O */ + const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_PTR), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), + TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), + TEST_INT64_BYTES(0) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + data.objects = g_new(void*, 2); + data.objects[0] = buf->data; + data.objects[1] = NULL; + gbinder_reader_init(&reader, &data, 0, buf->size); + + g_assert(gbinder_reader_read_fd(&reader) < 0); + gbinder_driver_close_fds(ipc->driver, data.objects, + (guint8*)buf->data + buf->size); + /* The above call doesn't close the descriptor */ + g_assert(close(fd) == 0); + + g_free(data.objects); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + +/*==========================================================================* + * dupfd_ok + *==========================================================================*/ + +static +void +test_dupfd_ok( + void) +{ + /* Using 64-bit I/O */ + const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_FD), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), + TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), + TEST_INT64_BYTES(0) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + int fd2; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + data.objects = g_new(void*, 2); + data.objects[0] = buf->data; + data.objects[1] = NULL; + gbinder_reader_init(&reader, &data, 0, buf->size); + + fd2 = gbinder_reader_read_dup_fd(&reader); + g_assert(fd2 >= 0); + g_assert(fd2 != fd); + gbinder_driver_close_fds(ipc->driver, data.objects, + (guint8*)buf->data + buf->size); + /* The above call closes fd*/ + g_assert(close(fd) < 0); + g_assert(close(fd2) == 0); + + g_free(data.objects); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + +/*==========================================================================* + * dupfd_badtype + *==========================================================================*/ + +static +void +test_dupfd_badtype( + void) +{ + /* Using 64-bit I/O */ + const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_PTR), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), + TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), + TEST_INT64_BYTES(0) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + data.objects = g_new(void*, 2); + data.objects[0] = buf->data; + data.objects[1] = NULL; + gbinder_reader_init(&reader, &data, 0, buf->size); + + g_assert(gbinder_reader_read_dup_fd(&reader) < 0); + gbinder_driver_close_fds(ipc->driver, data.objects, + (guint8*)buf->data + buf->size); + /* The above call doesn't close fd*/ + g_assert(close(fd) == 0); + + g_free(data.objects); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + +/*==========================================================================* + * dupfd_badfd + *==========================================================================*/ + +static +void +test_dupfd_badfd( + void) +{ + /* Using 64-bit I/O */ + const int fd = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); + const guint8 input[] = { + TEST_INT32_BYTES(BINDER_TYPE_FD), + TEST_INT32_BYTES(0x7f | BINDER_FLAG_ACCEPTS_FDS), + TEST_INT32_BYTES(fd), TEST_INT32_BYTES(0), + TEST_INT64_BYTES(0) + }; + GBinderIpc* ipc = gbinder_ipc_new(GBINDER_DEFAULT_HWBINDER, NULL); + GBinderBuffer* buf = gbinder_buffer_new(ipc->driver, + g_memdup(input, sizeof(input)), sizeof(input), NULL); + GBinderReaderData data; + GBinderReader reader; + + g_assert(ipc); + memset(&data, 0, sizeof(data)); + data.buffer = buf; + data.reg = gbinder_ipc_object_registry(ipc); + data.objects = g_new(void*, 2); + data.objects[0] = buf->data; + data.objects[1] = NULL; + gbinder_reader_init(&reader, &data, 0, buf->size); + + /* Invalidate the descriptor by closing it */ + g_assert(close(fd) == 0); + g_assert(gbinder_reader_read_dup_fd(&reader) < 0); + gbinder_driver_close_fds(ipc->driver, data.objects, + (guint8*)buf->data + buf->size); + + g_free(data.objects); + gbinder_buffer_free(buf); + gbinder_ipc_unref(ipc); +} + /*==========================================================================* * object *==========================================================================*/ @@ -1294,6 +1550,12 @@ int main(int argc, char* argv[]) g_free(path); } + g_test_add_func(TEST_("fd/ok"), test_fd_ok); + g_test_add_func(TEST_("fd/shortbuf"), test_fd_shortbuf); + g_test_add_func(TEST_("fd/badtype"), test_fd_badtype); + g_test_add_func(TEST_("dupfd/ok"), test_dupfd_ok); + g_test_add_func(TEST_("dupfd/badtype"), test_dupfd_badtype); + g_test_add_func(TEST_("dupfd/badfd"), test_dupfd_badfd); g_test_add_func(TEST_("object/valid"), test_object); g_test_add_func(TEST_("object/invalid"), test_object_invalid); g_test_add_func(TEST_("object/no_reg"), test_object_no_reg); diff --git a/unit/unit_writer/unit_writer.c b/unit/unit_writer/unit_writer.c index 1750e00..18ccc68 100644 --- a/unit/unit_writer/unit_writer.c +++ b/unit/unit_writer/unit_writer.c @@ -39,6 +39,8 @@ #include +#include + static TestOpt test_opt; #define BUFFER_OBJECT_SIZE_32 (24) @@ -77,6 +79,7 @@ test_null( gbinder_writer_append_string16_utf16(NULL, NULL, 0); gbinder_writer_append_bool(NULL, FALSE); gbinder_writer_append_bool(&writer, FALSE); + gbinder_writer_append_fd(NULL, 0); gbinder_writer_append_bytes(NULL, NULL, 0); gbinder_writer_append_bytes(&writer, NULL, 0); gbinder_writer_append_hidl_vec(NULL, NULL, 0, 0); @@ -706,6 +709,76 @@ test_parent( gbinder_local_request_unref(req); } +/*==========================================================================* + * fd + * fd_invalid + *==========================================================================*/ + +static +void +test_fd2( + int fd) +{ + GBinderLocalRequest* req = gbinder_local_request_new(&gbinder_io_32, NULL); + GBinderOutputData* data; + GUtilIntArray* offsets; + GBinderWriter writer; + + gbinder_local_request_init_writer(req, &writer); + gbinder_writer_append_fd(&writer, fd); + data = gbinder_local_request_data(req); + offsets = gbinder_output_data_offsets(data); + g_assert(offsets); + g_assert(offsets->count == 1); + g_assert(offsets->data[0] == 0); + g_assert(!gbinder_output_data_buffers_size(data)); + g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); + gbinder_local_request_unref(req); +} + +static +void +test_fd( + void) +{ + test_fd2(0); +} + +static +void +test_fd_invalid( + void) +{ + test_fd2(-1); +} + +/*==========================================================================* + * fd_close_error + *==========================================================================*/ + +static +void +test_fd_close_error( + void) +{ + const GBinderIo* io = &gbinder_io_32; + GBinderLocalRequest* req = gbinder_local_request_new(io, NULL); + GBinderOutputData* data; + GBinderWriter writer; + int fd = -1; + + gbinder_local_request_init_writer(req, &writer); + gbinder_writer_append_fd(&writer, STDOUT_FILENO); + data = gbinder_local_request_data(req); + g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32); + + /* Fetch duplicated fd and close it. That makes the second close + * done by gbinder_writer_data_close_fd() fail. */ + g_assert(io->decode_fd_object(data->bytes->data, data->bytes->len, &fd)); + g_assert(close(fd) == 0); + gbinder_local_request_unref(req); +} + /*==========================================================================* * local_object *==========================================================================*/ @@ -874,6 +947,9 @@ int main(int argc, char* argv[]) g_test_add_func(TEST_("buffer"), test_buffer); g_test_add_func(TEST_("parent"), test_parent); + g_test_add_func(TEST_("fd"), test_fd); + g_test_add_func(TEST_("fd_invalid"), test_fd_invalid); + g_test_add_func(TEST_("fd_close_error"), test_fd_close_error); g_test_add_func(TEST_("local_object"), test_local_object); g_test_add_func(TEST_("remote_object"), test_remote_object); g_test_add_func(TEST_("byte_array"), test_byte_array);