Skip to content

Commit 1c3f9bd

Browse files
committed
fix(codegen): emit string literals as raw byte data to prevent assembly injection
Updated code generation to emit string literals as explicit byte sequences (char values) instead of interpolating their contents into NASM string literals, eliminating the possibility of user-controlled assembly injection. Although the parser currently does not support escape sequences (e.g. \" or \n), this change establishes a secure foundation and a clear extension point for future safe handling of escaped characters.
1 parent 88e5c91 commit 1c3f9bd

1 file changed

Lines changed: 59 additions & 13 deletions

File tree

src/codegen.cpp

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ struct StrMap {
1414

1515
static std::string op_to_asm(std::string_view op) {
1616
static constexpr std::array<StrMap, 4> table{{
17-
{ "*"sv, "imul"sv },
18-
{ "+"sv, "add"sv },
19-
{ "-"sv, "sub"sv },
20-
{ "/"sv, "idiv"sv },
17+
{"*"sv, "imul"sv},
18+
{"+"sv, "add"sv},
19+
{"-"sv, "sub"sv},
20+
{"/"sv, "idiv"sv},
2121
}};
2222

2323
auto it = std::lower_bound(
@@ -35,12 +35,12 @@ static std::string op_to_asm(std::string_view op) {
3535

3636
static std::string cmp_to_jmp(std::string_view c) {
3737
static constexpr std::array<StrMap, 6> table{{
38-
{ "!="sv, "jne"sv },
39-
{ "<"sv, "jl"sv },
40-
{ "<="sv, "jle"sv },
41-
{ "=="sv, "je"sv },
42-
{ ">"sv, "jg"sv },
43-
{ ">="sv, "jge"sv },
38+
{"!="sv, "jne"sv},
39+
{"<"sv, "jl"sv},
40+
{"<="sv, "jle"sv},
41+
{"=="sv, "je"sv},
42+
{">"sv, "jg"sv},
43+
{">="sv, "jge"sv},
4444
}};
4545

4646
auto it = std::lower_bound(
@@ -90,16 +90,62 @@ void CodeGenerator::gen_variables() {
9090
pr(S);
9191
}
9292

93-
for (auto &kv : ids) {
93+
for (auto &kv: ids) {
9494
pr("\t" + kv.first + " resb 8");
9595
}
9696
}
9797

98+
static inline void append_u8(std::vector<char> &buf, unsigned char v) {
99+
if (v >= 100) {
100+
buf.push_back(static_cast<char>(48 + v / 100));
101+
v %= 100;
102+
buf.push_back(static_cast<char>(48 + v / 10));
103+
buf.push_back(static_cast<char>(48 + v % 10));
104+
} else if (v >= 10) {
105+
buf.push_back(static_cast<char>(48 + v / 10));
106+
buf.push_back(static_cast<char>(48 + v % 10));
107+
} else {
108+
buf.push_back(static_cast<char>(48 + v));
109+
}
110+
}
111+
112+
static constexpr char DB_PREFIX[] = "\tdb ";
113+
static constexpr char COMMA_SPACE[] = ", ";
114+
static constexpr char NL_NUL[] = "10, 0";
115+
116+
98117
void CodeGenerator::gen_start() {
99118
pr("section .data");
119+
120+
std::vector<char> buf;
121+
buf.reserve(128);
122+
100123
for (auto &kv: consts) {
101-
pr("\t" + kv.first + " db \"" + kv.second + "\",10,0");
124+
buf.clear();
125+
126+
// "\t<label> db "
127+
buf.push_back('\t');
128+
buf.insert(buf.end(), kv.first.begin(), kv.first.end());
129+
buf.push_back(' ');
130+
buf.insert(buf.end(), std::begin(DB_PREFIX) + 1,
131+
std::end(DB_PREFIX) - 1); // skip '\t' duplication
132+
133+
// string bytes
134+
for (unsigned char c: kv.second) {
135+
append_u8(buf, c);
136+
buf.insert(buf.end(),
137+
std::begin(COMMA_SPACE),
138+
std::end(COMMA_SPACE) - 1);
139+
}
140+
141+
// newline + NUL
142+
buf.insert(buf.end(),
143+
std::begin(NL_NUL),
144+
std::end(NL_NUL) - 1);
145+
146+
pr(std::string_view(buf.data(), buf.size()));
102147
}
148+
103149
const auto S = R"(section .text
104150
global _start
105151
@@ -200,7 +246,7 @@ void CodeGenerator::gen_code() {
200246
}
201247
}
202248

203-
void CodeGenerator::writeAsm(const std::string& path) {
249+
void CodeGenerator::writeAsm(const std::string &path) {
204250
out.clear();
205251

206252
// MUST reset every time

0 commit comments

Comments
 (0)