Skip to content

Commit 7729025

Browse files
authored
Fix crashes found by fuzzing and improve fuzzing utilities (#158)
* fix: crash on redefine of define with no content * fix: crash on reverse redefine with no content * fix: crash on import all with no import name * impr: fuzzing tooling
1 parent 9833500 commit 7729025

4 files changed

Lines changed: 141 additions & 10 deletions

File tree

fuzz/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ afl-fuzz -i inputs -o output -x ./dict/hexpat.dict -- ./plfuzz @@
2929
```
3030
This will run a simple fuzzing session with the inputs in the `inputs` folder and outputting to the `output` folder.
3131

32+
### Minimization
33+
34+
To minimize the found crashes, you can simpy use the `afl-tmin` tool that comes with AFL++.
35+
Here is an example of how to minimize a crash:
36+
```bash
37+
afl-tmin -i output/crashes/<crash_file> -o output/minimized/<crash_file> -- ./plfuzz @@
38+
```
39+
40+
We also provide a small script to minimize all crashes in the `output/crashes` folder:
41+
```bash
42+
python3 afl-pytmin.py output/crashes output/minimized ./plfuzz
43+
```
44+
3245
### Debugging
3346
During the session, if the fuzzer finds crashes or halts, it will output the crashing input to the
3447
`output/crashes` or `output/hangs` folder.

fuzz/afl-pytmin.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/env/python
2+
# Adapted from https://github.com/ilsani/afl-pytmin/tree/master
3+
# ALL RIGHTS RESERVED TO THE ORIGINAL AUTHOR
4+
#
5+
# python afl-pytmin.py <input-dir> <output-dir> <harness-bin>
6+
#
7+
8+
import sys
9+
import os
10+
import subprocess
11+
12+
from queue import Queue
13+
from threading import Thread
14+
15+
AFL_TMIN_CMD = "afl-tmin"
16+
17+
def tmin(workerid, queue, output_dirname, harness_bin):
18+
19+
while True:
20+
21+
data = queue.get()
22+
filename = data["filename"]
23+
counter = data["counter"]
24+
25+
output_path = "%s/%d" %(output_dirname, counter)
26+
27+
args = [
28+
AFL_TMIN_CMD,
29+
"-i",
30+
filename,
31+
"-o",
32+
output_path,
33+
"-t",
34+
"3000",
35+
"-m",
36+
"4096",
37+
"--",
38+
harness_bin,
39+
"@@"
40+
]
41+
42+
print("[i] Processing %s file ... " %(filename))
43+
44+
with open(os.devnull, 'w') as devnull:
45+
p = subprocess.Popen(args, stdout=devnull, stderr=devnull, shell=False)
46+
# p.communicate()
47+
p.wait()
48+
49+
# p.stdout.close()
50+
51+
queue.task_done()
52+
53+
def read_filename(input_dirname):
54+
55+
for f in os.listdir(input_dirname):
56+
yield ("%s/%s" %(input_dirname, f)).strip()
57+
58+
def main():
59+
60+
try:
61+
62+
n_cores = 5
63+
input_dirname = sys.argv[1]
64+
output_dirname = sys.argv[2]
65+
harness_bin = sys.argv[3]
66+
67+
if not os.path.exists(output_dirname):
68+
os.makedirs(output_dirname)
69+
70+
queue = Queue()
71+
72+
# Set up some threads
73+
for i in range(n_cores):
74+
worker = Thread(target=tmin, args=(i, queue, output_dirname, harness_bin, ))
75+
worker.setDaemon(True)
76+
worker.start()
77+
78+
# Fill the queue
79+
counter = 0
80+
for filename in read_filename(input_dirname):
81+
82+
data = { "filename": filename,
83+
"counter": counter }
84+
85+
queue.put(data)
86+
counter = counter + 1
87+
88+
queue.join()
89+
90+
except Exception as e:
91+
print("[!] Error: %s" %(str(e)))
92+
93+
if __name__ == "__main__":
94+
main()

lib/include/pl/core/preprocessor.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,12 @@ namespace pl::core {
140140
std::unordered_map<Token::Directive, api::DirectiveHandler> m_directiveHandlers;
141141
std::unordered_map<Token::Keyword, api::StatementHandler> m_statementHandlers;
142142

143-
std::unordered_map<std::string, std::vector<Token>> m_defines;
143+
struct Define {
144+
Token nameToken;
145+
std::vector<Token> values;
146+
};
147+
148+
std::unordered_map<std::string, Define> m_defines;
144149
std::unordered_map<std::string, std::vector<std::pair<std::string, u32>>> m_pragmas;
145150
std::vector<ExcludedLocation> m_excludedLocations;
146151

lib/source/pl/core/preprocessor.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,17 +178,34 @@ namespace pl::core {
178178
}
179179

180180
if (m_defines.contains(name)) {
181-
bool isValueSame = m_defines[name] == values;
181+
bool isValueSame = m_defines[name].values == values;
182182
if (!isValueSame) {
183-
errorAt(values[0].location, "Previous definition occurs at line '{}'.", m_defines[name][0].location.line);
184-
errorAt(m_defines[name][0].location, "Macro '{}' is redefined in line '{}'.", name, values[0].location.line);
185-
m_defines[name].clear();
186-
m_defines[name] = values;
183+
auto& define = m_defines[name];
184+
Location defineLocation{};
185+
186+
if (define.values.empty()) {
187+
defineLocation = define.nameToken.location;
188+
} else {
189+
defineLocation = define.values[0].location;
190+
}
191+
192+
Location ourLocation{};
193+
if (values.empty()) {
194+
ourLocation = token.location;
195+
} else {
196+
ourLocation = values[0].location;
197+
}
198+
199+
errorAt(ourLocation, "Previous definition occurs at line '{}'.", defineLocation.line);
200+
errorAt(defineLocation, "Macro '{}' is redefined in line '{}'.", name, defineLocation.line);
201+
202+
m_defines[name] = { token, values };
203+
187204
removeKey(token);
188205
m_keys.emplace_back(token);
189206
}
190207
} else {
191-
m_defines[name] = values;
208+
m_defines[name] = { token, values };
192209
m_keys.emplace_back(token);
193210
}
194211
}
@@ -340,7 +357,7 @@ namespace pl::core {
340357
if (auto *tokenLiteral = std::get_if<Token::Literal>(&m_token->value); m_token->type == Token::Type::String && tokenLiteral != nullptr) {
341358
path = tokenLiteral->toString(false);
342359

343-
} else if (auto *identifier = std::get_if<Token::Identifier>(&m_token->value); m_token->type == Token::Type::Identifier) {
360+
} else if (auto *identifier = std::get_if<Token::Identifier>(&m_token->value); m_token->type == Token::Type::Identifier && identifier != nullptr) {
344361
saveImport.push_back(*m_token);
345362
path = identifier->get();
346363
m_token++;
@@ -476,7 +493,7 @@ namespace pl::core {
476493
Token::Identifier *tokenIdentifier = std::get_if<Token::Identifier>(&m_token->value);
477494
if (tokenIdentifier != nullptr)
478495
tokenIdentifier->setType(Token::Identifier::IdentifierType::Macro);
479-
for (const auto &newToken: m_defines[keyIdentifier->get()])
496+
for (const auto &newToken: m_defines[keyIdentifier->get()].values)
480497
resultValues.push_back(newToken);
481498
} else
482499
resultValues.push_back(value);
@@ -614,7 +631,9 @@ namespace pl::core {
614631
}
615632

616633
void Preprocessor::addDefine(const std::string &name, const std::string &value) {
617-
m_defines[name] = {Token { Token::Type::String, value, {nullptr, 0, 0, 0 } } } ;
634+
auto nameToken = Token { Token::Type::Identifier, name, Location::Empty() };
635+
auto valueToken = Token { Token::Type::String, value, Location::Empty() };
636+
m_defines[name] = { nameToken, { valueToken }};
618637
}
619638

620639
void Preprocessor::addPragmaHandler(const std::string &pragmaType, const api::PragmaHandler &handler) {

0 commit comments

Comments
 (0)