Files
libgbinder/unit/unit_local_request/unit_local_request.c
Ratchanan Srirattanamet a9dfd6e453 writer: don't write object offset for NULL binder object
Writing offset will trigger the kernel-side code to transform the flat
binder object into the handle form, which (in my understanding) is not
a valid operation for a NULL binder object. Meanwhile, the receiving
side will create a corresponding Binder object from such handle,
tripping the stability check as it will no longer accept UNDECLARED.

OTOH, if the offset is not written, then the receiving side will receive
the flat binder object as-is, with type BINDER and pointer NULL, which
will be interpreted as NULL binder. This is also what Android's
Parcel.cpp does [1][2].

IMO, this is sort of a hack. Binder kernel driver should handle the NULL
binder internally, and not relying on the sender doing the correct
thing. Meanwhile, the receiver should always reject a flat binder object
of type BINDER. But that's how Android work, so... 🤷

[1]: https://github.com/LineageOS/android_frameworks_native/blob/lineage-19.1/libs/binder/Parcel.cpp#L1327-L1332
[2]: https://github.com/LineageOS/android_frameworks_native/blob/lineage-19.1/libs/binder/Parcel.cpp#L2023-L2029

Origin: vendor
Forwarded: https://github.com/mer-hybris/libgbinder/pull/135
2025-10-17 23:29:53 +08:00

651 lines
22 KiB
C

