/* 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 "custom_memory_manager.h" #include #include #include #include "ua_server_internal.h" #include "ua_types_encoding_binary.h" static UA_Boolean tortureEncoding(const uint8_t *data, size_t size, size_t *newOffset) { *newOffset = 0; if (size <= 2) return UA_FALSE; // get some random type uint16_t typeIndex = (uint16_t)(data[0] | data[1] << 8); data += 2; size -= 2; if (typeIndex >= UA_TYPES_COUNT) return UA_FALSE; void *dst = UA_new(&UA_TYPES[typeIndex]); if (!dst) return UA_FALSE; const UA_ByteString binary = { size, //length (UA_Byte *) (void *) data }; UA_StatusCode ret = UA_decodeBinary(&binary, newOffset, dst, &UA_TYPES[typeIndex], NULL); if (ret == UA_STATUSCODE_GOOD) { // copy the datatype to test void *dstCopy = UA_new(&UA_TYPES[typeIndex]); if (!dstCopy) return UA_FALSE; UA_copy(dst, dstCopy, &UA_TYPES[typeIndex]); UA_delete(dstCopy, &UA_TYPES[typeIndex]); // now also test encoding UA_ByteString encoded; UA_ByteString_allocBuffer(&encoded, *newOffset); const UA_Byte *end = &encoded.data[*newOffset]; UA_Byte *pos = encoded.data; ret = UA_encodeBinary(dst, &UA_TYPES[typeIndex], &pos, &end, NULL, NULL); if (ret == UA_STATUSCODE_GOOD) { // do nothing } UA_ByteString_deleteMembers(&encoded); } UA_delete(dst, &UA_TYPES[typeIndex]); return UA_TRUE; } static UA_Boolean tortureExtensionObject(const uint8_t *data, size_t size, size_t *newOffset) { *newOffset = 0; // check if there is still enough data to create an extension object // we need at least a nodeid.numeric which equals to 4 bytes if (size < 4) return UA_FALSE; UA_UInt32 identifier = (UA_UInt32)(data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24); size-= 4; UA_NodeId objectId = UA_NODEID_NUMERIC(0, identifier); UA_ExtensionObject obj; UA_ExtensionObject_init(&obj); obj.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING; obj.content.encoded.typeId = objectId; obj.content.encoded.body.length = size; obj.content.encoded.body.data = (UA_Byte*)(void*)data; // discard const. We are sure that we don't change it const UA_DataType *type = UA_findDataTypeByBinary(&obj.content.encoded.typeId); UA_StatusCode ret = UA_STATUSCODE_GOOD; if (type) { void *dstCopy = UA_new(type); if (!dstCopy) return UA_FALSE; ret = UA_decodeBinary(&obj.content.encoded.body, newOffset, dstCopy, type, NULL); if (ret == UA_STATUSCODE_GOOD) { UA_Variant var; UA_Variant_init(&var); UA_Variant_setScalar(&var, dstCopy, type); } UA_delete(dstCopy, type); } return ret==UA_STATUSCODE_GOOD; } /* ** Main entry point. The fuzzer invokes this function with each ** fuzzed input. */ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (!UA_memoryManager_setLimitFromLast4Bytes(data, size)) return 0; size -= 4; size_t offset; if (!tortureEncoding(data, size, &offset)) { return 0; } if (offset >= size) return 0; tortureExtensionObject(&data[offset], size-offset, &offset); return 0; }