Coverage Report

Created: 2024-11-21 07:03

/src/libfuzzer-js/js.cpp
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
}