322 lines
12 KiB
C
322 lines
12 KiB
C
![]() |
/*
|
||
|
* Copyright(c) 2006 to 2020 ADLINK Technology Limited and others
|
||
|
*
|
||
|
* This program and the accompanying materials are made available under the
|
||
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
||
|
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
||
|
* v. 1.0 which is available at
|
||
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||
|
*
|
||
|
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
||
|
*/
|
||
|
#include <stdio.h>
|
||
|
#include "dds/dds.h"
|
||
|
#include "test_common.h"
|
||
|
|
||
|
static dds_entity_t participant, topic, reader, writer, read_condition, read_condition_unread;
|
||
|
|
||
|
static void create_entities (void)
|
||
|
{
|
||
|
char topicname[100];
|
||
|
struct dds_qos *qos;
|
||
|
|
||
|
create_unique_topic_name ("ddsc_return_loan_test", topicname, sizeof topicname);
|
||
|
participant = dds_create_participant (DDS_DOMAIN_DEFAULT, NULL, NULL);
|
||
|
CU_ASSERT_FATAL (participant > 0);
|
||
|
|
||
|
qos = dds_create_qos ();
|
||
|
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, 0);
|
||
|
dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 1);
|
||
|
topic = dds_create_topic (participant, &RoundTripModule_DataType_desc, topicname, qos, NULL);
|
||
|
CU_ASSERT_FATAL (topic > 0);
|
||
|
dds_delete_qos (qos);
|
||
|
|
||
|
writer = dds_create_writer (participant, topic, NULL, NULL);
|
||
|
CU_ASSERT_FATAL (writer > 0);
|
||
|
reader = dds_create_reader (participant, topic, NULL, NULL);
|
||
|
CU_ASSERT_FATAL (reader > 0);
|
||
|
read_condition = dds_create_readcondition (reader, DDS_ANY_STATE);
|
||
|
CU_ASSERT_FATAL (read_condition > 0);
|
||
|
read_condition_unread = dds_create_readcondition (reader, DDS_ANY_INSTANCE_STATE | DDS_ANY_VIEW_STATE | DDS_NOT_READ_SAMPLE_STATE);
|
||
|
CU_ASSERT_FATAL (read_condition > 0);
|
||
|
}
|
||
|
|
||
|
static void delete_entities (void)
|
||
|
{
|
||
|
dds_return_t result;
|
||
|
result = dds_delete (participant);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
}
|
||
|
|
||
|
CU_Test (ddsc_loan, bad_params, .init = create_entities, .fini = delete_entities)
|
||
|
{
|
||
|
dds_return_t result;
|
||
|
|
||
|
/* buf = NULL */
|
||
|
result = dds_return_loan (reader, NULL, -1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
result = dds_return_loan (reader, NULL, 0);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
result = dds_return_loan (reader, NULL, 1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
|
||
|
/* buf[0] = NULL, size > 0 */
|
||
|
void *buf = NULL;
|
||
|
result = dds_return_loan (reader, &buf, 1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
/* buf[0] != NULL, size <= 0 */
|
||
|
char dummy = 0;
|
||
|
buf = &dummy;
|
||
|
result = dds_return_loan (reader, &buf, 0);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
result = dds_return_loan (reader, &buf, -1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_BAD_PARAMETER);
|
||
|
|
||
|
/* not a reader or condition (checking only the ones we have at hand) */
|
||
|
result = dds_return_loan (participant, &buf, 1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_ILLEGAL_OPERATION);
|
||
|
result = dds_return_loan (topic, &buf, 1);
|
||
|
CU_ASSERT (result == DDS_RETCODE_ILLEGAL_OPERATION);
|
||
|
}
|
||
|
|
||
|
CU_Test (ddsc_loan, success, .init = create_entities, .fini = delete_entities)
|
||
|
{
|
||
|
const RoundTripModule_DataType s = {
|
||
|
.payload = {
|
||
|
._length = 1,
|
||
|
._buffer = (uint8_t[]) { 'a' }
|
||
|
}
|
||
|
};
|
||
|
const unsigned char zeros[3 * sizeof (s)] = { 0 };
|
||
|
dds_return_t result;
|
||
|
for (size_t i = 0; i < 3; i++)
|
||
|
{
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
}
|
||
|
|
||
|
/* rely on things like address sanitizer, valgrind for detecting double frees and leaks */
|
||
|
int32_t n;
|
||
|
void *ptrs[3] = { NULL };
|
||
|
void *ptr0copy, *ptr1copy;
|
||
|
dds_sample_info_t si[3];
|
||
|
|
||
|
/* read 1, return: this should cause memory to be allocated for 1 sample only */
|
||
|
n = dds_read (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] != NULL && ptrs[1] == NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
/* return resets buf[0] (so that it picks up the loan the next time) and zeros the data */
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL);
|
||
|
CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, sizeof (s)) == 0);
|
||
|
|
||
|
/* read 3, return: should work fine, causes realloc */
|
||
|
n = dds_read (reader, ptrs, si, 3, 3);
|
||
|
CU_ASSERT_FATAL (n == 3);
|
||
|
CU_ASSERT_FATAL (ptrs[0] != NULL && ptrs[1] != NULL && ptrs[2] != NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
ptr1copy = ptrs[1];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL);
|
||
|
CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, 3 * sizeof (s)) == 0);
|
||
|
|
||
|
/* read 1 using loan, expecting to get the same address (no realloc needed), defer return.
|
||
|
Expect ptrs[1] to remain unchanged, although that probably is really an implementation
|
||
|
detail rather than something one might want to rely on */
|
||
|
n = dds_read (read_condition, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == ptr1copy);
|
||
|
|
||
|
/* read 3, letting read allocate */
|
||
|
int32_t n2;
|
||
|
void *ptrs2[3] = { NULL };
|
||
|
n2 = dds_read (read_condition, ptrs2, si, 3, 3);
|
||
|
CU_ASSERT_FATAL (n2 == 3);
|
||
|
CU_ASSERT_FATAL (ptrs2[0] != NULL && ptrs2[1] != NULL && ptrs2[2] != NULL);
|
||
|
CU_ASSERT_FATAL (ptrs2[0] != ptrs[0]);
|
||
|
|
||
|
/* contents of first sample should be the same; the point of comparing them
|
||
|
is that valgrind/address sanitizer will get angry with us if one of them
|
||
|
has been freed; can't use memcmp because the sequence buffers should be
|
||
|
at different addresses */
|
||
|
{
|
||
|
const struct RoundTripModule_DataType *a = ptrs[0];
|
||
|
const struct RoundTripModule_DataType *b = ptrs2[0];
|
||
|
CU_ASSERT_FATAL (a->payload._length == b->payload._length);
|
||
|
CU_ASSERT_FATAL (a->payload._buffer != b->payload._buffer);
|
||
|
CU_ASSERT_FATAL (a->payload._buffer[0] == b->payload._buffer[0]);
|
||
|
}
|
||
|
|
||
|
/* return loan -- to be freed when we delete the reader */
|
||
|
result = dds_return_loan (read_condition, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL);
|
||
|
|
||
|
/* use "dds_return_loan" to free the second result immediately, there's no
|
||
|
easy way to check this happens short of using a custom sertopic */
|
||
|
ptr0copy = ptrs2[0];
|
||
|
result = dds_return_loan (read_condition, ptrs2, n2);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
CU_ASSERT_FATAL (ptrs2[0] == NULL);
|
||
|
|
||
|
//This should be a use-after-free
|
||
|
//CU_ASSERT_FATAL (memcmp (ptr0copy, zeros, sizeof (s)) == 0);
|
||
|
}
|
||
|
|
||
|
CU_Test (ddsc_loan, take_cleanup, .init = create_entities, .fini = delete_entities)
|
||
|
{
|
||
|
const RoundTripModule_DataType s = {
|
||
|
.payload = {
|
||
|
._length = 1,
|
||
|
._buffer = (uint8_t[]) { 'a' }
|
||
|
}
|
||
|
};
|
||
|
dds_return_t result;
|
||
|
|
||
|
/* rely on things like address sanitizer, valgrind for detecting double frees and leaks */
|
||
|
int32_t n;
|
||
|
void *ptrs[3] = { NULL };
|
||
|
void *ptr0copy;
|
||
|
dds_sample_info_t si[3];
|
||
|
|
||
|
/* take 1 from an empty reader: this should cause memory to be allocated for
|
||
|
1 sample only, be stored as the loan, but not become visisble to the
|
||
|
application */
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 0);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL && ptrs[1] == NULL);
|
||
|
|
||
|
/* take 1 that's present: allocates a loan */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] != NULL && ptrs[1] == NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
|
||
|
/* if it really got handled as a loan, the same address must come out again
|
||
|
(rely on address sanitizer allocating at a different address each time) */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
|
||
|
/* take that fails (for lack of data in this case) must reuse the loan, but
|
||
|
hand it back and restore the null pointer */
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 0);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL && ptrs[1] == NULL);
|
||
|
|
||
|
/* take that succeeds again must therefore still be using the same address */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
|
||
|
/* take that fails (with the loan still out) must allocate new memory and
|
||
|
free it */
|
||
|
int32_t n2;
|
||
|
void *ptrs2[3] = { NULL };
|
||
|
n2 = dds_take (reader, ptrs2, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n2 == 0);
|
||
|
CU_ASSERT_FATAL (ptrs2[0] == NULL && ptrs2[1] == NULL);
|
||
|
|
||
|
/* return the loan and the next take should reuse the memory */
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
}
|
||
|
|
||
|
CU_Test (ddsc_loan, read_cleanup, .init = create_entities, .fini = delete_entities)
|
||
|
{
|
||
|
const RoundTripModule_DataType s = {
|
||
|
.payload = {
|
||
|
._length = 1,
|
||
|
._buffer = (uint8_t[]) { 'a' }
|
||
|
}
|
||
|
};
|
||
|
dds_return_t result;
|
||
|
|
||
|
/* rely on things like address sanitizer, valgrind for detecting double frees and leaks */
|
||
|
int32_t n;
|
||
|
void *ptrs[3] = { NULL };
|
||
|
void *ptr0copy;
|
||
|
dds_sample_info_t si[3];
|
||
|
|
||
|
/* read 1 from an empty reader: this should cause memory to be allocated for
|
||
|
1 sample only, be stored as the loan, but not become visisble to the
|
||
|
application */
|
||
|
n = dds_read (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 0);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL && ptrs[1] == NULL);
|
||
|
|
||
|
/* read 1 that's present: allocates a loan */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_take (reader, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] != NULL && ptrs[1] == NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
|
||
|
/* if it really got handled as a loan, the same address must come out again
|
||
|
(rely on address sanitizer allocating at a different address each time) */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_read (read_condition_unread, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
ptr0copy = ptrs[0];
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
|
||
|
/* take that fails (for lack of data in this case) must reuse the loan, but
|
||
|
hand it back and restore the null pointer */
|
||
|
n = dds_read (read_condition_unread, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 0);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == NULL && ptrs[1] == NULL);
|
||
|
|
||
|
/* take that succeeds again must therefore still be using the same address */
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_read (read_condition_unread, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
|
||
|
/* take that fails (with the loan still out) must allocate new memory and
|
||
|
free it */
|
||
|
int32_t n2;
|
||
|
void *ptrs2[3] = { NULL };
|
||
|
n2 = dds_read (read_condition_unread, ptrs2, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n2 == 0);
|
||
|
CU_ASSERT_FATAL (ptrs2[0] == NULL && ptrs2[1] == NULL);
|
||
|
|
||
|
/* return the loan and the next take should reuse the memory */
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
result = dds_write (writer, &s);
|
||
|
CU_ASSERT_FATAL (result == 0);
|
||
|
n = dds_read (read_condition_unread, ptrs, si, 1, 1);
|
||
|
CU_ASSERT_FATAL (n == 1);
|
||
|
CU_ASSERT_FATAL (ptrs[0] == ptr0copy && ptrs[1] == NULL);
|
||
|
result = dds_return_loan (reader, ptrs, n);
|
||
|
CU_ASSERT_FATAL (result == DDS_RETCODE_OK);
|
||
|
}
|