| /* 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 <open62541/plugin/log_stdout.h> |
| #include <open62541/server_config_default.h> |
| #include <open62541/types.h> |
| |
| #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; |
| } |