/*
* Copyright (C) 2018-2024 Slava Monich <slava@monich.com>
* Copyright (C) 2018-2022 Jolla Ltd.
*
* 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_common.h"
#include "test_binder.h"
#include "gbinder_local_object.h"
#include "gbinder_local_request_p.h"
#include "gbinder_output_data.h"
#include "gbinder_rpc_protocol.h"
#include "gbinder_buffer_p.h"
#include "gbinder_driver.h"
#include "gbinder_writer.h"
#include "gbinder_io.h"
#include "gbinder_ipc.h"
#include <gutil_intarray.h>
static TestOpt test_opt;
static const char TMP_DIR_TEMPLATE[] = "gbinder-test-local-request-XXXXXX";
static
void
test_int_inc(
void* data)
{
(*((int*)data))++;
}
static
GBinderBuffer*
test_buffer_from_bytes(
GBinderDriver* driver,
const GByteArray* bytes)
{
/* Prevent double free */
test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
return gbinder_buffer_new(driver, bytes->data, bytes->len, NULL);
}
static
GBinderBuffer*
test_buffer_from_bytes_and_objects(
GBinderDriver* driver,
const GByteArray* bytes,
void** objects)
{
/* Prevent double free */
test_binder_set_destroy(gbinder_driver_fd(driver), bytes->data, NULL);
return gbinder_buffer_new(driver, bytes->data, bytes->len, objects);
}
static
GBinderLocalRequest*
test_local_request_new()
{
return gbinder_local_request_new(&gbinder_io_32,
gbinder_rpc_protocol_for_device(NULL), NULL);
}
/*==========================================================================*
* null
*==========================================================================*/
static
void
test_null(
void)
{
GBinderWriter writer;
int count = 0;
g_assert(!gbinder_local_request_new(NULL, NULL, NULL));
g_assert(!gbinder_local_request_new(&gbinder_io_32, NULL, NULL));
g_assert(!gbinder_local_request_new(NULL,
gbinder_rpc_protocol_for_device(NULL), NULL));
g_assert(!gbinder_local_request_ref(NULL));
g_assert(!gbinder_local_request_new_from_data(NULL, NULL));
gbinder_local_request_unref(NULL);
gbinder_local_request_init_writer(NULL, NULL);
gbinder_local_request_init_writer(NULL, &writer);
gbinder_local_request_cleanup(NULL, NULL, NULL);
gbinder_local_request_cleanup(NULL, test_int_inc, &count);
g_assert(count == 1);
g_assert(!gbinder_local_request_data(NULL));
g_assert(!gbinder_local_request_append_bool(NULL, FALSE));
g_assert(!gbinder_local_request_append_int32(NULL, 0));
g_assert(!gbinder_local_request_append_int64(NULL, 0));
g_assert(!gbinder_local_request_append_float(NULL, 0));
g_assert(!gbinder_local_request_append_double(NULL, 0));
g_assert(!gbinder_local_request_append_string8(NULL, NULL));
g_assert(!gbinder_local_request_append_string16(NULL, NULL));
g_assert(!gbinder_local_request_append_hidl_string(NULL, NULL));
g_assert(!gbinder_local_request_append_hidl_string_vec(NULL, NULL, 0));
g_assert(!gbinder_local_request_append_local_object(NULL, NULL));
g_assert(!gbinder_local_request_append_remote_object(NULL, NULL));
}
/*==========================================================================*
* cleanup
*==========================================================================*/
static
void
test_cleanup(
void)
{
GBinderLocalRequest* req = test_local_request_new();
int count = 0;
gbinder_local_request_cleanup(req, NULL, &count);
gbinder_local_request_cleanup(req, test_int_inc, &count);
gbinder_local_request_cleanup(req, test_int_inc, &count);
g_assert(!count);
gbinder_local_request_unref(req);
g_assert(count == 2);
}
/*==========================================================================*
* init_data
*==========================================================================*/
static
void
test_init_data(
void)
{
const guint8 init_data[] = { 0x01, 0x02, 0x03, 0x04 };
GBytes* init_bytes = g_bytes_new_static(init_data, sizeof(init_data));
GBinderLocalRequest* req = gbinder_local_request_new
(&gbinder_io_32, gbinder_rpc_protocol_for_device(NULL), init_bytes);
GBinderOutputData* data;
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(init_data));
g_assert(!memcmp(data->bytes->data, init_data, data->bytes->len));
g_assert(gbinder_local_request_ref(req) == req);
gbinder_local_request_unref(req);
gbinder_local_request_unref(req);
req = test_local_request_new();
data = gbinder_local_request_data(req);
g_assert(data->bytes);
g_assert(!data->bytes->len);
gbinder_local_request_unref(req);
g_bytes_unref(init_bytes);
}
/*==========================================================================*
* bool
*==========================================================================*/
static
void
test_bool(
void)
{
static const guint8 output_true[] = { 0x01, 0x00, 0x00, 0x00 };
static const guint8 output_false[] = { 0x00, 0x00, 0x00, 0x00 };
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_bool(req, FALSE);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(output_false));
g_assert(!memcmp(data->bytes->data, output_false, data->bytes->len));
gbinder_local_request_unref(req);
req = test_local_request_new();
gbinder_local_request_append_bool(req, TRUE);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(output_true));
g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len));
gbinder_local_request_unref(req);
req = test_local_request_new();
gbinder_local_request_append_bool(req, 42);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(output_true));
g_assert(!memcmp(data->bytes->data, output_true, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* int32
*==========================================================================*/
static
void
test_int32(
void)
{
const guint32 value = 1234567;
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_int32(req, value);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(value));
g_assert(!memcmp(data->bytes->data, &value, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* int64
*==========================================================================*/
static
void
test_int64(
void)
{
const guint64 value = 123456789;
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_int64(req, value);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(value));
g_assert(!memcmp(data->bytes->data, &value, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* float
*==========================================================================*/
static
void
test_float(
void)
{
const gfloat value = 123456789;
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_float(req, value);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(value));
g_assert(!memcmp(data->bytes->data, &value, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* double
*==========================================================================*/
static
void
test_double(
void)
{
const gdouble value = 123456789;
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_double(req, value);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(value));
g_assert(!memcmp(data->bytes->data, &value, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* string8
*==========================================================================*/
static
void
test_string8(
void)
{
/* The size of the string gets aligned at 4-byte boundary */
static const char input[] = "test";
static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 };
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_string8(req, input);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(output));
g_assert(!memcmp(data->bytes->data, output, data->bytes->len));
gbinder_local_request_unref(req);
/* NULL string doesn't get encoded at all (should it be?) */
req = test_local_request_new();
gbinder_local_request_append_string8(req, NULL);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(!data->bytes->len);
gbinder_local_request_unref(req);
}
/*==========================================================================*
* string16
*==========================================================================*/
static
void
test_string16(
void)
{
static const char input[] = "x";
static const guint8 output[] = {
TEST_INT32_BYTES(1),
TEST_INT16_BYTES('x'), 0x00, 0x00
};
const gint32 null_output = -1;
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_string16(req, input);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(output));
g_assert(!memcmp(data->bytes->data, output, data->bytes->len));
gbinder_local_request_unref(req);
/* NULL string gets encoded as -1 */
req = test_local_request_new();
gbinder_local_request_append_string16(req, NULL);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == sizeof(null_output));
g_assert(!memcmp(data->bytes->data, &null_output, data->bytes->len));
gbinder_local_request_unref(req);
}
/*==========================================================================*
* hidl_string
*==========================================================================*/
static
void
test_hidl_string(
void)
{
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
GUtilIntArray* offsets;
gbinder_local_request_append_hidl_string(req, NULL);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets->count == 2);
g_assert(offsets->data[0] == 0);
g_assert(gbinder_output_data_buffers_size(data)==sizeof(GBinderHidlString));
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
}
/*==========================================================================*
* hidl_string_vec
*==========================================================================*/
static
void
test_hidl_string_vec(
void)
{
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
GUtilIntArray* offsets;
gbinder_local_request_append_hidl_string_vec(req, NULL, 0);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets->count == 2);
g_assert(offsets->data[0] == 0);
g_assert(gbinder_output_data_buffers_size(data) == sizeof(GBinderHidlVec));
g_assert(data->bytes->len == 2*BUFFER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
}
/*==========================================================================*
* local_object
*==========================================================================*/
static
void
test_local_object(
void)
{
GBinderLocalRequest* req;
GBinderOutputData* data;
GUtilIntArray* offsets;
GBinderIpc* ipc = gbinder_ipc_new(NULL, NULL);
const char* const ifaces[] = { "android.hidl.base@1.0::IBase", NULL };
GBinderLocalObject* obj = gbinder_local_object_new(ipc, ifaces, NULL, NULL);
/* Append a real object */
req = test_local_request_new();
gbinder_local_request_append_local_object(req, obj);
data = gbinder_local_request_data(req);
offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
g_assert_cmpuint(offsets->count, == ,1);
g_assert_cmpuint(offsets->data[0], == ,0);
g_assert_cmpuint(gbinder_output_data_buffers_size(data), == ,0);
g_assert_cmpuint(data->bytes->len, == ,BINDER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
/* Append NULL object */
req = test_local_request_new();
gbinder_local_request_append_local_object(req, NULL);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
gbinder_local_object_unref(obj);
gbinder_ipc_unref(ipc);
}
/*==========================================================================*
* remote_object
*==========================================================================*/
static
void
test_remote_object(
void)
{
GBinderLocalRequest* req = test_local_request_new();
GBinderOutputData* data;
gbinder_local_request_append_remote_object(req, NULL);
data = gbinder_local_request_data(req);
g_assert(!gbinder_output_data_offsets(data));
g_assert(!gbinder_output_data_buffers_size(data));
g_assert(data->bytes->len == BINDER_OBJECT_SIZE_32);
gbinder_local_request_unref(req);
}
/*==========================================================================*
* remote_request
*==========================================================================*/
static
void
test_remote_request(
void)
{
/* The size of the string gets aligned at 4-byte boundary */
static const char input[] = "test";
static const guint8 output[] = { 't', 'e', 's', 't', 0, 0, 0, 0 };
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(driver);
const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, protocol, NULL);
GBinderLocalRequest* req2;
GBinderOutputData* data2;
const GByteArray* bytes;
const GByteArray* bytes2;
GBinderBuffer* buffer;
void** no_obj = g_new0(void*, 1);
gbinder_local_request_append_string8(req, input);
bytes = gbinder_local_request_data(req)->bytes;
/* Copy flat structures (no binder objects) */
buffer = test_buffer_from_bytes(driver, bytes);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
bytes2 = data2->bytes;
g_assert(!gbinder_output_data_offsets(data2));
g_assert(!gbinder_output_data_buffers_size(data2));
g_assert(bytes2->len == sizeof(output));
g_assert(!memcmp(bytes2->data, output, bytes2->len));
gbinder_local_request_unref(req2);
/* Same thing but with non-NULL (albeit empty) array of objects */
buffer = test_buffer_from_bytes_and_objects(driver, bytes, no_obj);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
data2 = gbinder_local_request_data(req2);
bytes2 = data2->bytes;
g_assert(!gbinder_output_data_offsets(data2));
g_assert(!gbinder_output_data_buffers_size(data2));
g_assert(bytes2->len == sizeof(output));
g_assert(!memcmp(bytes2->data, output, bytes2->len));
gbinder_local_request_unref(req2);
gbinder_local_request_unref(req);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* remote_request_obj
*==========================================================================*/
static
void
test_remote_request_obj_validate_data(
GBinderOutputData* data)
{
const GByteArray* bytes = data->bytes;
GUtilIntArray* offsets = gbinder_output_data_offsets(data);
g_assert(offsets);
g_assert(offsets->count == 2);
g_assert(offsets->data[0] == 4);
g_assert(offsets->data[1] == 4 + BUFFER_OBJECT_SIZE_64);
g_assert(bytes->len == 4 + 2*BUFFER_OBJECT_SIZE_64 + BINDER_OBJECT_SIZE_64);
/* GBinderHidlString + the contents (2 bytes) aligned at 8-byte boundary */
g_assert(gbinder_output_data_buffers_size(data) ==
(sizeof(GBinderHidlString) + 8));
}
static
void
test_remote_request_obj(
void)
{
GBinderDriver* driver = gbinder_driver_new(GBINDER_DEFAULT_BINDER, NULL);
const GBinderIo* io = gbinder_driver_io(driver);
const GBinderRpcProtocol* protocol = gbinder_driver_protocol(driver);
GBinderLocalRequest* req = gbinder_local_request_new(io, protocol, NULL);
GBinderLocalRequest* req2;
GBinderOutputData* data;
GUtilIntArray* offsets;
GBinderBuffer* buffer;
const GByteArray* bytes;
void** objects;
guint i;
gbinder_local_request_append_int32(req, 1);
gbinder_local_request_append_hidl_string(req, "2");
gbinder_local_request_append_local_object(req, NULL);
data = gbinder_local_request_data(req);
test_remote_request_obj_validate_data(data);
bytes = data->bytes;
offsets = gbinder_output_data_offsets(data);
objects = g_new0(void*, offsets->count + 1);
for (i = 0; i < offsets->count; i++) {
objects[i] = bytes->data + offsets->data[i];
}
buffer = test_buffer_from_bytes_and_objects(driver, data->bytes, objects);
req2 = gbinder_local_request_new_from_data(buffer, NULL);
gbinder_buffer_free(buffer);
test_remote_request_obj_validate_data(gbinder_local_request_data(req2));
/* req2 has to be freed first because req owns data */
gbinder_local_request_unref(req2);
gbinder_local_request_unref(req);
gbinder_driver_unref(driver);
}
/*==========================================================================*
* Common
*==========================================================================*/
#define TEST_PREFIX "/local_request/"
int main(int argc, char* argv[])
{
TestConfig test_config;
int result;
g_test_init(&argc, &argv, NULL);
g_test_add_func(TEST_PREFIX "null", test_null);
g_test_add_func(TEST_PREFIX "cleanup", test_cleanup);
g_test_add_func(TEST_PREFIX "init_data", test_init_data);
g_test_add_func(TEST_PREFIX "bool", test_bool);
g_test_add_func(TEST_PREFIX "int32", test_int32);
g_test_add_func(TEST_PREFIX "int64", test_int64);
g_test_add_func(TEST_PREFIX "float", test_float);
g_test_add_func(TEST_PREFIX "double", test_double);
g_test_add_func(TEST_PREFIX "string8", test_string8);
g_test_add_func(TEST_PREFIX "string16", test_string16);
g_test_add_func(TEST_PREFIX "hidl_string", test_hidl_string);
g_test_add_func(TEST_PREFIX "hidl_string_vec", test_hidl_string_vec);
g_test_add_func(TEST_PREFIX "local_object", test_local_object);
g_test_add_func(TEST_PREFIX "remote_object", test_remote_object);
g_test_add_func(TEST_PREFIX "remote_request", test_remote_request);
g_test_add_func(TEST_PREFIX "remote_request_obj", test_remote_request_obj);
test_init(&test_opt, argc, argv);
test_config_init(&test_config, TMP_DIR_TEMPLATE);
result = g_test_run();
test_config_cleanup(&test_config);
return result;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/