
UT आधारित स्वचालित फ़ज़ ड्राइवर जनरेशन
UTopia यूनिट टेस्ट से स्वचालित रूप से fuzz ड्राइवर जनरेट करने का एक उपकरण है।
UTopia डेवलपर्स को fuzzers लिखने की विशेष जानकारी के बिना fuzz टेस्टिंग करने की सुविधा देगा। यहां तक कि fuzzing से परिचित डेवलपर्स भी fuzz ड्राइवर स्वचालित रूप से जनरेट करके काफी समय बचा सकते हैं।
UTopia उन C/C++ लाइब्रेरीज़ को सपोर्ट करता है जिनके पास GoogleTest, Boost.Test या Tizen TCT के साथ यूनिट टेस्ट हैं।
UTopia द्वारा पाए गए बग देखने के लिए, Trophy पेज पर जाएँ। आप वहाँ कुछ UTopia-आधारित fuzzers भी देख सकते हैं।
आसान सेटअप के लिए, हम UTopia चलाने के लिए Docker इमेज प्रदान करते हैं। आप Docker कंटेनर के अंदर UTopia बिल्ड कर सकते हैं और fuzzers जनरेट/चला सकते हैं।
नीचे दिए गए कमांड से Docker इमेज बनाएँ।
docker buildx build -f docker/Dockerfile -t utopia . #llvm-10
docker buildx build --build-arg LLVM_VERSION=12 -f docker/Dockerfile -t utopia . #llvm-12
UTopia को LLVM संस्करण 10 के साथ सर्वोत्तम कार्य करने के लिए डिज़ाइन किया गया है। और, यह पुष्टि की गई है कि यह LLVM संस्करण 12 पर यूनिट टेस्ट पास करता है। इसलिए हम अनुशंसा करते हैं कि आप LLVM 10 का उपयोग करें, लेकिन यदि आप चाहें तो LLVM संस्करण 12 भी आज़मा सकते हैं।
UTopia LLVM, Protobuf और GoogleTest पर निर्भर करता है। आप निर्भरताओं को मैन्युअल रूप से इंस्टॉल कर सकते हैं, लेकिन हम दी गई Docker इमेज का उपयोग करने की अनुशंसा करते हैं।
रिपॉजिटरी क्लोन करने के बाद, सभी सबमॉड्यूल को इनिशियलाइज़ और अपडेट करें:
git submodule update --init --recursive
UTopia को बिल्ड करने के लिए, नीचे दी गई cmake प्रक्रिया का पालन करें।
cd $UTOPIA_HOME_DIR
cmake -B build -S .
cmake --build build -j$(nproc)
कुछ चुनिंदा प्रोजेक्ट्स के लिए, आप हमारे टूल को अतिरिक्त प्रयास के बिना चलाने हेतु helper स्क्रिप्ट का उपयोग कर सकते हैं। कृपया helper/README.md देखें। अन्य प्रोजेक्ट्स के लिए, कृपया निम्नलिखित मैनुअल देखें।
Target Analyzer लक्ष्य लाइब्रेरी कोड का विश्लेषण करता है और परिणाम को json फ़ाइल के रूप में जनरेट करता है। आवश्यक कमांड-लाइन विकल्प नीचे दिए गए हैं।
target_analyzer --db ${builddb_path} --extern ${extern_path} --public ${api_json_path} --out ${output_path}
Build db एक json फ़ाइल है जिसमें लक्ष्य लाइब्रेरी कोड की AST और IR फ़ाइलों के पथ होते हैं। इसका प्रारूप नीचे जैसा दिखता है।
{
"bc": "/root/fuzz-test-generation/exp/sample/output/bc/libcommon.a.bc",
"ast": [
"/root/fuzz-test-generation/exp/sample/libcommon.a_ast/codec/common/src/ast1.o.ast",
"/root/fuzz-test-generation/exp/sample/libcommon.a_ast/codec/common/src/ast2.o.ast"
],
"project_dir": "/root/fuzz-test-generation/exp/sample"
}
किसी विशेष लाइब्रेरी का LLVM बिटकोड फ़ाइल पथ "bc" कीवर्ड का उपयोग करके निर्दिष्ट किया जाना चाहिए। हम अभी तक केवल एक बिटकोड फ़ाइल स्वीकार करते हैं, इसलिए आप कई बिट कोड फ़ाइलों को एक बिट कोड फ़ाइल में जोड़ने के लिए llvm-link का उपयोग कर सकते हैं।
किसी विशेष लाइब्रेरी की AST फ़ाइल पथ को "ast" कीवर्ड का उपयोग करके निर्दिष्ट किया जाना चाहिए। ध्यान दें कि निर्दिष्ट बिटकोड फ़ाइल और AST फ़ाइलें किसी विशेष लाइब्रेरी के समान स्रोत कोड से जनरेट की जाती हैं।
Target Analyzer सटीक परिणाम के लिए अन्य लाइब्रेरीज़ की target analyzer रिपोर्ट स्वीकार करता है। यह पथ एक निर्देशिका पथ होना चाहिए जहाँ अन्य रिपोर्टें संग्रहीत हों, जिसका अर्थ है कि एक से अधिक लाइब्रेरी रिपोर्ट की अनुमति है।
विश्लेषण किए जाने वाले API फ़ंक्शन नाम। यह नीचे दिए गए प्रारूप की json फ़ाइल होनी चाहिए।
{
"libcommon.a": [
"API1",
"API2",
"API3"
]
}
आप नीचे दिए गए कमांड का उपयोग करके किसी विशेष लाइब्रेरी से API सूची प्राप्त कर सकते हैं।
nm --no-demangle --defined-only -g ${librarypath} | awk '$2=="T" {k=""; for(i=3;i<=NF;i++) k=k $i""; print k}'
Direction गुण एक आवश्यक पैरामीटर है जिसका उपयोग target analyzer करता है। यह संकेत करता है कि किसी फ़ंक्शन में पैरामीटर पढ़ने (Dir_In), लिखने (Dir_Out), या पढ़ने और लिखने दोनों (Dir_In | Dir_Out) के लिए उपयोग किया जाता है। target analyzer इस गुण को निम्नलिखित तत्वों से युक्त एन्यूमरेशन प्रकार के माध्यम से स्पष्ट करता है:
enum Dir {
Dir_NoOp = 0x000, // No operation
Dir_In = 0x100, // Input direction
Dir_Out = 0x010, // Output direction
Dir_Unidentified = 0x001 // Unidentified direction
};
उदाहरण के लिए, दिए गए JSON फ़ाइल अंश में:
{
"Direction": {
"BF_crypt(0)": 256,
"BF_crypt(1)": 272,
"BF_crypt(2)": 272,
"BF_decode(1)": 272
}
}
यह संकेतन यह समझने में मदद करता है कि फ़ंक्शन के भीतर प्रत्येक पैरामीटर का उपयोग इनपुट, आउटपुट, या दोनों के लिए किस प्रकार किया जाता है, जिससे फ़ंक्शन में होने वाले डेटा प्रवाह और किए जाने वाले संचालन की स्पष्ट जानकारी मिलती है।
UT Analyzer लक्ष्य लाइब्रेरी के यूनिट टेस्ट कोड का विश्लेषण करता है और परिणाम को json फ़ाइल के रूप में जनरेट करता है। आवश्यक कमांड-लाइन विकल्प नीचे दिए गए हैं।
ut_analyzer --entry ${entry_path} --extern ${extern_path} --ut ${ut_type} --name ${lib_name} --public ${api_json_path} --out ${output_path}
अधिकांश विकल्प target_analyzer के समान हैं। ध्यान दें कि entry_path को लाइब्रेरी के लिए नहीं, बल्कि यूनिट टेस्ट एक्ज़ीक्यूटेबल के AST/IR फ़ाइलों को निर्दिष्ट करना चाहिए।
लक्ष्य प्रोजेक्ट द्वारा उपयोग किया जाने वाला फ्रेमवर्क, tct, gtest, या boost हो सकता है।
fuzz_generator target_anlayzer और ut_analyzer की रिपोर्ट फ़ाइलों का उपयोग करके fuzz ड्राइवर जनरेट करता है। आवश्यक कमांड-लाइन विकल्प नीचे दिए गए हैं।
fuzz_generator --src ${src_path} --target ${target_analyzer_report_path} --ut ${ut_analyzer_report_path} --public ${api_json_path} --out ${output_dir}
src_path वह निर्देशिका पथ है जहाँ यूनिट टेस्ट का स्रोत कोड संग्रहीत होता है। fuzz_generator इस निर्देशिका की प्रतिलिपि बनाता है और इन प्रतिलिपि की गई फ़ाइलों को संशोधित करके fuzz ड्राइवर जनरेट करता है।
अन्य विकल्प target_analyzer विकल्पों के समान हैं।
यह अनुभाग जनरेट किए गए fuzz ड्राइवर की कार्यक्षमता और कार्यान्वयन विवरण का वर्णन करता है, जो यह समझने के लिए महत्वपूर्ण है कि fuzz टेस्टिंग लक्ष्य स्रोत कोड के साथ कैसे एकीकृत होती है।
fuzz_entry.cc (स्वतः-जनित फ़ाइल)
DEFINE_PROTO_FUZZER(const AutoFuzz::FuzzArgsProfile &autofuzz_mutation) {
... /* Values are assigned from autofuzz_mutation */
enterAutofuzz();
}
यह फ़ंक्शन जनरेट किए गए fuzz ड्राइवर के लिए प्रवेश बिंदु के रूप में कार्य करता है। यह fuzzer द्वारा उत्पन्न मानों को लेता है और उन्हें चरों में निर्दिष्ट करता है, जिनका उपयोग बाद में लाइब्रेरी फ़ंक्शन को आमंत्रित करने के लिए किया जाता है। अंत में, फ़ंक्शन fuzz टेस्टिंग प्रक्रिया को आगे बढ़ाने के लिए enterAutofuzz(); को कॉल करता है।
लक्ष्य परीक्षण केस को परिभाषित करने वाला स्रोत कोड
#ifdef __cplusplus
extern "C" {
#endif
void enterAutofuzz() {
class AutofuzzTest : public ::Parser_TestArray_Test {
public:
void runTest() {
try {
SetUpTestCase();
} catch (std::exception &E) {}
try {
SetUp();
} catch (std::exception &E) {}
try {
TestBody();
} catch (std::exception &E) {}
try {
TearDown();
} catch (std::exception &E) {}
try {
TearDownTestCase();
} catch (std::exception &E) {}
}
};
AutofuzzTest Fuzzer;
Fuzzer.runTest();
}
#ifdef __cplusplus
}
#endif
लक्ष्य परीक्षण केस को परिभाषित करने वाले स्रोत कोड के अंतिम भाग में, UTopia enterAutofuzz फ़ंक्शन इंजेक्ट करता है। इस फ़ंक्शन के भीतर, AutofuzzTest क्लास घोषित की जाती है, जो ::Parser_TestArray_Test से इनहेरिट करती है। यह पैरेंट क्लास GoogleTest फ्रेमवर्क द्वारा परिभाषित होती है और संबोधित किए जा रहे टेस्ट केस के लिए विशिष्ट होती है, जैसा कि नीचे दिखाया गया है:
TEST(Parser, TestArray)
{
...
}
runTest() विधि टेस्ट केस को GoogleTest से स्वतंत्र रूप से निष्पादित करती है, और उन पाँच फ़ंक्शनों को आमंत्रित करती है जिन्हें GoogleTest सामान्यतः प्रत्येक टेस्ट केस के लिए कॉल करता है। यह दृष्टिकोण GoogleTest फ्रेमवर्क पर निर्भर किए बिना सीधे परीक्षण निष्पादन की अनुमति देता है।
Fuzz ड्राइवर ${output_dir} में जनरेट होते हैं, जिसे fuzz_generator को कमांड-लाइन विकल्प के रूप में पास किया जाता है। आप उन्हें यूनिट टेस्ट एक्ज़ीक्यूटेबल के लिए उपयोग किए गए समान कंपाइलर कमांड का उपयोग करके बिल्ड कर सकते हैं। ध्यान दें कि आपको fuzz_generator द्वारा जनरेट की गई fuzz_entry.cc, FuzzArgsProto.pb.cc फ़ाइलों को शामिल करना चाहिए। आपको वे फ़ाइलें ${output_dir} में मिलेंगी।
python3 -m helper.make {library name}
python3 -m helper.build {library name}
आपको 'UTopia' की पूरी पाइपलाइन के सभी आउटपुट निम्नलिखित दो निर्देशिकाओं में मिलेंगे:
exp/{library name}/output,result/test/{library name}