Skip to content

Commit a51c585

Browse files
feat: quickjs sanitizer
1 parent 4ca22b2 commit a51c585

File tree

6 files changed

+207
-3
lines changed

6 files changed

+207
-3
lines changed

dependencies/quickjs-ng.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ package("quickjs-ng")
5454
table.insert(configs, "-DQJS_CONFIG_MSAN=" .. (package:config("msan") and "ON" or "OFF"))
5555
table.insert(configs, "-DQJS_CONFIG_UBSAN=" .. (package:config("ubsan") and "ON" or "OFF"))
5656
table.insert(configs, "-DQJS_BUILD_LIBC=" .. (package:config("libc") and "ON" or "OFF"))
57-
io.replace("CMakeLists.txt", "add_executable(test_conv\n tests/test_conv.c\n)", "", {plain = true})
57+
5858
if package:config("shared") and package:is_plat("windows") then
5959
table.insert(configs, "-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=ON")
6060
end

scripts/rebuild.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ foreach ($pidx in $pids) {
33
Stop-Process -Id $pidx -Force
44
}
55

6-
6+
xmake f --toolchain=clang-cl -m releasedbg -y
77
xmake b --yes inject
88
xmake b --yes shell
99
if ($LASTEXITCODE -ne 0) {

src/shell/entry.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050

5151
#include "cpptrace/from_current.hpp"
5252

53+
#include "qjs_sanitizer.h"
54+
5355
namespace mb_shell {
5456
window_proc_hook entry::main_window_loop_hook{};
5557
void main() {

src/shell/qjs_sanitizer.cc

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#include "qjs_sanitizer.h"
2+
3+
#include "blook/hook.h"
4+
#include "quickjs.h"
5+
#include <blook/blook.h>
6+
#include <mutex>
7+
#include <string>
8+
#include <unordered_map>
9+
#include <thread>
10+
11+
#define FN(name) {#name, (void *)&name}
12+
static const std::unordered_map<std::string, void *> func_to_hook = {
13+
FN(JS_NewObject),
14+
FN(JS_NewArray),
15+
FN(JS_NewCFunction),
16+
FN(JS_NewCFunctionData),
17+
FN(JS_NewPromiseCapability),
18+
FN(JS_NewString),
19+
FN(JS_NewObjectProto),
20+
FN(JS_NewArrayBuffer),
21+
FN(JS_NewDate),
22+
FN(JS_NewSymbol),
23+
FN(JS_NewArrayBufferCopy),
24+
FN(JS_NewTypedArray),
25+
FN(JS_NewUint8Array),
26+
FN(JS_NewUint8ArrayCopy),
27+
FN(JS_NewCFunction2),
28+
FN(JS_NewCFunction3),
29+
FN(JS_NewCFunctionData2),
30+
FN(JS_NewCModule),
31+
FN(JS_ParseJSON),
32+
FN(JS_JSONStringify),
33+
FN(JS_WriteObject),
34+
FN(JS_ReadObject),
35+
FN(JS_EvalFunction),
36+
FN(JS_LoadModule),
37+
FN(JS_SetConstructor),
38+
FN(JS_SetPropertyFunctionList),
39+
FN(JS_AddModuleExport),
40+
FN(JS_AddModuleExportList),
41+
FN(JS_SetModuleExport),
42+
FN(JS_SetModuleExportList),
43+
FN(JS_FreeContext),
44+
FN(JS_DupContext),
45+
FN(JS_SetContextOpaque),
46+
FN(JS_GetContextOpaque),
47+
FN(JS_GetRuntime),
48+
FN(JS_SetClassProto),
49+
FN(JS_GetClassProto),
50+
FN(JS_GetFunctionProto),
51+
FN(JS_AddIntrinsicBaseObjects),
52+
FN(JS_AddIntrinsicDate),
53+
FN(JS_AddIntrinsicEval),
54+
FN(JS_AddIntrinsicRegExpCompiler),
55+
FN(JS_AddIntrinsicRegExp),
56+
FN(JS_AddIntrinsicJSON),
57+
FN(JS_AddIntrinsicProxy),
58+
FN(JS_AddIntrinsicMapSet),
59+
FN(JS_AddIntrinsicTypedArrays),
60+
FN(JS_AddIntrinsicPromise),
61+
FN(JS_AddIntrinsicBigInt),
62+
FN(JS_AddIntrinsicWeakRef),
63+
FN(JS_AddPerformance),
64+
FN(JS_AddIntrinsicDOMException),
65+
FN(JS_IsEqual),
66+
FN(JS_IsStrictEqual),
67+
FN(JS_IsSameValue),
68+
FN(JS_IsSameValueZero),
69+
FN(JS_Throw),
70+
FN(JS_GetException),
71+
FN(JS_HasException),
72+
FN(JS_SetUncatchableError),
73+
FN(JS_ClearUncatchableError),
74+
FN(JS_ResetUncatchableError),
75+
FN(JS_NewError),
76+
FN(JS_NewInternalError),
77+
FN(JS_NewPlainError),
78+
FN(JS_NewRangeError),
79+
FN(JS_NewReferenceError),
80+
FN(JS_NewSyntaxError),
81+
FN(JS_NewTypeError),
82+
FN(JS_ThrowInternalError),
83+
FN(JS_ThrowPlainError),
84+
FN(JS_ThrowRangeError),
85+
FN(JS_ThrowReferenceError),
86+
FN(JS_ThrowSyntaxError),
87+
FN(JS_ThrowTypeError),
88+
FN(JS_ThrowDOMException),
89+
FN(JS_ThrowOutOfMemory),
90+
FN(JS_FreeValue),
91+
FN(JS_DupValue),
92+
FN(JS_ToBool),
93+
FN(JS_ToNumber),
94+
FN(JS_ToInt32),
95+
FN(JS_ToInt64),
96+
FN(JS_ToIndex),
97+
FN(JS_ToFloat64),
98+
FN(JS_ToBigInt64),
99+
FN(JS_ToBigUint64),
100+
FN(JS_ToInt64Ext),
101+
FN(JS_NewStringLen),
102+
FN(JS_NewTwoByteString),
103+
FN(JS_NewAtomString),
104+
FN(JS_ToString),
105+
FN(JS_ToPropertyKey),
106+
FN(JS_ToCStringLen2),
107+
FN(JS_FreeCString),
108+
FN(JS_NewObjectProtoClass),
109+
FN(JS_NewObjectClass),
110+
FN(JS_NewObjectFrom),
111+
FN(JS_NewObjectFromStr),
112+
FN(JS_ToObject),
113+
FN(JS_ToObjectString),
114+
FN(JS_IsFunction),
115+
FN(JS_IsConstructor),
116+
FN(JS_SetConstructorBit),
117+
FN(JS_NewArrayFrom),
118+
FN(JS_GetProxyTarget),
119+
FN(JS_GetProxyHandler),
120+
FN(JS_GetProperty),
121+
FN(JS_GetPropertyUint32),
122+
FN(JS_GetPropertyInt64),
123+
FN(JS_GetPropertyStr),
124+
FN(JS_SetProperty),
125+
FN(JS_SetPropertyUint32),
126+
FN(JS_SetPropertyInt64),
127+
FN(JS_SetPropertyStr),
128+
FN(JS_HasProperty),
129+
FN(JS_IsExtensible),
130+
FN(JS_PreventExtensions),
131+
FN(JS_DeleteProperty),
132+
FN(JS_SetPrototype),
133+
FN(JS_GetPrototype),
134+
FN(JS_GetLength),
135+
FN(JS_SetLength),
136+
FN(JS_SealObject),
137+
FN(JS_FreezeObject),
138+
FN(JS_GetOwnPropertyNames),
139+
FN(JS_GetOwnProperty),
140+
FN(JS_FreePropertyEnum),
141+
FN(JS_Call),
142+
FN(JS_Invoke),
143+
FN(JS_CallConstructor),
144+
FN(JS_CallConstructor2),
145+
FN(JS_Eval),
146+
FN(JS_Eval2),
147+
FN(JS_EvalThis),
148+
FN(JS_EvalThis2),
149+
FN(JS_GetGlobalObject),
150+
FN(JS_IsInstanceOf),
151+
FN(JS_DefineProperty),
152+
FN(JS_DefinePropertyValue),
153+
FN(JS_DefinePropertyValueUint32),
154+
FN(JS_DefinePropertyValueStr),
155+
FN(JS_DefinePropertyGetSet),
156+
FN(JS_GetOpaque2),
157+
FN(JS_DetachArrayBuffer),
158+
FN(JS_GetArrayBuffer),
159+
FN(JS_GetUint8Array),
160+
FN(JS_GetTypedArrayBuffer),
161+
FN(JS_PromiseState),
162+
FN(JS_PromiseResult),
163+
FN(JS_SetIsHTMLDDA),
164+
FN(JS_GetImportMeta),
165+
FN(JS_GetModuleName),
166+
FN(JS_GetModuleNamespace),
167+
FN(JS_EnqueueJob),
168+
FN(JS_WriteObject2),
169+
FN(JS_ReadObject2),
170+
FN(JS_ResolveModule),
171+
FN(JS_GetScriptOrModuleName),
172+
};
173+
174+
static std::mutex thread_context_map_mutex;
175+
static std::unordered_map<JSContext *, std::thread::id> thread_context_map;
176+
177+
void mb_shell::qjs_sanitizer_enable() {
178+
for (const auto &[name, addr] : func_to_hook) {
179+
blook::VEHHookManager::instance().add_breakpoint(
180+
blook::VEHHookManager::SoftwareBreakpoint {
181+
.address = addr
182+
}, [name](blook::VEHHookManager::VEHHookContext &ctx) {
183+
std::lock_guard lock(thread_context_map_mutex);
184+
auto js_ctx = reinterpret_cast<JSContext *>(ctx.exception_info->ContextRecord->Rcx);
185+
if (thread_context_map.find(js_ctx) == thread_context_map.end()) {
186+
thread_context_map[js_ctx] = std::this_thread::get_id();
187+
} else {
188+
auto existing_thread_id = thread_context_map[js_ctx];
189+
if (existing_thread_id != std::this_thread::get_id()) {
190+
// Detected cross-thread JS context access
191+
std::cerr << "Detected cross-thread access to JSContext in function " << name << std::endl;
192+
}
193+
}
194+
}
195+
);
196+
}
197+
}

src/shell/qjs_sanitizer.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
namespace mb_shell {
4+
void qjs_sanitizer_enable();
5+
} // namespace mb_shell

xmake.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ includes("dependencies/breeze-ui.lua")
1919

2020
set_runtimes("MT")
2121
add_requires("breeze-glfw", {alias = "glfw"})
22-
add_requires("blook d74d6c7e0f13fe787f0dfb461c56c96a4495cf91",
22+
add_requires("blook 3524a931af49be471840e5312fb0c18e888706fd",
2323
"nanovg", "glad", "nanosvg",
2424
"reflect-cpp", "wintoast v1.3.1", "cpptrace v0.8.3", "breeze-ui")
2525

0 commit comments

Comments
 (0)