cpp
/*
* Copyright (C) 2022 The Android Open Source Project
*
* 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 <fuzzer/FuzzedDataProvider.h>
#include <hidl/metadata.h>
#include <import_parser.h>
#include <interface_utils.h>
#include <rlimit_parser.h>
using namespace android;
using namespace android::init;
const std::vector<std::string> kValidInputs[] = {
{"", "cpu", "10", "10"}, {"", "RLIM_CPU", "10", "10"}, {"", "12", "unlimited", "10"},
{"", "13", "-1", "10"}, {"", "14", "10", "unlimited"}, {"", "15", "10", "-1"},
};
const std::string kValidPaths[] = {
"/system/etc/init/hw/init.rc",
"/system/etc/init",
};
const int32_t kMaxBytes = 256;
const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
class InitParserFuzzer {
public:
InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
void Process();
private:
void InvokeParser();
void InvokeLimitParser();
void InvokeInterfaceUtils();
InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
FuzzedDataProvider fdp_;
};
void InitParserFuzzer::InvokeLimitParser() {
if (fdp_.ConsumeBool()) {
std::vector<std::string> input;
input.push_back("");
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
ParseRlimit(input);
} else {
ParseRlimit(fdp_.PickValueInArray(kValidInputs));
}
}
std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
std::vector<HidlInterfaceMetadata> random_interface;
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
HidlInterfaceMetadata metadata;
metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
}
random_interface.push_back(metadata);
}
return random_interface;
}
InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
InterfaceInheritanceHierarchyMap result;
std::vector<HidlInterfaceMetadata> random_interface;
if (fdp_.ConsumeBool()) {
random_interface = GenerateInterfaceMetadata();
} else {
random_interface = HidlInterfaceMetadata::all();
}
for (const HidlInterfaceMetadata& iface : random_interface) {
std::set<FQName> inherited_interfaces;
for (const std::string& intf : iface.inherited) {
FQName fqname;
(void)fqname.setTo(intf);
inherited_interfaces.insert(fqname);
}
FQName fqname;
(void)fqname.setTo(iface.name);
result[fqname] = inherited_interfaces;
}
return result;
}
void InitParserFuzzer::InvokeInterfaceUtils() {
InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
SetKnownInterfaces(hierarchy_map);
IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
std::set<std::string> interface_set;
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
[&]() {
interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
},
[&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
[&]() { interface_set.insert(kValidInterfaces); },
});
set_interface_values();
}
CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
}
void InitParserFuzzer::InvokeParser() {
Parser parser;
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
parser.AddSectionParser(name, std::make_unique<ImportParser>(&parser));
std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths)
: fdp_.ConsumeRandomLengthString(kMaxBytes);
parser.ParseConfig(path);
parser.ParseConfigFileInsecure(path, false /* follow_symlinks */);
}
void InitParserFuzzer::Process() {
while (fdp_.remaining_bytes()) {
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
[&]() { InvokeParser(); },
[&]() { InvokeInterfaceUtils(); },
[&]() { InvokeLimitParser(); },
});
invoke_parser_fuzzer();
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
InitParserFuzzer init_parser_fuzzer(data, size);
init_parser_fuzzer.Process();
return 0;
}
源码路径:
代码分析:
根据提供的代码,我分析了其逻辑和总结了代码的主要作用:
-
这是一个针对Android init解析器的Fuzz测试代码。
-
定义了相关的输入样本空间,包括init配置语句、init文件路径等。
-
InitParserFuzzer类封装了Fuzz测试逻辑。
-
Process()方法是主要的Fuzz逻辑,它会随机调用不同的解析组件进行测试。
-
InvokeParser()方法对init配置文件解析器进行Fuzz测试。
-
InvokeLimitParser()方法对rlimit解析进行测试。
-
InvokeInterfaceUtils()对AIDL接口分析代码进行测试。
-
GenerateHierarchyMap()随机生成接口继承关系。
-
GenerateInterfaceMetadata()随机生成接口元数据。
-
LLVMFuzzerTestOneInput()是LibFuzzer入口,将测试数据传入InitParserFuzzer。
总结:这是一段针对Android init框架各组件的Fuzzing测试代码。主要对init配置文件解析、rlimit解析和AIDL接口分析代码进行安全性Fuzz测试,通过随机的数据探测潜在的崩溃或异常,从而提高init框架的健壮性和安全性。
cpp
const std::vector<std::string> kValidInputs[] = {
{"", "cpu", "10", "10"}, {"", "RLIM_CPU", "10", "10"}, {"", "12", "unlimited", "10"},
{"", "13", "-1", "10"}, {"", "14", "10", "unlimited"}, {"", "15", "10", "-1"},
};
这是一段 C++ 代码,它定义了一个名为 kValidInputs
的常量数组,数组的元素类型是 std::vector<std::string>
,即字符串向量。¹
数组中有六个字符串向量,每个字符串向量包含四个字符串,分别表示资源限制的类型、软限制、硬限制和期望的结果。²
资源限制的类型可以是 cpu
、RLIM_CPU
或一个整数,表示要设置或获取的资源的编号。³
软限制和硬限制可以是一个整数或 unlimited
,表示要设置的资源使用量的上限。³
期望的结果可以是一个整数或 -1
,表示要检查的资源使用量是否符合预期。⁴
这段代码可能是用于测试某个函数的输入和输出的,例如 setrlimit
或 getrlimit
,这些函数可以用于控制进程的资源使用情况。.
cpp
const std::string kValidPaths[] = {
"/system/etc/init/hw/init.rc",
"/system/etc/init",
};
这是一段 C++ 代码,它定义了一个名为 kValidPaths
的常量数组,数组的元素类型是 std::string
,即字符串。
数组中有两个字符串,分别表示两个 Android 系统的配置文件的路径。²
/system/etc/init/hw/init.rc
是一个用于初始化硬件相关的服务和属性的配置文件。³/system/etc/init
是一个用于存放其他 init.rc 文件的目录,这些文件可以用于定义不同的服务、动作和属性。
这段代码可能是用于测试 Parser 类的输入和输出的,Parser 类是一个用于解析 init.rc 文件的类。
cpp
const int32_t kMaxBytes = 256;
const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
这是一段 C++ 代码,它定义了两个常量变量,分别是 kMaxBytes
和 kValidInterfaces
。
kMaxBytes
是一个int32_t
类型的变量,它表示一个 32 位的有符号整数,它的值是 256。² 这个变量可能是用于限制字符串的最大长度的。kValidInterfaces
是一个std::string
类型的变量,它表示一个字符串,它的值是"android.frameworks.vr.composer@2.0::IVrComposerClient"
。这个变量可能是用于表示一个 Android 系统的接口的名字,这个接口是一个用于虚拟现实的组件。
cpp
class InitParserFuzzer {
public:
InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
void Process();
private:
void InvokeParser();
void InvokeLimitParser();
void InvokeInterfaceUtils();
InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
FuzzedDataProvider fdp_;
};
这段代码的逻辑和作用是:
- 这段代码是一个用于测试 Android 系统初始化程序的模糊器¹,它使用了 FuzzedDataProvider² 这个库来生成随机的输入数据。
- 这段代码定义了一个 InitParserFuzzer 类,它有一个构造函数,一个 Process 方法,和四个私有方法。
- 构造函数接受一个指向数据的指针和一个数据的大小,然后用它们初始化一个 FuzzedDataProvider 对象,用于后续的数据生成。
- Process 方法是模糊器的主要入口,它调用了三个私有方法:InvokeParser,InvokeLimitParser,和 InvokeInterfaceUtils,分别用于测试不同的功能。
- InvokeParser 方法用于测试 Parser 类,它是一个用于解析 init.rc 文件的类,init.rc 文件是 Android 系统的配置文件³。这个方法创建了一个 Parser 对象,然后根据随机生成的名字和路径,调用了 AddSectionParser 和 ParseConfigFile 方法,分别用于添加一个解析器和解析一个配置文件。
- InvokeLimitParser 方法用于测试 ParseRlimit 函数,它是一个用于解析资源限制的函数,资源限制是一种用于控制进程的资源使用的机制。这个方法根据随机生成的布尔值,选择了一个随机的或者预定义的字符串向量作为输入,然后调用了 ParseRlimit 函数。
- InvokeInterfaceUtils 方法用于测试一些与接口相关的函数,接口是一种用于定义 Android 系统服务的规范。这个方法首先调用了 GenerateHierarchyMap 方法,生成了一个接口继承关系的映射,然后调用了 SetKnownInterfaces 和 IsKnownInterface 函数,分别用于设置和检查已知的接口。然后,这个方法生成了一个随机的或者预定义的接口集合,然后调用了 CheckInterfaceInheritanceHierarchy 函数,用于检查接口的继承关系是否正确。
- GenerateHierarchyMap 方法用于生成一个接口继承关系的映射,它根据随机生成的布尔值,选择了一个随机的或者预定义的接口元数据向量,然后遍历每个接口元数据,提取它的名字和继承的接口,然后将它们存储在一个映射中。
- GenerateInterfaceMetadata 方法用于生成一个接口元数据向量,它根据随机生成的大小,生成了一些随机的接口元数据,每个接口元数据包含一个名字和一些继承的接口,然后将它们存储在一个向量中。
cpp
void InitParserFuzzer::InvokeLimitParser() {
if (fdp_.ConsumeBool()) {
std::vector<std::string> input;
input.push_back("");
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
ParseRlimit(input);
} else {
ParseRlimit(fdp_.PickValueInArray(kValidInputs));
}
}
这段代码的逻辑和作用是:
- 这段代码是 InitParserFuzzer 类的一个私有方法,它用于测试 ParseRlimit 函数,这个函数是一个用于解析资源限制的函数,资源限制是一种用于控制进程的资源使用的机制。¹
- 这段代码首先调用了 fdp_.ConsumeBool() 方法,这个方法是 FuzzedDataProvider 类的一个方法,它用于从数据中随机消耗一个布尔值。²
- 如果这个布尔值为真,那么这段代码创建了一个空的字符串向量,然后分别调用了 fdp_.ConsumeRandomLengthString(kMaxBytes) 方法,这个方法用于从数据中随机消耗一个最大长度为 kMaxBytes 的字符串,然后将这个字符串添加到字符串向量中。这样,这段代码生成了一个包含四个随机字符串的字符串向量,然后将这个字符串向量作为输入,调用了 ParseRlimit 函数。
- 如果这个布尔值为假,那么这段代码调用了 fdp_.PickValueInArray(kValidInputs) 方法,这个方法用于从 kValidInputs 数组中随机选择一个字符串向量,然后将这个字符串向量作为输入,调用了 ParseRlimit 函数。
- 这样,这段代码可以用于测试 ParseRlimit 函数在不同的输入下的行为和结果,以检查是否有错误或异常。
cpp
InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
InterfaceInheritanceHierarchyMap result;
std::vector<HidlInterfaceMetadata> random_interface;
if (fdp_.ConsumeBool()) {
random_interface = GenerateInterfaceMetadata();
} else {
random_interface = HidlInterfaceMetadata::all();
}
for (const HidlInterfaceMetadata& iface : random_interface) {
std::set<FQName> inherited_interfaces;
for (const std::string& intf : iface.inherited) {
FQName fqname;
(void)fqname.setTo(intf);
inherited_interfaces.insert(fqname);
}
FQName fqname;
(void)fqname.setTo(iface.name);
result[fqname] = inherited_interfaces;
}
return result;
}
这段代码的逻辑和作用是:
- 这段代码是 InitParserFuzzer 类的一个私有方法,它用于生成一个接口继承关系的映射,接口继承关系是一种用于表示 Android 系统服务之间的层次结构的数据结构¹。
- 这段代码首先创建了一个空的接口继承关系的映射,然后根据 fdp_.ConsumeBool() 方法,这个方法是 FuzzedDataProvider 类的一个方法,它用于从数据中随机消耗一个布尔值,选择了一个随机的或者预定义的接口元数据向量,接口元数据是一种用于描述 Android 系统服务的规范的结构体²。
- 然后,这段代码用一个 for 循环,遍历每个接口元数据,提取它的名字和继承的接口,然后将它们转换为 FQName 类型,FQName 是一种用于表示接口的完全限定名的类³,然后将它们存储在一个集合和一个映射中。
- 最后,这段代码返回这个映射,这个映射可以用于测试一些与接口相关的函数,例如 SetKnownInterfaces 和 CheckInterfaceInheritanceHierarchy 等。
cpp
void InitParserFuzzer::InvokeInterfaceUtils() {
InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
SetKnownInterfaces(hierarchy_map);
IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
std::set<std::string> interface_set;
for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
[&]() {
interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
},
[&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
[&]() { interface_set.insert(kValidInterfaces); },
});
set_interface_values();
}
CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
}
这段代码的逻辑和作用是:
- 这段代码是 InitParserFuzzer 类的一个私有方法,它用于测试一些与接口相关的函数,接口是一种用于定义 Android 系统服务的规范。
- 这段代码首先调用了 GenerateHierarchyMap 方法,生成了一个接口继承关系的映射,这个映射表示了不同的接口之间的层次结构。
- 然后,这段代码调用了 SetKnownInterfaces 函数,将这个映射作为参数传入,这个函数用于设置已知的接口,以便后续的检查。
- 接着,这段代码调用了 IsKnownInterface 函数,将一个随机生成的字符串作为参数传入,这个函数用于检查这个字符串是否是一个已知的接口的名字。
- 然后,这段代码创建了一个空的接口集合,然后根据一个随机生成的大小,循环添加一些随机的或者预定义的接口的名字到这个集合中,这些接口的名字可以是 aidl 类型的,也可以是 hidl 类型的,也可以是 kValidInterfaces 这个常量变量的值。
- 最后,这段代码调用了 CheckInterfaceInheritanceHierarchy 函数,将这个接口集合和这个映射作为参数传入,这个函数用于检查这个接口集合中的每个接口是否符合这个映射中的继承关系。
这段代码的作用是用于测试 Android 系统服务的接口的正确性和效率。
cpp
void InitParserFuzzer::InvokeParser() {
Parser parser;
std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
parser.AddSectionParser(name, std::make_unique<ImportParser>(&parser));
std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths)
: fdp_.ConsumeRandomLengthString(kMaxBytes);
parser.ParseConfig(path);
parser.ParseConfigFileInsecure(path, false /* follow_symlinks */);
}
这段代码的逻辑和作用是:
- 这段代码是 InitParserFuzzer 类的一个私有方法,它用于测试 Parser 类,这个类是一个用于解析 init.rc 文件的类,init.rc 文件是 Android 系统的配置文件¹。
- 这段代码首先创建了一个 Parser 对象,然后根据 fdp_.ConsumeBool() 方法,这个方法是 FuzzedDataProvider 类的一个方法,它用于从数据中随机消耗一个布尔值,选择了一个随机的或者预定义的字符串作为解析器的名字,这个名字可以是 "import" 或者其他的²。
- 然后,这段代码调用了 parser.AddSectionParser 方法,将这个名字和一个 ImportParser 对象作为参数传入,这个方法用于添加一个解析器到 Parser 对象中,ImportParser 是一个用于解析 import 语句的解析器,import 语句是一种用于导入其他 init.rc 文件的语句³。
- 接着,这段代码再根据 fdp_.ConsumeBool() 方法,选择了一个随机的或者预定义的字符串作为配置文件的路径,这个路径可以是 kValidPaths 数组中的一个或者其他的²。
- 然后,这段代码调用了 parser.ParseConfig 方法,将这个路径作为参数传入,这个方法用于解析一个配置文件,并将其中的服务、动作和属性添加到 Parser 对象中。
- 最后,这段代码调用了 parser.ParseConfigFileInsecure 方法,将这个路径和一个布尔值作为参数传入,这个方法用于以不安全的方式解析一个配置文件,即不检查文件的权限和所有者,这个布尔值表示是否跟随符号链接。
- 这样,这段代码可以用于测试 Parser 类在不同的输入下的行为和结果,以检查是否有错误或异常。
cpp
void InitParserFuzzer::Process() {
while (fdp_.remaining_bytes()) {
auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
[&]() { InvokeParser(); },
[&]() { InvokeInterfaceUtils(); },
[&]() { InvokeLimitParser(); },
});
invoke_parser_fuzzer();
}
}
这段代码的逻辑和作用是:
- 这段代码是 InitParserFuzzer 类的一个公有方法,它是模糊器的主要入口,用于测试 Android 系统初始化程序的不同功能¹。
- 这段代码用一个 while 循环,循环执行直到数据被消耗完,每次循环中,它调用了 fdp_.PickValueInArray 方法,这个方法是 FuzzedDataProvider 类的一个方法,它用于从一个数组中随机选择一个元素,这个数组中的元素是一些函数对象,分别对应了 InitParserFuzzer 类的三个私有方法:InvokeParser,InvokeInterfaceUtils,和 InvokeLimitParser²。
- 然后,这段代码将这个随机选择的函数对象赋值给一个变量 invoke_parser_fuzzer,然后调用这个变量,这相当于调用了 InitParserFuzzer 类的其中一个私有方法,这些私有方法分别用于测试 Parser 类,ParseRlimit 函数,和一些与接口相关的函数¹。
- 这样,这段代码可以用于随机地测试 Android 系统初始化程序的不同功能,以检查是否有错误或异常。
cpp
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
InitParserFuzzer init_parser_fuzzer(data, size);
init_parser_fuzzer.Process();
return 0;
}
这段代码的逻辑和作用是:
- 这段代码是一个 C 函数,它的名字是 LLVMFuzzerTestOneInput,它是 libFuzzer 库的一个接口,用于将随机生成的数据传递给被测对象¹。
- 这个函数接受两个参数,一个是指向数据的指针,另一个是数据的大小,它们的类型分别是 const uint8_t* 和 size_t。
- 这个函数的函数体中,首先创建了一个 InitParserFuzzer 对象,它是一个用于测试 Android 系统初始化程序的模糊器²,然后将数据和大小作为参数传递给它的构造函数。
- 然后,这个函数调用了 init_parser_fuzzer.Process() 方法,这个方法是模糊器的主要入口,它用于随机地测试 Android 系统初始化程序的不同功能,例如解析配置文件,设置资源限制,和检查接口继承关系等²。
- 最后,这个函数返回 0,表示成功执行,这个返回值是 libFuzzer 库的要求¹。
- 这样,这段代码可以用于将 libFuzzer 库和 InitParserFuzzer 类结合起来,用于对 Android 系统初始化程序进行模糊测试,以检查是否有错误或异常。