Line | Count | Source (jump to first uncovered line) |
1 | | #include <iostream> |
2 | | #include <fstream> |
3 | | #include <stdio.h> |
4 | | #include "js.h" |
5 | | extern "C" { |
6 | | #include <quickjs-libc.h> |
7 | | } |
8 | | |
9 | | JS::JS(void) : |
10 | 30 | memoryLimit(0) { |
11 | 30 | } |
12 | | |
13 | 0 | void JS::SetBytecode(const std::vector<char>& bytecode) { |
14 | 0 | SetBytecode(std::vector<uint8_t>( |
15 | 0 | (uint8_t*)bytecode.data(), |
16 | 0 | (uint8_t*)(bytecode.data() + bytecode.size()))); |
17 | 0 | } |
18 | | |
19 | 30 | void JS::SetBytecode(const std::vector<uint8_t>& bytecode) { |
20 | 30 | this->bytecode = bytecode; |
21 | 30 | } |
22 | | |
23 | 0 | void JS::SetMemoryLimit(const size_t limit) { |
24 | 0 | memoryLimit = limit; |
25 | 0 | } |
26 | | |
27 | 0 | std::vector<char> JS::LoadFile(const std::string& fn) { |
28 | 0 | std::vector<char> buffer; |
29 | 0 | std::ifstream file(fn, std::ios::binary | std::ios::ate); |
30 | 0 | std::streamsize size = file.tellg(); |
31 | 0 | if ( size <= 0 ) { |
32 | 0 | throw std::runtime_error("LoadFile: Load error"); |
33 | 0 | } |
34 | 0 | file.seekg(0, std::ios::beg); |
35 | |
|
36 | 0 | buffer.resize(size+1); |
37 | 0 | if (!file.read(buffer.data(), size)) { |
38 | 0 | throw std::runtime_error("LoadFile: Read error"); |
39 | 0 | } |
40 | 0 | buffer[size] = 0x00; |
41 | |
|
42 | 0 | return buffer; |
43 | 0 | } |
44 | | |
45 | 0 | std::vector<uint8_t> JS::CompileJavascript(const std::string& javascriptFilename) { |
46 | 0 | std::vector<char> buffer; |
47 | |
|
48 | 0 | try { |
49 | 0 | buffer = LoadFile(javascriptFilename); |
50 | 0 | } catch ( std::exception ) { |
51 | 0 | std::cout << "Cannot read JavaScript file" << std::endl; |
52 | 0 | exit(1); |
53 | 0 | } |
54 | | |
55 | 0 | { |
56 | 0 | std::vector<uint8_t> ret; |
57 | 0 | JSRuntime* rt = JS_NewRuntime(); |
58 | 0 | JSContext* ctx = JS_NewContext(rt); |
59 | |
|
60 | 0 | JS_AddIntrinsicEval(ctx); |
61 | 0 | JS_AddIntrinsicRegExpCompiler(ctx); |
62 | |
|
63 | 0 | JSValue obj; |
64 | | |
65 | | /* Parse */ |
66 | 0 | { |
67 | 0 | obj = JS_Eval( |
68 | 0 | ctx, |
69 | 0 | buffer.data(), |
70 | 0 | buffer.size() - 1, |
71 | 0 | javascriptFilename.c_str(), |
72 | 0 | JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_GLOBAL | JS_EVAL_TYPE_MODULE); |
73 | 0 | if (JS_IsException(obj)) { |
74 | 0 | goto err_compile; |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | /* To bytecode */ |
79 | 0 | { |
80 | 0 | size_t out_buf_len; |
81 | 0 | uint8_t* out_buf = JS_WriteObject(ctx, &out_buf_len, obj, JS_WRITE_OBJ_BYTECODE); |
82 | 0 | if ( !out_buf ) { |
83 | 0 | goto err_compile; |
84 | 0 | } |
85 | | |
86 | 0 | ret = std::vector<uint8_t>(out_buf, out_buf + out_buf_len); |
87 | |
|
88 | 0 | js_free(ctx, out_buf); |
89 | 0 | } |
90 | | |
91 | 0 | JS_FreeValue(ctx, obj); |
92 | |
|
93 | 0 | JS_FreeContext(ctx); |
94 | 0 | JS_FreeRuntime(rt); |
95 | |
|
96 | 0 | return ret; |
97 | | |
98 | 0 | err_compile: |
99 | 0 | js_std_dump_error(ctx); |
100 | 0 | std::cout << "Cannot compile JavaScript file" << std::endl; |
101 | 0 | exit(1); |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | 35.6k | std::optional<std::string> JS::Run(const std::string& data) { |
106 | 35.6k | return Run(data.data(), data.size(), true); |
107 | 35.6k | } |
108 | | |
109 | 35.6k | std::optional<std::string> JS::Run(const void* data, const size_t size, const bool asString) { |
110 | 35.6k | std::optional<std::string> ret = std::nullopt; |
111 | | |
112 | 35.6k | JSRuntime* rt = nullptr; |
113 | 35.6k | JSContext* ctx = nullptr; |
114 | | |
115 | 35.6k | if ( bytecode.empty() ) { |
116 | 0 | std::cout << "No bytecode defined" << std::endl; |
117 | 0 | exit(1); |
118 | 0 | } |
119 | | |
120 | | /* Instantiate */ |
121 | 35.6k | { |
122 | 35.6k | rt = JS_NewRuntime(); |
123 | 35.6k | ctx = JS_NewContext(rt); |
124 | 35.6k | } |
125 | | |
126 | | /* Configure */ |
127 | 35.6k | { |
128 | 35.6k | if ( memoryLimit ) { |
129 | 0 | /* noret */ JS_SetMemoryLimit(rt, memoryLimit); |
130 | 0 | } |
131 | 35.6k | /* noret */ JS_SetGCThreshold(rt, -1); |
132 | 35.6k | /* noret */ js_std_add_helpers(ctx, 0, nullptr); |
133 | 35.6k | } |
134 | | |
135 | | /* Specify input */ |
136 | 35.6k | { |
137 | 35.6k | const std::string scriptHeader = |
138 | 35.6k | asString == false ? |
139 | 0 | "var FuzzerOutput; var FuzzerInput = new Uint8Array([" : |
140 | 35.6k | "var FuzzerOutput; var FuzzerInput = \""; |
141 | | |
142 | 35.6k | std::string scriptBody; |
143 | 35.6k | const std::string scriptFooter = asString == false ? "]);" : "\";"; |
144 | | |
145 | 480M | for (size_t i = 0; i < size; i++) { |
146 | 480M | if ( asString == false ) { |
147 | 0 | scriptBody += std::to_string(((const uint8_t*)data)[i]); |
148 | 0 | if ( i + 1 != size ) { |
149 | 0 | scriptBody += ","; |
150 | 0 | } |
151 | 480M | } else { |
152 | 480M | char hex[16]; |
153 | 480M | sprintf(hex, "\\x%02X", ((const uint8_t*)data)[i]); |
154 | 480M | scriptBody += hex; |
155 | 480M | } |
156 | 480M | } |
157 | | |
158 | 35.6k | const std::string script = scriptHeader + scriptBody + scriptFooter; |
159 | 35.6k | JSValue val = JS_Eval(ctx, script.data(), script.size(), "<none>", JS_EVAL_TYPE_GLOBAL); |
160 | 35.6k | if (JS_IsException(val)) { |
161 | 0 | js_std_dump_error(ctx); |
162 | 0 | exit(1); |
163 | 0 | } |
164 | 35.6k | JS_FreeValue(ctx, val); |
165 | 35.6k | } |
166 | | |
167 | | /* Run */ |
168 | 0 | { |
169 | 35.6k | /* noret */ js_std_eval_binary(ctx, bytecode.data(), bytecode.size(), 0); |
170 | 35.6k | /* noret */ js_std_loop(ctx); |
171 | | |
172 | | |
173 | | /* Extract output */ |
174 | 35.6k | { |
175 | 35.6k | auto global = JS_GetGlobalObject(ctx); |
176 | 35.6k | auto val = JS_GetPropertyStr(ctx, global, "FuzzerOutput"); |
177 | 35.6k | if (JS_IsException(val)) { |
178 | 0 | js_std_dump_error(ctx); |
179 | 0 | exit(1); |
180 | 0 | } |
181 | 35.6k | if (JS_IsString(val)) { |
182 | 6.93k | int size = JS_JSStringToChar(&val, NULL); |
183 | 6.93k | if ( size > 0 ) { |
184 | 6.93k | char* FuzzerOutput = (char*)malloc(size+1); |
185 | 6.93k | JS_JSStringToChar(&val, FuzzerOutput); |
186 | 6.93k | ret = std::string(FuzzerOutput); |
187 | 6.93k | free(FuzzerOutput); |
188 | 6.93k | } |
189 | 6.93k | } |
190 | 35.6k | JS_FreeValue(ctx, val); |
191 | 35.6k | JS_FreeValue(ctx, global); |
192 | 35.6k | } |
193 | 35.6k | } |
194 | | |
195 | | /* Free */ |
196 | 0 | { |
197 | 35.6k | /* noret */ JS_FreeContext(ctx); |
198 | 35.6k | /* noret */ JS_FreeRuntime(rt); |
199 | 35.6k | } |
200 | | |
201 | 35.6k | return ret; |
202 | 35.6k | } |