Skip to content

Commit 8cd8479

Browse files
committed
Merge branch 'BadHorizontalScrolling' of https://github.com/paxcut/PatternLanguage into HEAD
2 parents 92e6e84 + 3474b26 commit 8cd8479

8 files changed

Lines changed: 168 additions & 15 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/lexer.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace pl::core {
4949
std::optional<u128> parseInteger(std::string_view literal);
5050

5151
Token makeToken(const Token& token, size_t length = 1);
52-
static Token makeTokenAt(const Token& token, Location& location, size_t length = 1);
52+
Token makeTokenAt(const Token& token, Location& location, size_t length = 1);
5353
void addToken(const Token& token);
5454
bool hasTheLineEnded(const char &ch) {
5555
if(ch == '\n') {

lib/include/pl/core/preprocessor.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ namespace pl::core {
3636
void addStatementHandler(const Token::Keyword &statementType, const api::StatementHandler &handler);
3737
void removePragmaHandler(const std::string &pragmaType);
3838
void removeDirectiveHandler(const Token::Directive &directiveType);
39-
39+
size_t getLongestLineLength() const { return m_longestLineLength; }
40+
void setLongestLineLength(size_t length) { m_longestLineLength = length; }
4041
void validateOutput();
4142

4243
[[nodiscard]] const std::vector<ExcludedLocation>& getExcludedLocations() const {
@@ -140,7 +141,12 @@ namespace pl::core {
140141
std::unordered_map<Token::Directive, api::DirectiveHandler> m_directiveHandlers;
141142
std::unordered_map<Token::Keyword, api::StatementHandler> m_statementHandlers;
142143

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

@@ -157,7 +163,7 @@ namespace pl::core {
157163
std::vector<Token> m_result;
158164
std::vector<Token> m_output;
159165
std::vector<std::string> m_namespaces;
160-
166+
size_t m_longestLineLength = 0;
161167
api::Source* m_source = nullptr;
162168

163169
bool m_onlyIncludeOnce = false;

lib/source/pl/core/ast/ast_node_attribute.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ namespace pl::core::ast {
335335
auto actualSize = pattern->getSize();
336336
if (actualSize < requestedSize) {
337337
pattern->setSize(requestedSize);
338-
evaluator->setReadOffset(u64(evaluator->getReadOffset() + (requestedSize - actualSize)));
338+
evaluator->setReadOffset(pattern->getOffset() + requestedSize);
339339
}
340340
else if (actualSize > requestedSize)
341341
err::E0004.throwError("Type size larger than expected", fmt::format("Pattern of type {} is larger than expected. Expected size {}, got {}", pattern->getTypeName(), requestedSize, actualSize), node->getLocation());
@@ -384,6 +384,8 @@ namespace pl::core::ast {
384384
.bitOffset = bitfieldPattern->getBitOffset()
385385
});
386386
}
387+
} else if (attributable->hasAttribute("fixed_size", true)) {
388+
// read offset might be modified by fixed_size's padding. keep it as is.
387389
} else {
388390
evaluator->setBitwiseReadOffset(endOffset);
389391
}

lib/source/pl/core/lexer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,11 +458,13 @@ namespace pl::core {
458458
Token Lexer::makeToken(const Token &token, const size_t length) {
459459
auto location = this->location();
460460
location.length = length;
461+
m_longestLineLength = std::max(m_longestLineLength, location.column + location.length);
461462
return { token.type, token.value, location };
462463
}
463464

464465
Token Lexer::makeTokenAt(const Token &token, Location& location, const size_t length) {
465466
location.length = length;
467+
m_longestLineLength = std::max(m_longestLineLength, location.column + location.length);
466468
return { token.type, token.value, location };
467469
}
468470

lib/source/pl/core/preprocessor.cpp

Lines changed: 29 additions & 10 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);
@@ -584,7 +601,7 @@ namespace pl::core {
584601
this->error(item);
585602
return { m_output, collectErrors() };
586603
}
587-
604+
setLongestLineLength(lexer->getLongestLineLength());
588605
m_token = m_result.begin();
589606
m_initialized = true;
590607
while (!eof())
@@ -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) {

tests/include/test_patterns/test_pattern_attributes.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,31 @@ namespace pl::test {
4141
return 1337;
4242
};
4343
44+
struct FixedSizeTest1 {
45+
u8 x;
46+
} [[fixed_size(4)]];
47+
48+
struct FixedSizeTest2 {
49+
u32 pos = $;
50+
u32 elem [[fixed_size(7)]];
51+
std::assert($ == pos + 7, "Fixed size variable attribute padding not working");
52+
53+
pos = $;
54+
FixedSizeTest1 fixedSizeTest1;
55+
std::assert($ == pos + 4, "Fixed size pattern attribute padding not working");
56+
};
57+
4458
FormatTransformTest formatTransformTest @ 0x00;
4559
SealedTest sealedTest @ 0x10;
4660
HiddenTest hiddenTest @ 0x20;
4761
ColorTest colorTest @ 0x30;
4862
NoUniqueAddressTest noUniqueAddressTest @ 0x40;
63+
FixedSizeTest1 fixedSizeTest1 @ 0x50;
64+
FixedSizeTest2 fixedSizeTest2 @ 0x60;
4965
5066
std::assert(formatTransformTest == 1337, "Transform attribute not working");
5167
std::assert(sizeof(noUniqueAddressTest) == sizeof(u32), "No Unique Address attribute not working");
68+
std::assert(sizeof(fixedSizeTest1) == 4, "Fixed size attribute not working");
5269
)test";
5370
}
5471

0 commit comments

Comments
 (0)