mirror of
https://github.com/open62541/open62541.git
synced 2026-10-29 05:11:36 +08:00
fix(types): clean up datatype copies and descriptions on allocation failures
This commit is contained in:
committed by
Julius Pfrommer
parent
d5741a467a
commit
3377e56591
@@ -133,6 +133,9 @@ UA_DataType_copy(const UA_DataType *t1, UA_DataType *t2) {
|
||||
t2->xmlEncodingId = UA_NODEID_NULL;
|
||||
t2->members = NULL;
|
||||
t2->membersSize = 0;
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
t2->typeName = NULL;
|
||||
#endif
|
||||
|
||||
/* Copy the typename */
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
@@ -173,6 +176,7 @@ UA_DataType_copy(const UA_DataType *t1, UA_DataType *t2) {
|
||||
if(m1->memberType == t1)
|
||||
m2->memberType = t2;
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
m2->memberName = NULL;
|
||||
nameLen = strlen(m1->memberName) + 1;
|
||||
char *mName = (char*)UA_malloc(nameLen);
|
||||
if(!mName) {
|
||||
|
||||
@@ -168,8 +168,11 @@ UA_DataType_fromStructureDescription(UA_DataType *type,
|
||||
type->typeKind = UA_DATATYPEKIND_UNION; break;
|
||||
case UA_STRUCTURETYPE_STRUCTUREWITHSUBTYPEDVALUES:
|
||||
case UA_STRUCTURETYPE_UNIONWITHSUBTYPEDVALUES:
|
||||
UA_DataType_clear(type);
|
||||
return UA_STATUSCODE_BADNOTIMPLEMENTED;
|
||||
default: return UA_STATUSCODE_BADINTERNALERROR;
|
||||
default:
|
||||
UA_DataType_clear(type);
|
||||
return UA_STATUSCODE_BADINTERNALERROR;
|
||||
}
|
||||
|
||||
/* Copy NodeIds */
|
||||
@@ -384,9 +387,18 @@ UA_DataType_toStructureDescription(const UA_DataType *type,
|
||||
UA_StructureField *sf = &sd->fields[i];
|
||||
const UA_DataTypeMember *dtm = &type->members[i];
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
sf->name = UA_String_fromChars(dtm->memberName);
|
||||
UA_String name = UA_STRING((char*)(uintptr_t)dtm->memberName);
|
||||
res = UA_String_copy(&name, &sf->name);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
UA_StructureDescription_clear(descr);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
res |= UA_NodeId_copy(&dtm->memberType->typeId, &sf->dataType);
|
||||
res = UA_NodeId_copy(&dtm->memberType->typeId, &sf->dataType);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
UA_StructureDescription_clear(descr);
|
||||
return res;
|
||||
}
|
||||
sf->valueRank = (dtm->isArray) ? UA_VALUERANK_ONE_DIMENSION : UA_VALUERANK_SCALAR;
|
||||
sf->isOptional = dtm->isOptional;
|
||||
if(dtm->isArray && (type->typeKind == UA_DATATYPEKIND_STRUCTURE ||
|
||||
@@ -486,8 +498,15 @@ UA_DataType_toEnumDescription(const UA_DataType *type,
|
||||
UA_EnumField *ef = &descr->enumDefinition.fields[i];
|
||||
ef->value = (UA_Int64)(uintptr_t)dtm->memberType;
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
ef->name = UA_STRING_ALLOC(dtm->memberName);
|
||||
ef->displayName = UA_LOCALIZEDTEXT_ALLOC("", dtm->memberName);
|
||||
UA_String name = UA_STRING((char*)(uintptr_t)dtm->memberName);
|
||||
ef->displayName.locale = UA_STRING_ALLOC("");
|
||||
res = UA_String_copy(&name, &ef->name);
|
||||
if(res == UA_STATUSCODE_GOOD)
|
||||
res = UA_String_copy(&name, &ef->displayName.text);
|
||||
if(res != UA_STATUSCODE_GOOD) {
|
||||
UA_EnumDescription_clear(descr);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ ua_add_test(check_utils_trustlist_path.c)
|
||||
ua_add_test(check_types_copy_complex.c)
|
||||
ua_add_test(check_types_print.c)
|
||||
ua_add_test(check_types_definition.c)
|
||||
ua_add_test(check_types_allocation_failures.c)
|
||||
ua_add_test(check_encoding_roundtrip.c)
|
||||
ua_add_test(check_types_builtin_binary.c)
|
||||
ua_add_test(check_utils_url_kvm.c)
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <open62541/types.h>
|
||||
#include <open62541/util.h>
|
||||
#include <check.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(UA_ENABLE_MALLOC_SINGLETON) && defined(UA_TYPES_STRUCTUREDESCRIPTION)
|
||||
static size_t allocationIndex, failingAllocation, live;
|
||||
static void *owned[1024];
|
||||
|
||||
static void *
|
||||
remember(void *ptr) {
|
||||
if(ptr) {
|
||||
for(size_t i = 0; i < 1024; i++) {
|
||||
if(!owned[i]) { owned[i] = ptr; live++; return ptr; }
|
||||
}
|
||||
ck_abort_msg("allocation tracker exhausted");
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
static void *testMalloc(size_t size) {
|
||||
return allocationIndex++ == failingAllocation ? NULL : remember(malloc(size));
|
||||
}
|
||||
static void *testCalloc(size_t count, size_t size) {
|
||||
return allocationIndex++ == failingAllocation ? NULL : remember(calloc(count, size));
|
||||
}
|
||||
static void testFree(void *ptr) {
|
||||
if(!ptr) return;
|
||||
for(size_t i = 0; i < 1024; i++) {
|
||||
if(owned[i] == ptr) { owned[i] = NULL; live--; free(ptr); return; }
|
||||
}
|
||||
ck_abort_msg("attempt to free borrowed datatype storage");
|
||||
}
|
||||
static void *testRealloc(void *ptr, size_t size) {
|
||||
if(allocationIndex++ == failingAllocation) return NULL;
|
||||
if(!ptr) return remember(realloc(NULL, size));
|
||||
for(size_t i = 0; i < 1024; i++) {
|
||||
if(owned[i] == ptr) {
|
||||
void *next = realloc(ptr, size);
|
||||
if(next || size == 0) { owned[i] = next; if(!next) live--; }
|
||||
return next;
|
||||
}
|
||||
}
|
||||
ck_abort_msg("attempt to resize borrowed datatype storage");
|
||||
return NULL;
|
||||
}
|
||||
static void setup(void) {
|
||||
allocationIndex = 0; failingAllocation = (size_t)-1; live = 0;
|
||||
memset(owned, 0, sizeof(owned));
|
||||
UA_mallocSingleton = testMalloc; UA_callocSingleton = testCalloc;
|
||||
UA_reallocSingleton = testRealloc; UA_freeSingleton = testFree;
|
||||
}
|
||||
static void teardown(void) {
|
||||
UA_mallocSingleton = malloc; UA_callocSingleton = calloc;
|
||||
UA_reallocSingleton = realloc; UA_freeSingleton = free;
|
||||
for(size_t i = 0; i < 1024; i++) free(owned[i]);
|
||||
}
|
||||
|
||||
static UA_StatusCode
|
||||
operation(int mode) {
|
||||
typedef struct { UA_UInt32 number; UA_String text; } Record;
|
||||
UA_DataTypeMember members[] = {
|
||||
{UA_TYPENAME("Number") &UA_TYPES[UA_TYPES_UINT32], 0, false, false},
|
||||
{UA_TYPENAME("Text") &UA_TYPES[UA_TYPES_STRING],
|
||||
offsetof(Record, text) - sizeof(UA_UInt32), false, false}};
|
||||
UA_DataType type;
|
||||
memset(&type, 0, sizeof(type));
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
type.typeName = "AllocationProbe";
|
||||
#endif
|
||||
type.typeId = UA_NODEID_STRING(1, "ProbeType");
|
||||
type.binaryEncodingId = UA_NODEID_STRING(1, "Probe.Binary");
|
||||
type.xmlEncodingId = UA_NODEID_STRING(1, "Probe.XML");
|
||||
type.typeKind = UA_DATATYPEKIND_STRUCTURE; type.memSize = sizeof(Record);
|
||||
type.membersSize = 2; type.members = members;
|
||||
if(mode == 0) {
|
||||
UA_DataType copy;
|
||||
UA_StatusCode res = UA_DataType_copy(&type, ©);
|
||||
if(res == UA_STATUSCODE_GOOD) {
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
ck_assert_str_eq(copy.typeName, "AllocationProbe");
|
||||
ck_assert_str_eq(copy.members[1].memberName, "Text");
|
||||
#endif
|
||||
ck_assert(UA_NodeId_equal(&type.typeId, ©.typeId));
|
||||
ck_assert_ptr_ne(type.typeId.identifier.string.data, copy.typeId.identifier.string.data);
|
||||
}
|
||||
UA_DataType_clear(©);
|
||||
return res;
|
||||
}
|
||||
if(mode == 3) {
|
||||
UA_StructureDescription description;
|
||||
UA_StructureDescription_init(&description);
|
||||
description.dataTypeId = type.typeId;
|
||||
description.name = UA_QUALIFIEDNAME(0, "Unsupported");
|
||||
description.structureDefinition.structureType = UA_STRUCTURETYPE_STRUCTUREWITHSUBTYPEDVALUES;
|
||||
UA_ExtensionObject object;
|
||||
UA_ExtensionObject_setValueNoDelete(&object, &description, &UA_TYPES[UA_TYPES_STRUCTUREDESCRIPTION]);
|
||||
UA_DataType output;
|
||||
UA_StatusCode res = UA_DataType_fromDescription(&output, &object, NULL);
|
||||
ck_assert_uint_ne(res, UA_STATUSCODE_GOOD);
|
||||
ck_assert(UA_NodeId_isNull(&output.typeId));
|
||||
UA_DataType_clear(&output);
|
||||
return res;
|
||||
}
|
||||
if(mode == 2) {
|
||||
type.typeKind = UA_DATATYPEKIND_ENUM; type.memSize = sizeof(UA_Int32);
|
||||
members[0].memberType = (const UA_DataType*)(intptr_t)-7;
|
||||
members[1].memberType = (const UA_DataType*)(intptr_t)42;
|
||||
}
|
||||
UA_ExtensionObject description;
|
||||
UA_StatusCode res = UA_DataType_toDescription(&type, &description);
|
||||
if(res == UA_STATUSCODE_GOOD) {
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
UA_String expected = UA_STRING("Text");
|
||||
if(mode == 2) {
|
||||
UA_EnumDescription *d = (UA_EnumDescription*)description.content.decoded.data;
|
||||
ck_assert(UA_String_equal(&d->enumDefinition.fields[1].name, &expected));
|
||||
} else {
|
||||
UA_StructureDescription *d = (UA_StructureDescription*)description.content.decoded.data;
|
||||
ck_assert(UA_String_equal(&d->structureDefinition.fields[1].name, &expected));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
UA_ExtensionObject_clear(&description);
|
||||
return res;
|
||||
}
|
||||
|
||||
START_TEST(everyAllocationCanFail) {
|
||||
UA_StatusCode result = operation(_i);
|
||||
ck_assert_uint_eq(result, _i == 3 ? UA_STATUSCODE_BADNOTIMPLEMENTED : UA_STATUSCODE_GOOD);
|
||||
size_t count = allocationIndex;
|
||||
ck_assert_uint_gt(count, 0);
|
||||
ck_assert_uint_eq(live, 0);
|
||||
for(size_t failure = 0; failure < count; failure++) {
|
||||
allocationIndex = 0; failingAllocation = failure;
|
||||
result = operation(_i);
|
||||
ck_assert_uint_eq(result, UA_STATUSCODE_BADOUTOFMEMORY);
|
||||
ck_assert_uint_eq(live, 0);
|
||||
}
|
||||
} END_TEST
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
Suite *s = suite_create("Datatype allocation failures");
|
||||
TCase *tc = tcase_create("Ownership and description conversion");
|
||||
#if defined(UA_ENABLE_MALLOC_SINGLETON) && defined(UA_TYPES_STRUCTUREDESCRIPTION)
|
||||
tcase_add_checked_fixture(tc, setup, teardown);
|
||||
tcase_add_loop_test(tc, everyAllocationCanFail, 0, 4);
|
||||
#endif
|
||||
suite_add_tcase(s, tc);
|
||||
SRunner *runner = srunner_create(s);
|
||||
srunner_set_fork_status(runner, CK_NOFORK);
|
||||
srunner_run_all(runner, CK_NORMAL);
|
||||
int failed = srunner_ntests_failed(runner);
|
||||
srunner_free(runner);
|
||||
return failed ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
@@ -111,8 +111,45 @@ START_TEST(definition_emptyStructureHasStorage) {
|
||||
UA_DataType_clear(&type);
|
||||
} END_TEST
|
||||
|
||||
START_TEST(definition_copyOwnedNodeIds) {
|
||||
UA_DataType original = UA_TYPES[UA_TYPES_ARGUMENT];
|
||||
original.typeId = UA_NODEID_STRING(2, "Argument");
|
||||
original.binaryEncodingId = UA_NODEID_STRING(3, "Argument.Binary");
|
||||
original.xmlEncodingId = UA_NODEID_STRING(4, "Argument.XML");
|
||||
UA_DataType copied;
|
||||
ck_assert_uint_eq(UA_DataType_copy(&original, &copied), UA_STATUSCODE_GOOD);
|
||||
ck_assert(UA_NodeId_equal(&original.typeId, &copied.typeId));
|
||||
ck_assert_ptr_ne(original.typeId.identifier.string.data,
|
||||
copied.typeId.identifier.string.data);
|
||||
ck_assert(UA_NodeId_equal(&original.binaryEncodingId, &copied.binaryEncodingId));
|
||||
ck_assert(UA_NodeId_equal(&original.xmlEncodingId, &copied.xmlEncodingId));
|
||||
UA_DataType_clear(&copied);
|
||||
/* Clearing the copy must leave the borrowed original strings intact. */
|
||||
ck_assert_uint_eq(original.xmlEncodingId.identifier.string.data[0], 'A');
|
||||
} END_TEST
|
||||
|
||||
START_TEST(definition_invalidStructureKindClearsOutput) {
|
||||
UA_StructureDescription description;
|
||||
UA_StructureDescription_init(&description);
|
||||
description.dataTypeId = UA_NODEID_STRING(1, "Unsupported");
|
||||
description.name = UA_QUALIFIEDNAME(1, "Unsupported");
|
||||
description.structureDefinition.structureType = (_i == 0) ?
|
||||
UA_STRUCTURETYPE_STRUCTUREWITHSUBTYPEDVALUES : (UA_StructureType)99;
|
||||
UA_ExtensionObject object;
|
||||
UA_ExtensionObject_setValueNoDelete(&object, &description, &UA_TYPES[UA_TYPES_STRUCTUREDESCRIPTION]);
|
||||
UA_DataType output;
|
||||
ck_assert_uint_ne(UA_DataType_fromDescription(&output, &object, NULL), UA_STATUSCODE_GOOD);
|
||||
ck_assert(UA_NodeId_isNull(&output.typeId));
|
||||
#ifdef UA_ENABLE_TYPEDESCRIPTION
|
||||
ck_assert_ptr_eq(output.typeName, NULL);
|
||||
#endif
|
||||
UA_DataType_clear(&output);
|
||||
} END_TEST
|
||||
|
||||
static Suite *testSuite_definition(void) {
|
||||
TCase *tc = tcase_create("DataTypeDefinition");
|
||||
tcase_add_loop_test(tc, definition_invalidStructureKindClearsOutput, 0, 2);
|
||||
tcase_add_test(tc, definition_copyOwnedNodeIds);
|
||||
tcase_add_test(tc, definition_roundtrip_structure);
|
||||
tcase_add_test(tc, definition_roundtrip_structure_nested);
|
||||
tcase_add_test(tc, definition_roundtrip_simpleType);
|
||||
|
||||
Reference in New Issue
Block a user