diff --git a/base/test/fuzztest/BUILD.gn b/base/test/fuzztest/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..61243ac948c902c7337294755121eddbd931b952 --- /dev/null +++ b/base/test/fuzztest/BUILD.gn @@ -0,0 +1,26 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/test.gni") + +##############################group########################################## +group("fuzztest") { + testonly = true + deps = [] + deps += [ + # deps file + "parcel_fuzzer:ParcelFuzzTest", + "refbase_fuzzer:RefbaseFuzzTest", + "timer_fuzzer:TimerFuzzTest", + ] +} +############################################################################### diff --git a/base/test/fuzztest/README_zh.md b/base/test/fuzztest/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..0ac3bbdac226aa8e7bfab67656a56dcc0be28955 --- /dev/null +++ b/base/test/fuzztest/README_zh.md @@ -0,0 +1,111 @@ +# c_utils基础库fuzzy测试 + + +## 简介 + +使用Fuzzing测试框架,将自动生成的随机数据输入到测试程序中,监视程序异常崩溃,以发现可能的程序错误。 + +## fuzz用例设计说明 +### Parcel模块用例设计说明 +Parcel模块的接口分为3组: +- 普通接口——内存地址对齐的接口; +- 非对齐接口——内存地址不对齐的接口; +- 其他接口——设置parcel数据大小相关的接口。 + +各组之间的接口互斥,不能在一次测试中同时调用。测试设置300秒时限,在时限内会反复调用用例入口函数,每轮测试首先随机选择一组,然后在组内循环随机调用接口若干次,以确保接口的调用顺序和调用次数的随机性。 + +### RefBase模块用例设计说明 +测试设置300秒时限,在时限内会反复调用用例入口函数,每轮测试创建多个线程,各个线程随机调用RefBase的接口,确保多线程下使用的稳定性。 +#### 测试流程 +```mermaid +flowchart TB + A[创建RefBase对象]-->B[随机创建n个线程] + B-->D{判断分组} + subgraph 线程1 + subgraph loop:随机调用RefBase的接口 + D-->|引用计数减1的接口|E[更新本线程引用计数] + E-->F{是否计数归零} + F-->|是|G[加写锁] + G-->调用引用计数减1的接口-->R[解写锁] + F-->|否|H[直接调用引用计数减1的接口] + D-->|Read类型或引用计数加1的接口|J{是否计数归零} + J-->|是|K[加读锁] + K-->L{RefBase自身是否已释放} + L-->|是|M[释放存在的WeakRefCounter对象] + M-->N[解读锁] + L-->|否|O[调用Read类型或引用计数加1的接口] + O-->Q[更新本线程引用计数] + J-->|否|P[调用Read类型或引用计数加1的接口] + P-->S[更新本线程引用计数] + end + R-->T[根据剩余的引用计数次数,调用计数减1的接口,将计数清零] + H-->T + Q-->T + S-->T + N-->return + end + B-->U[循环随机调用RefBase的接口] + subgraph 线程2 + U + end + B-->V[循环随机调用RefBase的接口] + subgraph 线程n + V + end + Y[若未释放RefBase,释放RefBase] + T-->Y + U-->Y + V-->Y +``` +#### 测试说明 +结构体SingleThreadCount记录单个线程内部的强弱引用计数情况: + +| 变量 | 描述 | +| -------------------------------------- | ------------------------------------------------------------ | +| **size_t** strongCount = 0; | 强引用计数,DecStrongRef、IncStrongRef、AttemptIncStrongRef等会影响该计数。 | +| **size_t** weakCount = 0; | 弱引用计数,DecWeakRef、IncWeakRef会影响该计数。 | +| **bool** weakRefCounterExists = false; | 是否创建了WeakRefCounter对象。 | +| **size_t** weakRefCount = 0; | WeakRefCounter造成的弱引用计数加减,DecWeakRefCount、IncWeakRefCount会影响该计数。 | + +由于RefBase及其成员RefCounter会在所有线程引用计数归零时自动释放,所以各个线程在不知道其它线程引用计数的情况下,需要在自身计数即将归零时加锁,确保RefBase和其成员RefCounter释放的过程中其它线程不会进行有关操作。 + +此外,attempIncStrongRef、AttemptIncStrong、AttemptAcquire、IncStrongRef之间需要加另一套锁,避免多个线程同时调用的情况下出现强引用计数异常。 + +### Timer模块用例设计说明 +测试设置300秒时限,在时限内会反复调用用例入口函数,每轮测试内部循环多次,每次循环首先设置计时器,然后随机组合调用Register和Unregister函数,每次调用Register时随机设置回调响应时间并随机sleep一段时间,使回调响应随机执行,最后关闭计时器。 + +## 编译构建 +### 编译fuzz测试程序 +``` +./build.sh --product-name rk3568 --build-target commonlibrary/c_utils/base/test/fuzztest +``` + +## 目录 + +``` +fuzztest/ +├── parcel_fuzzer/ + ├── corpus # Fuzz语料目录 + │ ├── init # Fuzz语料 + ├── BUILD.gn # Parcel模块的Fuzz用例编译配置 + ├── parcel_fuzzer.cpp # Parcel模块的Fuzz用例源文件 + ├── parcel_fuzzer.h # Parcel模块的Fuzz用例头文件 + ├── project.xml # Parcel模块的Fuzz选项配置文件 +├── refbase_fuzzer/ + ├── corpus # Fuzz语料目录 + │ ├── init # Fuzz语料 + ├── BUILD.gn # RefBase模块的Fuzz用例编译配置 + ├── refbase_fuzzer.cpp # RefBase模块的Fuzz用例源文件 + ├── refbase_fuzzer.h # RefBase模块的Fuzz用例头文件 + ├── project.xml # RefBase模块的Fuzz选项配置文件 +├── timer_fuzzer/ + ├── corpus # Fuzz语料目录 + │ ├── init # Fuzz语料 + ├── BUILD.gn # Timer模块的Fuzz用例编译配置 + ├── timer_fuzzer.cpp # Timer模块的Fuzz用例源文件 + ├── timer_fuzzer.h # Timer模块的Fuzz用例头文件 + ├── project.xml # Timer模块的Fuzz选项配置文件 +├── BUILD.gn # c_utils库Fuzz整体编译配置 +├── fuzz_log.h # Fuzz用例调试log头文件 +``` + diff --git a/base/test/fuzztest/fuzz_log.h b/base/test/fuzztest/fuzz_log.h new file mode 100644 index 0000000000000000000000000000000000000000..407796c27584eef7ce941242d70afe4a0908cd2e --- /dev/null +++ b/base/test/fuzztest/fuzz_log.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FUZZ_LOG_H +#define FUZZ_LOG_H + +#ifdef DEBUG_FUZZ +#include "hilog_base/log_base.h" +constexpr LogType FUZZ_LOG_TYPE = LOG_CORE; +constexpr unsigned int FUZZ_LOG_DOMAIN = 0xD003D00; +constexpr const char *FUZZ_LOG_TAG = "fuzz_test"; +#define FUZZ_LOGF(...) (void)HiLogBasePrint(FUZZ_LOG_TYPE, LOG_FATAL, FUZZ_LOG_DOMAIN, FUZZ_LOG_TAG, __VA_ARGS__) +#define FUZZ_LOGE(...) (void)HiLogBasePrint(FUZZ_LOG_TYPE, LOG_ERROR, FUZZ_LOG_DOMAIN, FUZZ_LOG_TAG, __VA_ARGS__) +#define FUZZ_LOGW(...) (void)HiLogBasePrint(FUZZ_LOG_TYPE, LOG_WARN, FUZZ_LOG_DOMAIN, FUZZ_LOG_TAG, __VA_ARGS__) +#define FUZZ_LOGI(...) (void)HiLogBasePrint(FUZZ_LOG_TYPE, LOG_INFO, FUZZ_LOG_DOMAIN, FUZZ_LOG_TAG, __VA_ARGS__) +#define FUZZ_LOGD(...) (void)HiLogBasePrint(FUZZ_LOG_TYPE, LOG_DEBUG, FUZZ_LOG_DOMAIN, FUZZ_LOG_TAG, __VA_ARGS__) +#else +#define PARCEL_LOGF(...) +#define PARCEL_LOGE(...) +#define PARCEL_LOGW(...) +#define PARCEL_LOGI(...) +#define PARCEL_LOGD(...) +#endif // (defined DEBUG_FUZZ) + +#endif // FUZZ_LOG_H \ No newline at end of file diff --git a/base/test/fuzztest/parcel_fuzzer/BUILD.gn b/base/test/fuzztest/parcel_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..4a51a15c788386b9b3f94e4fefb9f0987feb5a70 --- /dev/null +++ b/base/test/fuzztest/parcel_fuzzer/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("ParcelFuzzTest") { + module_out_path = "utils/base" + fuzz_config_file = "." + include_dirs = [ "../" ] + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + defines = [ "DEBUG_FUZZ" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog_base", + ] + sources = [ "parcel_fuzzer.cpp" ] +} +############################################################################### diff --git a/base/test/fuzztest/parcel_fuzzer/corpus/init b/base/test/fuzztest/parcel_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..e4ceac1bcd4e3b3427eb63cea0c28304064333cc --- /dev/null +++ b/base/test/fuzztest/parcel_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.cpp b/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a369edf47641abc78b46df78747d7f0b753a41b --- /dev/null +++ b/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.cpp @@ -0,0 +1,550 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "parcel_fuzzer.h" + +#include "fuzz_log.h" +#include "fuzzer/FuzzedDataProvider.h" +#include "parcel.h" +#include "securec.h" +#include "string_ex.h" + +using namespace std; + +namespace OHOS { +const uint8_t MAX_BUFFER_SIZE = 255; +const uint8_t MAX_STRING_LENGTH = 255; +const uint8_t MAX_VECTOR_SIZE = 10; +const int MAX_OPERATIONS_GENERAL = 500; +const int MAX_OPERATIONS_UNALIGNED = 100; +const int MAX_OPERATIONS_OTHER = 15; + +enum Group { + GENERAL_GROUP = 0, + UNALIGNED_GROUP = 1, + OTHER_GROUP = 2, +}; + +#define PARCEL_NO_INPUT_WITH_RETURN(T, FUN) \ + [](FuzzedDataProvider*, Parcel& p) { \ + FUZZ_LOGI("%{public}s", #FUN); \ + T t = p.FUN(); \ + (void)t; \ + } + +#define PARCEL_INT_INPUT_WITH_BOOL_RETURN(T, FUN) \ + [](FuzzedDataProvider* d, Parcel& p) { \ + FUZZ_LOGI("%{public}s", #FUN); \ + T data = d->ConsumeIntegral(); \ + bool t = p.FUN(data); \ + (void)t; \ + } + +#define PARCEL_REF_INPUT_WITH_BOOL_RETURN(T, FUN) \ + [](FuzzedDataProvider*, Parcel& p) { \ + FUZZ_LOGI("%{public}s", #FUN); \ + T out {}; \ + bool t = p.FUN(out); \ + (void)t; \ + } + +#define PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(T, FUN) \ + [](FuzzedDataProvider* d, Parcel& p) { \ + FUZZ_LOGI("%{public}s", #FUN); \ + size_t n = d->ConsumeIntegralInRange(1, MAX_VECTOR_SIZE); \ + void* buf = malloc(n * sizeof(T)); \ + size_t wb = d->ConsumeData(buf, n * sizeof(T)); \ + auto* tmp = static_cast(buf); \ + std::vector data(tmp, tmp + wb / sizeof(T)); \ + bool t = p.FUN(data); \ + (void)t; \ + free(buf); \ + } + +#define PARCEL_POINT_INPUT_WITH_BOOL_RETURN(T, FUN) \ + [](FuzzedDataProvider*, Parcel& p) { \ + FUZZ_LOGI("%{public}s", #FUN); \ + vector out; \ + bool t = p.FUN(&out); \ + (void)t; \ + } + +class TestParcelable : public virtual Parcelable { +public: + TestParcelable() = default; + explicit TestParcelable(bool asRemote) + { + asRemote_ = asRemote; + }; + ~TestParcelable() = default; + + bool Marshalling(Parcel& parcel) const override; + static TestParcelable* Unmarshalling(Parcel& parcel); + static bool Marshalling(Parcel& parcel, const sptr& object); + +public: + int32_t int32Write_ = -0x12345678; + int32_t int32Read_; +}; + +bool TestParcelable::Marshalling(Parcel& parcel) const +{ + bool result = parcel.WriteInt32(this->int32Write_); + return result; +} + +TestParcelable* TestParcelable::Unmarshalling(Parcel& parcel) +{ + auto* read = new TestParcelable(); + read->int32Read_ = parcel.ReadInt32(); + return read; +} + +bool TestParcelable::Marshalling(Parcel& parcel, const sptr& object) +{ + bool result = parcel.WriteInt32(object->int32Write_); + return result; +} + +class RemoteObject : public virtual Parcelable { +public: + RemoteObject() + { + asRemote_ = true; + }; + bool Marshalling(Parcel& parcel) const override; + static sptr Unmarshalling(Parcel& parcel); +}; + +bool RemoteObject::Marshalling(Parcel& parcel) const +{ + parcel_flat_binder_object flat; + flat.hdr.type = 0xff; + flat.flags = 0x7f; + flat.binder = 0; + flat.handle = (uint32_t)(-1); + flat.cookie = reinterpret_cast(this); + bool status = parcel.WriteBuffer(&flat, sizeof(parcel_flat_binder_object)); + if (!status) { + return false; + } + return true; +} + +sptr RemoteObject::Unmarshalling(Parcel& parcel) +{ + const uint8_t* buffer = parcel.ReadBuffer(sizeof(parcel_flat_binder_object)); + if (buffer == nullptr) { + return nullptr; + } + sptr obj = new RemoteObject(); + return obj; +} + +const std::vector> operations = { + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetDataSize), + PARCEL_NO_INPUT_WITH_RETURN(uintptr_t, GetData), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetOffsetsSize), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetWritableBytes), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetReadableBytes), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetDataCapacity), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetMaxCapacity), + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteBool"); + bool booltest = dataProvider->ConsumeBool(); + parcel.WriteBool(booltest); + }, + + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int8_t, WriteInt8), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int16_t, WriteInt16), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int32_t, WriteInt32), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int64_t, WriteInt64), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint8_t, WriteUint8), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint16_t, WriteUint16), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint32_t, WriteUint32), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint64_t, WriteUint64), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uintptr_t, WritePointer), + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteFloat"); + float floattest = dataProvider->ConsumeFloatingPoint(); + parcel.WriteFloat(floattest); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteDouble"); + double doubletest = dataProvider->ConsumeFloatingPoint(); + parcel.WriteDouble(doubletest); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteBuffer"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + void* buffer = malloc(bufferSize); + size_t writtenBytes = dataProvider->ConsumeData(buffer, bufferSize); + parcel.WriteBuffer(buffer, writtenBytes); + free(buffer); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteBufferAddTerminator"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + void* buffer = malloc(bufferSize); + size_t writtenBytes = dataProvider->ConsumeData(buffer, bufferSize); + if (writtenBytes == 0) { + free(buffer); + return; + } + size_t typeSize = dataProvider->ConsumeIntegralInRange(0, writtenBytes); + parcel.WriteBufferAddTerminator(buffer, writtenBytes, typeSize); + free(buffer); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteUnpadBuffer"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + void* buffer = malloc(bufferSize); + size_t writtenBytes = dataProvider->ConsumeData(buffer, bufferSize); + parcel.WriteUnpadBuffer(buffer, writtenBytes); + free(buffer); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteCString"); + string teststr = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + parcel.WriteCString(teststr.c_str()); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteString"); + string teststr = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + parcel.WriteString(teststr); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteString16"); + string utf8 = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + u16string utf16 = Str8ToStr16(utf8); + parcel.WriteString16(utf16); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteString16WithLength"); + string utf8 = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + u16string utf16 = Str8ToStr16(utf8); + char16_t* value = utf16.data(); + parcel.WriteString16WithLength(value, utf16.length()); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteString8WithLength"); + string utf8 = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + char* value = utf8.data(); + parcel.WriteString8WithLength(value, utf8.length()); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteParcelable"); + bool remoteFlag = dataProvider->ConsumeBool(); + TestParcelable parcelableWrite(remoteFlag); + parcel.WriteParcelable(&parcelableWrite); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteStrongParcelable"); + bool remoteFlag = dataProvider->ConsumeBool(); + sptr parcelableWrite = new TestParcelable(remoteFlag); + parcel.WriteStrongParcelable(parcelableWrite); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteRemoteObject"); + RemoteObject obj; + parcel.WriteRemoteObject(&obj); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteObject"); + bool remoteFlag = dataProvider->ConsumeBool(); + sptr parcelableWrite = new TestParcelable(remoteFlag); + parcel.WriteObject(parcelableWrite); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("ParseFrom"); + void* buffer = nullptr; + size_t testdataSize = parcel.GetDataSize(); + if (testdataSize > 0) { + buffer = malloc(testdataSize); + memcpy_s(buffer, testdataSize, reinterpret_cast(parcel.GetData()), testdataSize); + } + Parcel parcel2(nullptr); + parcel2.ParseFrom(reinterpret_cast(buffer), testdataSize); + }, + + PARCEL_NO_INPUT_WITH_RETURN(bool, ReadBool), + PARCEL_NO_INPUT_WITH_RETURN(int8_t, ReadInt8), + PARCEL_NO_INPUT_WITH_RETURN(int16_t, ReadInt16), + PARCEL_NO_INPUT_WITH_RETURN(int32_t, ReadInt32), + PARCEL_NO_INPUT_WITH_RETURN(int64_t, ReadInt64), + PARCEL_NO_INPUT_WITH_RETURN(uint8_t, ReadUint8), + PARCEL_NO_INPUT_WITH_RETURN(uint16_t, ReadUint16), + PARCEL_NO_INPUT_WITH_RETURN(uint32_t, ReadUint32), + PARCEL_NO_INPUT_WITH_RETURN(uint64_t, ReadUint64), + PARCEL_NO_INPUT_WITH_RETURN(float, ReadFloat), + PARCEL_NO_INPUT_WITH_RETURN(double, ReadDouble), + PARCEL_NO_INPUT_WITH_RETURN(uintptr_t, ReadPointer), + + PARCEL_REF_INPUT_WITH_BOOL_RETURN(bool, ReadBool), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int8_t, ReadInt8), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int16_t, ReadInt16), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int32_t, ReadInt32), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int64_t, ReadInt64), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint8_t, ReadUint8), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint16_t, ReadUint16), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint32_t, ReadUint32), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint64_t, ReadUint64), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(float, ReadFloat), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(double, ReadDouble), + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("ReadUnpadBuffer"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.ReadUnpadBuffer(bufferSize); + }, + + PARCEL_NO_INPUT_WITH_RETURN(const char*, ReadCString), + PARCEL_NO_INPUT_WITH_RETURN(string, ReadString), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(string, ReadString), + PARCEL_NO_INPUT_WITH_RETURN(u16string, ReadString16), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(u16string, ReadString16), + + [](FuzzedDataProvider*, Parcel& parcel) { + FUZZ_LOGI("ReadString16WithLength"); + int32_t strlen = 0; + parcel.ReadString16WithLength(strlen); + }, + + [](FuzzedDataProvider*, Parcel& parcel) { + FUZZ_LOGI("ReadString8WithLength"); + int32_t strlen = 0; + parcel.ReadString8WithLength(strlen); + }, + + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetReadPosition), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetWritePosition), + + PARCEL_NO_INPUT_WITH_RETURN(sptr, ReadParcelable), + PARCEL_NO_INPUT_WITH_RETURN(sptr, ReadStrongParcelable), + PARCEL_NO_INPUT_WITH_RETURN(bool, CheckOffsets), + PARCEL_NO_INPUT_WITH_RETURN(sptr, ReadObject), + + [](FuzzedDataProvider*, Parcel& parcel) { + FUZZ_LOGI("SetAllocator"); + parcel.SetAllocator(new DefaultAllocator()); + }, + + [](FuzzedDataProvider*, Parcel& parcel) { + FUZZ_LOGI("InjectOffsets"); + Parcel parcel2(nullptr); + parcel2.InjectOffsets(parcel.GetObjectOffsets(), parcel.GetOffsetsSize()); + }, + + [](FuzzedDataProvider*, Parcel& parcel) { + FUZZ_LOGI("FlushBuffer"); + parcel.FlushBuffer(); + }, + + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(int8_t, WriteInt8Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(int32_t, WriteInt32Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(int64_t, WriteInt64Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(uint8_t, WriteUInt8Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(uint16_t, WriteUInt16Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(uint32_t, WriteUInt32Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(uint64_t, WriteUInt64Vector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(float, WriteFloatVector), + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(double, WriteDoubleVector), + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteStringVector"); + size_t vectorSize = dataProvider->ConsumeIntegralInRange(1, MAX_VECTOR_SIZE); + std::vector testdata(vectorSize); + for (size_t i = 0; i < vectorSize; i++) { + size_t strlen = dataProvider->ConsumeIntegralInRange(1, MAX_STRING_LENGTH); + std::vector vec = dataProvider->ConsumeBytesWithTerminator(strlen); + testdata[i] = vec.data(); + } + parcel.WriteStringVector(testdata); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteString16Vector"); + size_t vectorSize = dataProvider->ConsumeIntegralInRange(1, MAX_VECTOR_SIZE); + std::vector testdata(vectorSize); + for (size_t i = 0; i < vectorSize; i++) { + size_t strlen = dataProvider->ConsumeIntegralInRange(1, MAX_STRING_LENGTH); + std::vector vec = dataProvider->ConsumeBytesWithTerminator(strlen); + string str = vec.data(); + testdata[i] = Str8ToStr16(str); + } + parcel.WriteString16Vector(testdata); + }, + + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(int32_t, ReadInt32Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(int64_t, ReadInt64Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(uint32_t, ReadUInt32Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(uint64_t, ReadUInt64Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(float, ReadFloatVector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(double, ReadDoubleVector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(string, ReadStringVector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(u16string, ReadString16Vector), +}; + +const std::vector> unaligned_operations = { + // error call ReadDouble after following methods, will crash with "signal SIGBUS: illegal alignment". + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("ReadBuffer"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.ReadBuffer(bufferSize); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("SkipBytes"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.SkipBytes(bufferSize); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("RewindRead"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.RewindRead(bufferSize); + }, + + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(bool, ReadBoolVector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(int8_t, ReadInt8Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(int16_t, ReadInt16Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(uint8_t, ReadUInt8Vector), + PARCEL_POINT_INPUT_WITH_BOOL_RETURN(uint16_t, ReadUInt16Vector), + + PARCEL_NO_INPUT_WITH_RETURN(bool, ReadBoolUnaligned), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int8_t, ReadInt8Unaligned), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(int16_t, ReadInt16Unaligned), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint8_t, ReadUint8Unaligned), + PARCEL_REF_INPUT_WITH_BOOL_RETURN(uint16_t, ReadUint16Unaligned), + + // error call WriteDouble/WriteFloat after RewindWrite, will crash with "signal SIGBUS: illegal alignment". + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("RewindWrite"); + size_t bufferSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.RewindWrite(bufferSize); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteBoolVector"); + size_t vectorSize = dataProvider->ConsumeIntegralInRange(1, MAX_VECTOR_SIZE); + std::vector data = dataProvider->ConsumeBytes(vectorSize); + if (data.size() > 0) { + std::vector testdata(data.size()); + for (size_t i = 0; i < testdata.size(); i++) { + testdata[i] = 1 & data[i]; + } + parcel.WriteBoolVector(testdata); + } + }, + + PARCEL_WRITE_VECTOR_WITH_BOOL_RETURN(int16_t, WriteInt16Vector), + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("WriteBoolUnaligned"); + bool booltest = dataProvider->ConsumeBool(); + parcel.WriteBoolUnaligned(booltest); + }, + + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int8_t, WriteInt8Unaligned), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(int16_t, WriteInt16Unaligned), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint8_t, WriteUint8Unaligned), + PARCEL_INT_INPUT_WITH_BOOL_RETURN(uint16_t, WriteUint16Unaligned), +}; + +const std::vector> other_operations = { + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetDataSize), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetDataCapacity), + PARCEL_NO_INPUT_WITH_RETURN(size_t, GetMaxCapacity), + + // cannot call randomly with other operations. + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("RewindWrite"); + size_t capacity = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.SetDataCapacity(capacity); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("RewindWrite"); + size_t dataSize = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.SetDataSize(dataSize); + }, + + [](FuzzedDataProvider* dataProvider, Parcel& parcel) { + FUZZ_LOGI("RewindWrite"); + size_t maxCapacity = dataProvider->ConsumeIntegralInRange(1, MAX_BUFFER_SIZE); + parcel.SetMaxCapacity(maxCapacity); + }, +}; + +void ParcelTestFunc(const uint8_t* data, size_t size, FuzzedDataProvider* dataProvider) +{ + FUZZ_LOGI("ParcelTestFunc start"); + uint8_t opSet = dataProvider->ConsumeIntegralInRange(GENERAL_GROUP, OTHER_GROUP); + uint8_t maxGeneral = operations.size() - 1; + uint8_t maxUnaligned = unaligned_operations.size() - 1; + uint8_t maxOther = other_operations.size() - 1; + Parcel parcel1(nullptr); + int opCnt = 0; + + switch (opSet) { + case GENERAL_GROUP: + while (dataProvider->remaining_bytes() > 0 && opCnt++ < MAX_OPERATIONS_GENERAL) { + uint8_t op = dataProvider->ConsumeIntegralInRange(0, maxGeneral); + operations[op](dataProvider, parcel1); + } + break; + case UNALIGNED_GROUP: + while (dataProvider->remaining_bytes() > 0 && opCnt++ < MAX_OPERATIONS_UNALIGNED) { + uint8_t op = dataProvider->ConsumeIntegralInRange(0, maxUnaligned); + unaligned_operations[op](dataProvider, parcel1); + } + break; + case OTHER_GROUP: + while (dataProvider->remaining_bytes() > 0 && opCnt++ < MAX_OPERATIONS_OTHER) { + uint8_t op = dataProvider->ConsumeIntegralInRange(0, maxOther); + other_operations[op](dataProvider, parcel1); + } + break; + default: + break; + } + FUZZ_LOGI("ParcelTestFunc end"); +} + +} // namespace OHOS + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + FuzzedDataProvider dataProvider(data, size); + OHOS::ParcelTestFunc(data, size, &dataProvider); + return 0; +} diff --git a/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.h b/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..92cc85eff49fe582f7798d5d1878ad62d21e5dfa --- /dev/null +++ b/base/test/fuzztest/parcel_fuzzer/parcel_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARCEL_FUZZER_H +#define PARCEL_FUZZER_H + +#define FUZZ_PROJECT_NAME "parcel_fuzzer" + +#endif // PARCEL_FUZZER_H diff --git a/base/test/fuzztest/parcel_fuzzer/project.xml b/base/test/fuzztest/parcel_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..9b0a87e51ab3e28c253b9d33a726e98f6a9465a8 --- /dev/null +++ b/base/test/fuzztest/parcel_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 10000 + + 300 + + 4096 + + diff --git a/base/test/fuzztest/refbase_fuzzer/BUILD.gn b/base/test/fuzztest/refbase_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..7f18716b0f237c640c37dce4db9b32fd388cbe85 --- /dev/null +++ b/base/test/fuzztest/refbase_fuzzer/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("RefbaseFuzzTest") { + module_out_path = "utils/base" + fuzz_config_file = "." + include_dirs = [ "../" ] + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + defines = [ "DEBUG_FUZZ" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog_base", + ] + sources = [ "refbase_fuzzer.cpp" ] +} +############################################################################### diff --git a/base/test/fuzztest/refbase_fuzzer/corpus/init b/base/test/fuzztest/refbase_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..e4ceac1bcd4e3b3427eb63cea0c28304064333cc --- /dev/null +++ b/base/test/fuzztest/refbase_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/base/test/fuzztest/refbase_fuzzer/project.xml b/base/test/fuzztest/refbase_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..2bb0dbd71370fa7c7b3e53168e4344fa49166dfd --- /dev/null +++ b/base/test/fuzztest/refbase_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.cpp b/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ec2ce49c5effe3bead0858439be9e8290fadcd07 --- /dev/null +++ b/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "refbase_fuzzer.h" + +#include +#include + +#include "fuzz_log.h" +#include "fuzzer/FuzzedDataProvider.h" +#include "refbase.h" +#include "rwlock.h" + +using namespace std; + +namespace OHOS { +const int MAX_THREADS = 10; +const int MAX_OPS = 200; +const int SLEEP_NANO_SECONDS = 10; + +uint32_t GetThreadId() +{ + std::thread::id tid = this_thread::get_id(); + return *(uint32_t*)&tid; +} + +struct TestRefBase : public RefBase { +public: + TestRefBase(bool* deleted, Utils::RWLock& rwLock) : deleted_(deleted), rwLock_(rwLock) + { + rwLock_.LockWrite(); + *deleted_ = false; + rwLock_.UnLockWrite(); + ExtendObjectLifetime(); + } + + virtual ~TestRefBase() + { + rwLock_.LockWrite(); + *deleted_ = true; + rwLock_.UnLockWrite(); + } + + void OnLastStrongRef(const void* objectId) override; + void OnFirstStrongRef(const void* objectId) override; + +private: + bool* deleted_; + Utils::RWLock& rwLock_; +}; + +void TestRefBase::OnLastStrongRef(const void* objectId) +{ + std::this_thread::sleep_for(std::chrono::nanoseconds(SLEEP_NANO_SECONDS)); +} + +void TestRefBase::OnFirstStrongRef(const void* objectId) +{ + std::this_thread::sleep_for(std::chrono::nanoseconds(SLEEP_NANO_SECONDS)); +} + +struct SingleThreadRefCounts { + size_t strongCount = 0; + size_t weakCount = 0; + bool weakRefCounterExists = false; + size_t weakRefCount = 0; +}; + +TestRefBase* g_ref; +bool g_refModified = false; +bool g_refDeleted = false; +Utils::RWLock g_deletedLock; +Utils::RWLock g_strongLock; +Utils::RWLock g_attemptLock; + +const std::vector> decOperations = { + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + if (refState->strongCount > 0) { + refState->strongCount--; + bool shouldLock = refState->strongCount == 0 && refState->weakCount == 0 && refState->weakRefCount == 0; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, DecStrongRef, refState->strongCount = %{public}d", GetThreadId(), + refState->strongCount); + g_ref->DecStrongRef(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + g_refModified = true; + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*& weakRef) { + if (refState->weakRefCount > 0) { + refState->weakRefCount--; + refState->weakRefCounterExists = refState->weakRefCount > 0; + bool shouldLock = refState->strongCount == 0 && refState->weakCount == 0 && refState->weakRefCount == 0; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, weakRef->DecWeakRefCount, refState->weakRefCount = %{public}d", + GetThreadId(), refState->weakRefCount); + weakRef->DecWeakRefCount(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + g_refModified = true; + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + if (refState->weakCount > 0) { + refState->weakCount--; + bool shouldLock = refState->strongCount == 0 && refState->weakCount == 0 && refState->weakRefCount == 0; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, DecWeakRef, refState->weakCount = %{public}d", GetThreadId(), + refState->weakCount); + g_ref->DecWeakRef(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + g_refModified = true; + } + }, +}; + +const std::vector> readOrIncOperations = { + [](SingleThreadRefCounts*, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, GetSptrRefCount", GetThreadId()); + g_ref->GetSptrRefCount(); + }, + + [](SingleThreadRefCounts*, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, GetRefCounter", GetThreadId()); + RefCounter* refCounter = g_ref->GetRefCounter(); + if (refCounter != nullptr) { + refCounter->GetRefCount(); + } + }, + + [](SingleThreadRefCounts*, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, GetWptrRefCount", GetThreadId()); + g_ref->GetWptrRefCount(); + }, + + [](SingleThreadRefCounts*, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, IsAttemptAcquireSet", GetThreadId()); + g_ref->IsAttemptAcquireSet(); + }, + + [](SingleThreadRefCounts*, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, IsExtendLifeTimeSet", GetThreadId()); + g_ref->IsExtendLifeTimeSet(); + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*& weakRef) { + if (refState->weakRefCounterExists) { + FUZZ_LOGI("thread = %{public}u, weakRef->GetWeakRefCount", GetThreadId()); + weakRef->GetWeakRefCount(); + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + g_attemptLock.LockRead(); + FUZZ_LOGI("thread = %{public}u, IncStrongRef", GetThreadId()); + g_ref->IncStrongRef(nullptr); + refState->strongCount++; + g_refModified = true; + g_attemptLock.UnLockRead(); + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*& weakRef) { + if (!refState->weakRefCounterExists) { + FUZZ_LOGI("thread = %{public}u, CreateWeakRef", GetThreadId()); + weakRef = g_ref->CreateWeakRef(nullptr); + refState->weakRefCounterExists = true; + // Only CreateWeakRef then release, will not delete RefBase, so g_refModified will not change. + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*& weakRef) { + if (refState->weakRefCounterExists) { + FUZZ_LOGI("thread = %{public}u, weakRef->IncWeakRefCount", GetThreadId()); + weakRef->IncWeakRefCount(nullptr); + refState->weakRefCount++; + g_refModified = true; + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*& weakRef) { + if (refState->weakRefCounterExists) { + g_attemptLock.LockWrite(); + FUZZ_LOGI("thread = %{public}u, weakRef->AttemptIncStrongRef", GetThreadId()); + weakRef->AttemptIncStrongRef(nullptr); + refState->strongCount++; + g_refModified = true; + g_attemptLock.UnLockWrite(); + } + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + FUZZ_LOGI("thread = %{public}u, IncWeakRef", GetThreadId()); + g_ref->IncWeakRef(nullptr); + refState->weakCount++; + g_refModified = true; + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + g_attemptLock.LockWrite(); + FUZZ_LOGI("thread = %{public}u, AttemptAcquire", GetThreadId()); + g_ref->AttemptAcquire(nullptr); + g_ref->IncStrongRef(nullptr); + refState->strongCount++; + g_refModified = true; + g_attemptLock.UnLockWrite(); + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + g_attemptLock.LockWrite(); + FUZZ_LOGI("thread = %{public}u, AttemptIncStrongRef", GetThreadId()); + g_ref->AttemptIncStrongRef(nullptr); + refState->strongCount++; + g_refModified = true; + g_attemptLock.UnLockWrite(); + }, + + [](SingleThreadRefCounts* refState, WeakRefCounter*&) { + g_attemptLock.LockWrite(); + FUZZ_LOGI("thread = %{public}u, AttemptIncStrong", GetThreadId()); + g_ref->AttemptIncStrong(nullptr); + g_ref->IncStrongRef(nullptr); + refState->strongCount++; + g_refModified = true; + g_attemptLock.UnLockWrite(); + }, +}; + +void TestLoop(const std::vector& fuzzOps) +{ + SingleThreadRefCounts state; + uint8_t lockedOpSize = readOrIncOperations.size(); + uint8_t totalOperationTypes = lockedOpSize + decOperations.size(); + WeakRefCounter* newWeakRef = nullptr; + + for (auto op : fuzzOps) { + auto opVal = op % totalOperationTypes; + if (opVal >= lockedOpSize) { + decOperations[opVal % lockedOpSize](&state, newWeakRef); + } else { + bool shouldLock = state.strongCount == 0 && state.weakCount == 0 && state.weakRefCount == 0; + if (shouldLock) { + g_strongLock.LockRead(); + if (g_refDeleted) { + if (state.weakRefCounterExists) { + FUZZ_LOGI("thread = %{public}u, delete newWeakRef", GetThreadId()); + delete newWeakRef; + } + FUZZ_LOGI("thread = %{public}u return", GetThreadId()); + g_strongLock.UnLockRead(); + return; + } + } + readOrIncOperations[opVal](&state, newWeakRef); + if (shouldLock) { + g_strongLock.UnLockRead(); + } + } + } + + // Clean up WeakRefCounter + if (state.weakRefCounterExists) { + FUZZ_LOGI("thread = %{public}u, delete newWeakRef", GetThreadId()); + delete newWeakRef; + } else if (state.weakRefCount > 0) { + for (; state.weakRefCount > 0; --state.weakRefCount) { + bool shouldLock = state.strongCount == 0 && state.weakCount == 0 && state.weakRefCount == 0; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, clean up DecWeakRefCount, refState->weakRefCount = %{public}d", + GetThreadId(), state.weakRefCount); + newWeakRef->DecWeakRefCount(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + } + } + + // Clean up any weak references + for (; state.weakCount > 0; state.weakCount--) { + bool shouldLock = state.strongCount == 0 && state.weakCount == 1; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, clean up DecWeakRef, refState->weakCount = %{public}d", GetThreadId(), + state.weakCount - 1); + g_ref->DecWeakRef(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + } + + // Clean up any strong references + for (; state.strongCount > 0; state.strongCount--) { + bool shouldLock = state.strongCount == 1; + if (shouldLock) { + g_strongLock.LockWrite(); + } + FUZZ_LOGI("thread = %{public}u, clean up DecStrongRef, refState->strongCount = %{public}d", GetThreadId(), + state.strongCount - 1); + g_ref->DecStrongRef(nullptr); + if (shouldLock) { + g_strongLock.UnLockWrite(); + } + } +} + +void RefbaseTestFunc(const uint8_t* data, size_t size, FuzzedDataProvider* dataProvider) +{ + FUZZ_LOGI("RefbaseTestFunc start"); + g_ref = new TestRefBase(&g_refDeleted, g_deletedLock); + g_refModified = false; + uint8_t threadNum = 1; + vector threads = vector(); + threadNum = dataProvider->ConsumeIntegralInRange(1, MAX_THREADS); + + for (uint8_t i = 0; i < threadNum; i++) { + uint8_t opsSize = 1; + opsSize = dataProvider->ConsumeIntegralInRange(1, MAX_OPS); + vector ops = dataProvider->ConsumeBytes(opsSize); + thread threadTmp = thread(TestLoop, ops); + threads.push_back(move(threadTmp)); + } + + for (thread& th : threads) { + th.join(); + } + + if (!g_refModified && !g_refDeleted) { + delete g_ref; + } + FUZZ_LOGI("RefbaseTestFunc end"); +} + +} // namespace OHOS + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + FuzzedDataProvider dataProvider(data, size); + OHOS::RefbaseTestFunc(data, size, &dataProvider); + return 0; +} diff --git a/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.h b/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..1b51fbec262a53aab83fceaf7cd61f573acbd105 --- /dev/null +++ b/base/test/fuzztest/refbase_fuzzer/refbase_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef REFBASE_FUZZER_H +#define REFBASE_FUZZER_H + +#define FUZZ_PROJECT_NAME "refbase_fuzzer" + +#endif // REFBASE_FUZZER_H diff --git a/base/test/fuzztest/timer_fuzzer/BUILD.gn b/base/test/fuzztest/timer_fuzzer/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..b0194ce6708f256928d1a81f2026012852576aac --- /dev/null +++ b/base/test/fuzztest/timer_fuzzer/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import("//build/test.gni") + +##############################fuzztest########################################## +ohos_fuzztest("TimerFuzzTest") { + module_out_path = "utils/base" + fuzz_config_file = "." + include_dirs = [ "../" ] + cflags = [ + "-g", + "-O0", + "-Wno-unused-variable", + "-fno-omit-frame-pointer", + ] + defines = [ "DEBUG_FUZZ" ] + external_deps = [ + "c_utils:utils", + "hilog:libhilog_base", + ] + sources = [ "timer_fuzzer.cpp" ] +} +############################################################################### diff --git a/base/test/fuzztest/timer_fuzzer/corpus/init b/base/test/fuzztest/timer_fuzzer/corpus/init new file mode 100644 index 0000000000000000000000000000000000000000..e4ceac1bcd4e3b3427eb63cea0c28304064333cc --- /dev/null +++ b/base/test/fuzztest/timer_fuzzer/corpus/init @@ -0,0 +1,14 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FUZZ \ No newline at end of file diff --git a/base/test/fuzztest/timer_fuzzer/project.xml b/base/test/fuzztest/timer_fuzzer/project.xml new file mode 100644 index 0000000000000000000000000000000000000000..2bb0dbd71370fa7c7b3e53168e4344fa49166dfd --- /dev/null +++ b/base/test/fuzztest/timer_fuzzer/project.xml @@ -0,0 +1,25 @@ + + + + + + 1000 + + 300 + + 4096 + + diff --git a/base/test/fuzztest/timer_fuzzer/timer_fuzzer.cpp b/base/test/fuzztest/timer_fuzzer/timer_fuzzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e847745c4195311544087fc062f70169d41157a --- /dev/null +++ b/base/test/fuzztest/timer_fuzzer/timer_fuzzer.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "timer_fuzzer.h" + +#include + +#include "fuzz_log.h" +#include "fuzzer/FuzzedDataProvider.h" +#include "securec.h" +#include "timer.h" + +using namespace std; + +namespace OHOS { +const uint8_t MAX_OPS = 100; +const uint8_t MAX_STRING_LENGTH = 255; +const int MAX_TIME_MS = 100; +std::atomic g_data(0); + +void TimeOutCallback() +{ + g_data++; +} + +const std::vector&)>> ops = { + [](FuzzedDataProvider* dataProvider, Utils::Timer& timer, vector& val) { + uint32_t interval = dataProvider->ConsumeIntegralInRange(0, MAX_TIME_MS); + bool once = dataProvider->ConsumeBool(); + uint32_t timerId = timer.Register(TimeOutCallback, interval, once); + val.push_back(timerId); + FUZZ_LOGI("Register, interval = %{public}d, once = %{public}d, timerId = %{public}d", interval, once, timerId); + uint32_t sleepTime = dataProvider->ConsumeIntegralInRange(0, MAX_TIME_MS); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + FUZZ_LOGI("sleep_for, time = %{public}d, g_data = %{public}d", + sleepTime, g_data.load(std::memory_order_relaxed)); + }, + + [](FuzzedDataProvider* dataProvider, Utils::Timer& timer, vector& val) { + if (val.size() == 0) { + return; + } + size_t id = dataProvider->ConsumeIntegralInRange(0, val.size() - 1); + timer.Unregister(val[id]); + FUZZ_LOGI("Unregister, timerId = %{public}d", val[id]); + for (size_t i = id; i < val.size() - 1; i++) { + swap(val[i], val[i + 1]); + } + val.pop_back(); + }, +}; + +void TimerTestFunc(const uint8_t* data, size_t size, FuzzedDataProvider* dataProvider) +{ + FUZZ_LOGI("TimerTestFunc start"); + string teststr = dataProvider->ConsumeRandomLengthString(MAX_STRING_LENGTH); + uint32_t timeoutMs = dataProvider->ConsumeIntegralInRange(0, MAX_TIME_MS); + Utils::Timer timer(teststr, timeoutMs); + FUZZ_LOGI("Timer, str = %{public}s, ms = %{public}d", teststr.c_str(), timeoutMs); + vector timerIds; + g_data = 0; + + while (dataProvider->remaining_bytes() > 0) { + timer.Setup(); + FUZZ_LOGI("Setup"); + int opCnt = 0; + while (dataProvider->remaining_bytes() > 0 && opCnt++ < MAX_OPS) { + uint8_t op = dataProvider->ConsumeIntegral() % ops.size(); + ops[op](dataProvider, timer, timerIds); + } + timer.Shutdown(); + timerIds.clear(); + FUZZ_LOGI("Shutdown"); + } + FUZZ_LOGI("TimerTestFunc end"); +} + +} // namespace OHOS + +/* Fuzzer entry point */ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + FuzzedDataProvider dataProvider(data, size); + OHOS::TimerTestFunc(data, size, &dataProvider); + return 0; +} diff --git a/base/test/fuzztest/timer_fuzzer/timer_fuzzer.h b/base/test/fuzztest/timer_fuzzer/timer_fuzzer.h new file mode 100644 index 0000000000000000000000000000000000000000..8f3d3b40a7bd981e925d8961af081a9c57c67ed0 --- /dev/null +++ b/base/test/fuzztest/timer_fuzzer/timer_fuzzer.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TIMER_FUZZER_H +#define TIMER_FUZZER_H + +#define FUZZ_PROJECT_NAME "timer_fuzzer" + +#endif // TIMER_FUZZER_H diff --git a/bundle.json b/bundle.json index 9f72ca3afacca48e8767b637c79e547f0596d893..1f1b0bd498c6a7e547235a9af8f8a393d55156b5 100644 --- a/bundle.json +++ b/bundle.json @@ -111,7 +111,8 @@ ], "test": [ "//commonlibrary/c_utils/base/test:unittest", - "//commonlibrary/c_utils/base/test/benchmarktest:benchmarktest" + "//commonlibrary/c_utils/base/test/benchmarktest:benchmarktest", + "//commonlibrary/c_utils/base/test/fuzztest:fuzztest" ] } }