From a988ca5e85307c85daf5ea312ad9614be53f54b9 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 19 Sep 2023 11:37:44 +0200 Subject: [PATCH 01/17] Add test for and handling of a real segfault --- .gitignore | 4 +- packages/fuzzer/fuzzing_async.cpp | 58 +++++++--- packages/fuzzer/fuzzing_sync.cpp | 42 +++++-- packages/fuzzer/package.json | 2 +- tests/signal_handlers/SIGSEGV/fuzz.js | 21 ++++ tests/signal_handlers/SIGSEGV/package.json | 3 +- tests/signal_handlers/SIGSEGV/tests.fuzz.js | 9 +- .../native-signal/CMakeLists.txt | 45 ++++++++ tests/signal_handlers/native-signal/index.ts | 27 +++++ .../native-signal/package.json | 28 +++++ ...-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz | Bin 0 -> 2918 bytes .../native-signal/signal_impl.cpp | 25 ++++ .../native-signal/tsconfig.json | 109 ++++++++++++++++++ tests/signal_handlers/package.json | 3 +- tests/signal_handlers/signal_handlers.test.js | 24 ++-- 15 files changed, 361 insertions(+), 39 deletions(-) create mode 100644 tests/signal_handlers/native-signal/CMakeLists.txt create mode 100644 tests/signal_handlers/native-signal/index.ts create mode 100644 tests/signal_handlers/native-signal/package.json create mode 100644 tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz create mode 100644 tests/signal_handlers/native-signal/signal_impl.cpp create mode 100644 tests/signal_handlers/native-signal/tsconfig.json diff --git a/.gitignore b/.gitignore index b3295948..becad648 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,6 @@ node_modules/ *.tgz # Dictionaries generated by Jazzer.js -.JazzerJs-merged-dictionaries \ No newline at end of file +.JazzerJs-merged-dictionaries + +compile_commands.json diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 6b3cbbd2..749d088a 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -14,6 +14,8 @@ #include "napi.h" #include +#include +#include #include #include @@ -66,31 +68,47 @@ using FinalizerDataType = void; TSFN gTSFN; +const std::string SEGFAULT_ERROR_MESSAGE = "Segmentation fault found in fuzz target"; + +volatile std::sig_atomic_t gSignalStatus; +std::jmp_buf errorBuffer; + +void ErrorSignalHandler(int signum) { + gSignalStatus = signum; + std::longjmp(errorBuffer, signum); +} + // The libFuzzer callback when fuzzing asynchronously. int FuzzCallbackAsync(const uint8_t *Data, size_t Size) { std::promise promise; auto input = DataType{Data, Size, &promise}; - auto future = promise.get_future(); - auto status = gTSFN.BlockingCall(&input); - if (status != napi_ok) { - Napi::Error::Fatal( - "FuzzCallbackAsync", - "Napi::TypedThreadSafeNapi::Function.BlockingCall() failed"); + if(setjmp(errorBuffer) == 0) { + auto future = promise.get_future(); + auto status = gTSFN.BlockingCall(&input); + if (status != napi_ok) { + Napi::Error::Fatal( + "FuzzCallbackAsync", + "Napi::TypedThreadSafeNapi::Function.BlockingCall() failed"); + } + // Wait until the JavaScript fuzz target has finished. + try { + future.get(); + } catch (JSException &exception) { + throw; + } catch (std::exception &exception) { + std::cerr << "==" << (unsigned long)GetPID() + << "== Jazzer.js: unexpected Error: " << exception.what() + << std::endl; + libfuzzer::PrintCrashingInput(); + // We call exit to immediately terminates the process without performing any + // cleanup including libfuzzer exit handlers. + _Exit(libfuzzer::ExitErrorCode); + } } - // Wait until the JavaScript fuzz target has finished. - try { - future.get(); - } catch (JSException &exception) { - throw; - } catch (std::exception &exception) { - std::cerr << "==" << (unsigned long)GetPID() - << "== Jazzer.js: unexpected Error: " << exception.what() - << std::endl; - libfuzzer::PrintCrashingInput(); - // We call exit to immediately terminates the process without performing any - // cleanup including libfuzzer exit handlers. - _Exit(libfuzzer::ExitErrorCode); + if (gSignalStatus) { + std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; + exit(139); } return EXIT_SUCCESS; } @@ -258,6 +276,8 @@ Napi::Value StartFuzzingAsync(const Napi::CallbackInfo &info) { "function and an array of libfuzzer arguments"); } + signal(SIGSEGV, ErrorSignalHandler); + auto fuzzer_args = LibFuzzerArgs(info.Env(), info[1].As()); // Store the JS fuzz target and corresponding environment, so that our C++ diff --git a/packages/fuzzer/fuzzing_sync.cpp b/packages/fuzzer/fuzzing_sync.cpp index 69d7a727..20686502 100644 --- a/packages/fuzzer/fuzzing_sync.cpp +++ b/packages/fuzzer/fuzzing_sync.cpp @@ -17,9 +17,13 @@ #include "utils.h" #include #include +#include #include +#include namespace { +const std::string SEGFAULT_ERROR_MESSAGE = "Segmentation fault found in fuzz target"; + // Information about a JS fuzz target. struct FuzzTargetInfo { Napi::Env env; @@ -36,9 +40,22 @@ std::optional gFuzzTarget; // This is only necessary in the sync fuzzing case, as async can be handled // much nicer directly in JavaScript. volatile std::sig_atomic_t gSignalStatus; +std::jmp_buf errorBuffer; } // namespace -void sigintHandler(int signum) { gSignalStatus = signum; } +void sigintHandler(int signum) { + gSignalStatus = signum; +} + +// This handles signals that indicate an unrecoverable error (currently only segfaults). +// Our handling of segfaults is odd because it avoids using our Javascript method to print and instead prints a message +// within C++ and exits almost immediately. This is because Node seems to really not like being called back into +// after `longjmp` jumps outside the scope Node thinks it should be in and so things in JS-land get pretty broken. +// However, catching it here, printing an ok error message, and letting libfuzzer make the crash file is good enough +void error_signal_handler(int signum) { + gSignalStatus = signum; + std::longjmp(errorBuffer, signum); +} // The libFuzzer callback when fuzzing synchronously int FuzzCallbackSync(const uint8_t *Data, size_t Size) { @@ -62,15 +79,24 @@ int FuzzCallbackSync(const uint8_t *Data, size_t Size) { // nice for efficiency if we could use a pointer instead of copying. // auto data = Napi::Buffer::Copy(gFuzzTarget->env, Data, Size); - auto result = gFuzzTarget->target.Call({data}); - - if (result.IsPromise()) { - AsyncReturnsHandler(); - } else { - SyncReturnsHandler(); + if (setjmp(errorBuffer) == 0) { + auto result = gFuzzTarget->target.Call({data}); + if (result.IsPromise()) { + AsyncReturnsHandler(); + } else { + SyncReturnsHandler(); + } } + if (gSignalStatus != 0) { + // if we caught a segfault, print the error message and die, letting libfuzzer print the crash file + // see the comment on `error_signal_handler` for why + if (gSignalStatus == SIGSEGV) { + std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; + exit(139); + } + // Non-zero exit codes will produce crash files. auto exitCode = Napi::Number::New(gFuzzTarget->env, 0); @@ -111,7 +137,7 @@ void StartFuzzing(const Napi::CallbackInfo &info) { info[2].As()}; signal(SIGINT, sigintHandler); - signal(SIGSEGV, sigintHandler); + signal(SIGSEGV, error_signal_handler); StartLibFuzzer(fuzzer_args, FuzzCallbackSync); // Explicitly reset the global function pointer because the JS diff --git a/packages/fuzzer/package.json b/packages/fuzzer/package.json index b4a25c30..2bc1f26c 100644 --- a/packages/fuzzer/package.json +++ b/packages/fuzzer/package.json @@ -18,7 +18,7 @@ "scripts": { "prebuild": "prebuild --runtime napi --backend cmake-js --all --strip --verbose", "install": "prebuild-install --runtime napi || npm run prebuild", - "build": "cmake-js build --out build", + "build": "cmake-js build --out build --debug", "format:fix": "clang-format -i *.cpp shared/*.cpp shared/*.h", "lint": "find . -path ./build -prune -type f -o -iname '*.h' -o -iname '*.cpp' | xargs clang-tidy" }, diff --git a/tests/signal_handlers/SIGSEGV/fuzz.js b/tests/signal_handlers/SIGSEGV/fuzz.js index f2afeec8..b1778f96 100644 --- a/tests/signal_handlers/SIGSEGV/fuzz.js +++ b/tests/signal_handlers/SIGSEGV/fuzz.js @@ -14,6 +14,8 @@ * limitations under the License. */ +const native = require("native-signal"); + let i = 0; module.exports.SIGSEGV_SYNC = (data) => { @@ -36,3 +38,22 @@ module.exports.SIGSEGV_ASYNC = (data) => { } i++; }; + +module.exports.NATIVE_SIGSEGV_SYNC = (data) => { + if (i === 1000) { + console.log("kill with signal"); + native.sigsev(0); + } + if (i > 1000) { + console.log("Signal has not stopped the fuzzing process"); + } + i++; +}; + +module.exports.NATIVE_SIGSEGV_ASYNC = async (data) => { + if (i === 1000) { + console.log("kill with signal"); + native.sigsev(0); + } + i++; +}; diff --git a/tests/signal_handlers/SIGSEGV/package.json b/tests/signal_handlers/SIGSEGV/package.json index 80715365..f5edb57d 100644 --- a/tests/signal_handlers/SIGSEGV/package.json +++ b/tests/signal_handlers/SIGSEGV/package.json @@ -7,7 +7,8 @@ "fuzz": "JAZZER_FUZZ=1 jest" }, "devDependencies": { - "@jazzer.js/jest-runner": "file:../../packages/jest-runner" + "@jazzer.js/jest-runner": "file:../../packages/jest-runner", + "native-signal": "file:../native-signal" }, "jest": { "projects": [ diff --git a/tests/signal_handlers/SIGSEGV/tests.fuzz.js b/tests/signal_handlers/SIGSEGV/tests.fuzz.js index 8525e712..8072c3bc 100644 --- a/tests/signal_handlers/SIGSEGV/tests.fuzz.js +++ b/tests/signal_handlers/SIGSEGV/tests.fuzz.js @@ -14,9 +14,16 @@ * limitations under the License. */ -const { SIGSEGV_ASYNC, SIGSEGV_SYNC } = require("./fuzz.js"); +const { + SIGSEGV_ASYNC, + SIGSEGV_SYNC, + NATIVE_SIGSEGV_SYNC, + NATIVE_SIGSEGV_ASYNC, +} = require("./fuzz.js"); describe("Jest", () => { it.fuzz("Sync", SIGSEGV_SYNC); it.fuzz("Async", SIGSEGV_ASYNC); + it.fuzz("Native", NATIVE_SIGSEGV_SYNC); + it.fuzz("Native Async", NATIVE_SIGSEGV_ASYNC); }); diff --git a/tests/signal_handlers/native-signal/CMakeLists.txt b/tests/signal_handlers/native-signal/CMakeLists.txt new file mode 100644 index 00000000..da11b2c8 --- /dev/null +++ b/tests/signal_handlers/native-signal/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.15) +cmake_policy(SET CMP0091 NEW) +cmake_policy(SET CMP0042 NEW) + +project(signal_impl) + +set(CMAKE_CXX_STANDARD 17) # mostly supported since GCC 7 +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(LLVM_ENABLE_LLD TRUE) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + cmake_policy(SET CMP0135 NEW) +endif() + +# To help with development, let's write compile_commands.json unconditionally. +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +include_directories(${CMAKE_JS_INC}) + +file(GLOB SOURCE_FILES "*.cpp") + +add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC}) +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") +target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB}) + +if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) + # Generate node.lib + execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) +endif() + +# Enable the functionality of Node-API version 4 and disable everything added +# later, so that we don't accidentally break compatibility with older versions +# of Node (see https://nodejs.org/api/n-api.html#node-api-version-matrix). +# +# Note that prebuild recommends in its README to use ${napi_build_version} here, +# but the variable is only set when cmake-js is invoked via prebuild (in which +# case the API version is taken from "binary.napi_versions" in package.json). +# Since we want the build to work in other cases as well, let's just use a +# constant. (There is currently no point in a dynamic setting anyway since we +# specify the oldest version that we're compatible with, and Node-API's ABI +# stability guarantees that this version is available in all future Node-API +# releases.) +add_definitions(-DNAPI_VERSION=4) diff --git a/tests/signal_handlers/native-signal/index.ts b/tests/signal_handlers/native-signal/index.ts new file mode 100644 index 00000000..d002d25a --- /dev/null +++ b/tests/signal_handlers/native-signal/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Code Intelligence GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { default as bind } from "bindings"; + +type NativeAddon = { + sigsegv: (loc: number) => void; +}; + +const addon: NativeAddon = bind("signal_impl"); + +export function sigsev(loc: number) { + addon.sigsegv(loc); +} diff --git a/tests/signal_handlers/native-signal/package.json b/tests/signal_handlers/native-signal/package.json new file mode 100644 index 00000000..7e0e7361 --- /dev/null +++ b/tests/signal_handlers/native-signal/package.json @@ -0,0 +1,28 @@ +{ + "name": "native-signal", + "version": "1.0.0", + "description": "", + "main": "dist/index.js", + "scripts": { + "prebuild": "prebuild --runtime napi --backend cmake-js --all --strip --verbose", + "install": "prebuild-install --runtime napi || npm run prebuild", + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "typescript": "^5.2.2", + "prebuild": "^12.0.0" + }, + "binary": { + "napi_versions": [ + 4 + ] + }, + "dependencies": { + "bindings": "^1.5.0", + "cmake-js": "^7.2.1", + "prebuild-install": "^7.1.1" + } +} diff --git a/tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz b/tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..929bac682a2161ea5535f32dd57161a1a1778f27 GIT binary patch literal 2918 zcmb8v_dgU41Hf^6l$8{bPFaCXPgnv+3w5X z60#|man8A%%rlZhjMr773G>4CP@|NT?=_>% z{7*lCyjcw-BU8B_!SpkYWj{&jJSRjTR52J|9bJu}ut=qlakaw|#Bw_lzK1KcP<<6q zQs+PT@k`%Rt66(u$gvyT@oQTT;P>6%?at^q+TPyhf>HeR2RRMt`>=ze#qh#0AHr~N zE9Nx5J-BwP1WIUh(Kr?K;;2RORngSA_#4wl6gcZk)b^8Eb(~KmWDRYCr5F|g`KT5` zU)DXQfBY-aH4lv$48k>Hat$$dIhK`%T2znJkT20D6(o$@7O_CAT-9Bz?vX{JbZUs7 znZ*IwH$qb>!}G>f6O?7RuB3qUGCLR`k})SZUOz<1$(Cy4w{V}fgry$ttX8LP`tCB3 zCGa~9!n6~pln`}Z(oC)28OMhkg#L#AKGABL>6Yh{c6xgV|2`);l0kV_Y*&<3K5{3N zRFi!z8*+eS0j)myf#BVcD@`9(io`H2MZOC>_wAMxp>2tzRX~%3XvqVFXE(?CH?sOg z$TiH`YSjr+sWVmR{ZL8Qd?gQuTe({g)Auk-L&W@`*1*hTh3VBgn^ymJw41H3oCY`X zSMbJEwZN-n#k68wSyDon!QD*|6xS`bPW&6DDUY8{Z4z-e@l-~Ro-rU^@OSe51xe9- z6v#LVp_9c9dSe;`N1T0)nGTTwF)sn3oMJM@XE)D_j^}VR-bK*Sa_eN@r+~KlLpMlq zP@wN)JMV%jXn@S3{8G=M3)dSz?dC4iKYmQ);%Pn}_hZa--4{6KWlThjr+7_4RLnS- z79La-H8|8zGbML--BB4uBv62S*)urWW=bo87`$+M6wDGU#_``BeN*&jNav~JpHEax zrt}}`K1;9+>o?=DqzmftPg3LmZogK#-^?QF@q#O#+799C!E{lDy)s$6+_m)>E%r+z zc+QlOq3LCJZi9IGvQ*g{0MNo%tk$>DbRx(fXPdM&8uiNjCB3JnJb1LC(mZQLbLu5+B5=!TfqZaRlG4|%Xr!}}c}PQd^2#AM+z36b zklF8D%m)SL;%oU?p(7ElBL}I7Qmn9Dh@@N3=&gH3BAyCEU5pXB(O?|y%hkOH_M(V| zmrkrMH0~&8;sEAwcb5l#!j?`*Cd&5i%EN%>=BWYVUIFhI-fH#)yy7_<%u72%ep@)& zX7(Z@k6GsFji(WA5@}=8Z%#vb%N;^8)jZBKhktLEzK;%oZ5{ocr9O*+w>(b3h+$l> zOCg^xT>M5Yn1Qo60LIr!L5S8R#etGtta5GK7?^ETJv8W!Mf&qt-dyP)i^#>((IhYJ zV{@wz_V8B-5w`A~vss3d-` zD1CQQ9h*UU_I*R}4~!(MnYk%6!dF^CnW#lBcR+pRy+s>a+_3g84}khlHAsD3J?m}k zbd)E8k$gaWyeweubgZHaYeT#1ghO=@ts^oEI&d9gs_>NW40tRW- z@x7V{+t_ycd9o#G2oZ>eEf|Iz-L@&$QfP;y?A$QKtl-h5BMvNb9?@k#jpzW+Q1r=%z{X1h zTt-wAje5WY2@x2EHs;;>^RU=&l4Pjf1+)|df22;3#?-Z{C-H$DMjxd~f6fIQ!;7(r zWRs6c^IEe}WNh%~NOiA$cLtLFs~nInolWUXd@9%fOnFPifogEZh(hXhy&+%dh*C|A zJUyG|okE6}V)MxLW!MCAefg~)CoWTfUnjSx zVm1Q!_8-*&Zd|Eqq&kUy_h8(SO~#5xT1fnIw+nvLXNT7>H-$!+3ItysuExsbwWjE0 z{9xSVH!}F^mFRkTV*4*K0uU>TI z`}!5Gw{eA0GADI5{AsY5zf$YhBsEihj@?P(H5AiIPwURqX~^3=WYXnh`9GZ2e;vHw z?MlW=$Zz=P54-c^^2l4iupO*S#Eaz?3gV)ct(;ay+(mLb+rwk<;%~fR|HGU9^RJ`} zKoudeWw6#v-R;<3M#cisYO*_1-O|K=iB(TB*i(GbigyeXR3^I+uo%NOtyXn^=CdEy zef;9MZ+&8$g}lV7S81`+O_#@B&{y?8C|HGR$<ZX@sXWqp>8|R0 zsWv(^p&vc5k=gH~-{L^oM+k7G5(~w07q%vWcnG8317;B{X(hUB&0liS^rQ3XTQzk! zzGCm~#^o=L+LaBr>;54-_&o{Asjq7op0JHE{U)@*1Q(OF?(BQ&lveI)P~`o`5a7ts zsbESBGT3u0pvcBRIzrnN2F^FHa@5`)v}_ygYGRUEYC*lNOK@;fNqx2&D9^PT z=3T4=L~JYuVG~}!lvNizsU?C|r#@apP$wGWS~eOh;NZ^(Q*vcV)Mi4`Z0tW_VSx#P z<=3n%rnIJ=EQ5dYcQBtf2&yLE1WnDGI_ozhY!`Rh{gA7d)TpkvEDH?c?W*sWsn@Sm z-nYYv)#fZ2$N&PH>(2SF-S1z&jIegjk8yvL z(+=)IyKaV{cE``&w^8D$bII^oPH=4QE0Pl}2$3&HS#xM4I_+QW+9a;@<%VB4ZLW)k zYz#$x#$Ci*K@j!?=>vj6MKWQFOE)h-w*o|P7is*RMYyx)ob8%rh3F>#y=}F2wYh{9 zPtRp^;HqIE`vx`It**a9+L&!(fakVGf|INzMl@ro>``sC=$uIm#G+p*ku!WF;cS;NF`Qb$%6 z)d&mh`ciY>#cdB=NX1@IR{Ru}f+rQ^zb|)1mK1=Hw8er>6f$bn8c0I83<9H$>gnBQ zr{#_sA5Z{6NhiNkJS^&HQ+kqCFfJ*N*iN>8)+!kcZmDWMd#|+NJGUfP1ubNk1^?Nb z0S2`ywk&17q-+7Z0tq@By&bQ!fL8X|3=IDQ DENjO) literal 0 HcmV?d00001 diff --git a/tests/signal_handlers/native-signal/signal_impl.cpp b/tests/signal_handlers/native-signal/signal_impl.cpp new file mode 100644 index 00000000..0b23b046 --- /dev/null +++ b/tests/signal_handlers/native-signal/signal_impl.cpp @@ -0,0 +1,25 @@ +#include +#include + +#include + +void sigsegv(const Napi::CallbackInfo &info) +{ + if (info.Length() != 1 || !info[0].IsNumber()) + { + throw Napi::Error::New(info.Env(), "Need a single integer argument"); + } + // accepts a parameter to prevent the compiler from optimizing a static segfault away + int location = info[0].ToNumber(); + int *a = (int *)location; + *a = 10; +} + +Napi::Object Init(Napi::Env env, Napi::Object exports) +{ + exports["sigsegv"] = Napi::Function::New(env); + + return exports; +} + +NODE_API_MODULE(signal_impl, Init); diff --git a/tests/signal_handlers/native-signal/tsconfig.json b/tests/signal_handlers/native-signal/tsconfig.json new file mode 100644 index 00000000..9e7faec6 --- /dev/null +++ b/tests/signal_handlers/native-signal/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/tests/signal_handlers/package.json b/tests/signal_handlers/package.json index ac9507a5..543a4f15 100644 --- a/tests/signal_handlers/package.json +++ b/tests/signal_handlers/package.json @@ -7,6 +7,7 @@ "test": "jest --verbose" }, "devDependencies": { - "@jazzer.js/core": "file:../../packages/core" + "@jazzer.js/core": "file:../../packages/core", + "native-signal": "file:native-signal" } } diff --git a/tests/signal_handlers/signal_handlers.test.js b/tests/signal_handlers/signal_handlers.test.js index b56eef56..8c8c663d 100644 --- a/tests/signal_handlers/signal_handlers.test.js +++ b/tests/signal_handlers/signal_handlers.test.js @@ -76,7 +76,7 @@ describe("SIGINT handlers", () => { describe("SIGSEGV handlers", () => { let fuzzTestBuilder; - const errorMessage = "= Segmentation Fault"; + const errorMessage = "Segmentation fault found in fuzz target"; beforeEach(() => { fuzzTestBuilder = new FuzzTestBuilder() @@ -104,6 +104,22 @@ describe("SIGSEGV handlers", () => { assertSignalMessagesLogged(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); + it("stop fuzzing on native SIGSEGV", () => { + const fuzzTest = fuzzTestBuilder + .sync(true) + .fuzzEntryPoint("NATIVE_SIGSEGV_SYNC") + .build(); + expect(() => fuzzTest.execute()).toThrowError(); + assertErrorAndCrashFileLogged(fuzzTest, errorMessage); + }); + it("stop fuzzing on native async SIGSEGV", () => { + const fuzzTest = fuzzTestBuilder + .sync(false) + .fuzzEntryPoint("NATIVE_SIGSEGV_ASYNC") + .build(); + expect(() => fuzzTest.execute()).toThrowError(); + assertErrorAndCrashFileLogged(fuzzTest, errorMessage); + }); }); describe("in Jest fuzzing mode", () => { @@ -133,12 +149,6 @@ describe("SIGSEGV handlers", () => { function assertSignalMessagesLogged(fuzzTest) { expect(fuzzTest.stderr).toContain("kill with signal"); - // We asked for a coverage report. Here we only look for the universal part of its header. - // Jest prints to stdout. - expect(fuzzTest.stdout).toContain( - "| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s", - ); - // Count how many times "Signal has not stopped the fuzzing process" has been printed. const matches = fuzzTest.stderr.match( /Signal has not stopped the fuzzing process/g, From 7fcc9fb6099ffde0331f0bd65e3d4458b3d5024d Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 19 Sep 2023 12:03:00 +0200 Subject: [PATCH 02/17] Fix misspelled function --- tests/signal_handlers/SIGSEGV/fuzz.js | 4 ++-- tests/signal_handlers/native-signal/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/signal_handlers/SIGSEGV/fuzz.js b/tests/signal_handlers/SIGSEGV/fuzz.js index b1778f96..aea2162b 100644 --- a/tests/signal_handlers/SIGSEGV/fuzz.js +++ b/tests/signal_handlers/SIGSEGV/fuzz.js @@ -42,7 +42,7 @@ module.exports.SIGSEGV_ASYNC = (data) => { module.exports.NATIVE_SIGSEGV_SYNC = (data) => { if (i === 1000) { console.log("kill with signal"); - native.sigsev(0); + native.sigsegv(0); } if (i > 1000) { console.log("Signal has not stopped the fuzzing process"); @@ -53,7 +53,7 @@ module.exports.NATIVE_SIGSEGV_SYNC = (data) => { module.exports.NATIVE_SIGSEGV_ASYNC = async (data) => { if (i === 1000) { console.log("kill with signal"); - native.sigsev(0); + native.sigsegv(0); } i++; }; diff --git a/tests/signal_handlers/native-signal/index.ts b/tests/signal_handlers/native-signal/index.ts index d002d25a..36134352 100644 --- a/tests/signal_handlers/native-signal/index.ts +++ b/tests/signal_handlers/native-signal/index.ts @@ -22,6 +22,6 @@ type NativeAddon = { const addon: NativeAddon = bind("signal_impl"); -export function sigsev(loc: number) { +export function sigsegv(loc: number) { addon.sigsegv(loc); } From e163c5859383df42e1eb6c708d488d6255a88901 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 19 Sep 2023 14:20:56 +0200 Subject: [PATCH 03/17] Clean up package files --- packages/fuzzer/package.json | 2 +- tests/signal_handlers/native-signal/package.json | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/fuzzer/package.json b/packages/fuzzer/package.json index 2bc1f26c..b4a25c30 100644 --- a/packages/fuzzer/package.json +++ b/packages/fuzzer/package.json @@ -18,7 +18,7 @@ "scripts": { "prebuild": "prebuild --runtime napi --backend cmake-js --all --strip --verbose", "install": "prebuild-install --runtime napi || npm run prebuild", - "build": "cmake-js build --out build --debug", + "build": "cmake-js build --out build", "format:fix": "clang-format -i *.cpp shared/*.cpp shared/*.h", "lint": "find . -path ./build -prune -type f -o -iname '*.h' -o -iname '*.cpp' | xargs clang-tidy" }, diff --git a/tests/signal_handlers/native-signal/package.json b/tests/signal_handlers/native-signal/package.json index 7e0e7361..ac2449bd 100644 --- a/tests/signal_handlers/native-signal/package.json +++ b/tests/signal_handlers/native-signal/package.json @@ -4,16 +4,12 @@ "description": "", "main": "dist/index.js", "scripts": { - "prebuild": "prebuild --runtime napi --backend cmake-js --all --strip --verbose", - "install": "prebuild-install --runtime napi || npm run prebuild", - "build": "tsc", - "test": "echo \"Error: no test specified\" && exit 1" + "build": "cmake-js build && tsc" }, "author": "", "license": "ISC", "devDependencies": { - "typescript": "^5.2.2", - "prebuild": "^12.0.0" + "typescript": "^5.2.2" }, "binary": { "napi_versions": [ @@ -22,7 +18,6 @@ }, "dependencies": { "bindings": "^1.5.0", - "cmake-js": "^7.2.1", - "prebuild-install": "^7.1.1" + "cmake-js": "^7.2.1" } } From 4f07701bc2d812a8c069b37225c04227ff08d6ad Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 19 Sep 2023 15:40:26 +0200 Subject: [PATCH 04/17] Fix clean build not building native-signal --- tests/signal_handlers/native-signal/package.json | 1 + ...ive-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz | Bin 2918 -> 0 bytes tests/signal_handlers/package.json | 1 + 3 files changed, 2 insertions(+) delete mode 100644 tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz diff --git a/tests/signal_handlers/native-signal/package.json b/tests/signal_handlers/native-signal/package.json index ac2449bd..8b36dca4 100644 --- a/tests/signal_handlers/native-signal/package.json +++ b/tests/signal_handlers/native-signal/package.json @@ -4,6 +4,7 @@ "description": "", "main": "dist/index.js", "scripts": { + "install": "npm run build", "build": "cmake-js build && tsc" }, "author": "", diff --git a/tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz b/tests/signal_handlers/native-signal/prebuilds/native-signal-v1.0.0-napi-v4-darwin-arm64.tar.gz deleted file mode 100644 index 929bac682a2161ea5535f32dd57161a1a1778f27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2918 zcmb8v_dgU41Hf^6l$8{bPFaCXPgnv+3w5X z60#|man8A%%rlZhjMr773G>4CP@|NT?=_>% z{7*lCyjcw-BU8B_!SpkYWj{&jJSRjTR52J|9bJu}ut=qlakaw|#Bw_lzK1KcP<<6q zQs+PT@k`%Rt66(u$gvyT@oQTT;P>6%?at^q+TPyhf>HeR2RRMt`>=ze#qh#0AHr~N zE9Nx5J-BwP1WIUh(Kr?K;;2RORngSA_#4wl6gcZk)b^8Eb(~KmWDRYCr5F|g`KT5` zU)DXQfBY-aH4lv$48k>Hat$$dIhK`%T2znJkT20D6(o$@7O_CAT-9Bz?vX{JbZUs7 znZ*IwH$qb>!}G>f6O?7RuB3qUGCLR`k})SZUOz<1$(Cy4w{V}fgry$ttX8LP`tCB3 zCGa~9!n6~pln`}Z(oC)28OMhkg#L#AKGABL>6Yh{c6xgV|2`);l0kV_Y*&<3K5{3N zRFi!z8*+eS0j)myf#BVcD@`9(io`H2MZOC>_wAMxp>2tzRX~%3XvqVFXE(?CH?sOg z$TiH`YSjr+sWVmR{ZL8Qd?gQuTe({g)Auk-L&W@`*1*hTh3VBgn^ymJw41H3oCY`X zSMbJEwZN-n#k68wSyDon!QD*|6xS`bPW&6DDUY8{Z4z-e@l-~Ro-rU^@OSe51xe9- z6v#LVp_9c9dSe;`N1T0)nGTTwF)sn3oMJM@XE)D_j^}VR-bK*Sa_eN@r+~KlLpMlq zP@wN)JMV%jXn@S3{8G=M3)dSz?dC4iKYmQ);%Pn}_hZa--4{6KWlThjr+7_4RLnS- z79La-H8|8zGbML--BB4uBv62S*)urWW=bo87`$+M6wDGU#_``BeN*&jNav~JpHEax zrt}}`K1;9+>o?=DqzmftPg3LmZogK#-^?QF@q#O#+799C!E{lDy)s$6+_m)>E%r+z zc+QlOq3LCJZi9IGvQ*g{0MNo%tk$>DbRx(fXPdM&8uiNjCB3JnJb1LC(mZQLbLu5+B5=!TfqZaRlG4|%Xr!}}c}PQd^2#AM+z36b zklF8D%m)SL;%oU?p(7ElBL}I7Qmn9Dh@@N3=&gH3BAyCEU5pXB(O?|y%hkOH_M(V| zmrkrMH0~&8;sEAwcb5l#!j?`*Cd&5i%EN%>=BWYVUIFhI-fH#)yy7_<%u72%ep@)& zX7(Z@k6GsFji(WA5@}=8Z%#vb%N;^8)jZBKhktLEzK;%oZ5{ocr9O*+w>(b3h+$l> zOCg^xT>M5Yn1Qo60LIr!L5S8R#etGtta5GK7?^ETJv8W!Mf&qt-dyP)i^#>((IhYJ zV{@wz_V8B-5w`A~vss3d-` zD1CQQ9h*UU_I*R}4~!(MnYk%6!dF^CnW#lBcR+pRy+s>a+_3g84}khlHAsD3J?m}k zbd)E8k$gaWyeweubgZHaYeT#1ghO=@ts^oEI&d9gs_>NW40tRW- z@x7V{+t_ycd9o#G2oZ>eEf|Iz-L@&$QfP;y?A$QKtl-h5BMvNb9?@k#jpzW+Q1r=%z{X1h zTt-wAje5WY2@x2EHs;;>^RU=&l4Pjf1+)|df22;3#?-Z{C-H$DMjxd~f6fIQ!;7(r zWRs6c^IEe}WNh%~NOiA$cLtLFs~nInolWUXd@9%fOnFPifogEZh(hXhy&+%dh*C|A zJUyG|okE6}V)MxLW!MCAefg~)CoWTfUnjSx zVm1Q!_8-*&Zd|Eqq&kUy_h8(SO~#5xT1fnIw+nvLXNT7>H-$!+3ItysuExsbwWjE0 z{9xSVH!}F^mFRkTV*4*K0uU>TI z`}!5Gw{eA0GADI5{AsY5zf$YhBsEihj@?P(H5AiIPwURqX~^3=WYXnh`9GZ2e;vHw z?MlW=$Zz=P54-c^^2l4iupO*S#Eaz?3gV)ct(;ay+(mLb+rwk<;%~fR|HGU9^RJ`} zKoudeWw6#v-R;<3M#cisYO*_1-O|K=iB(TB*i(GbigyeXR3^I+uo%NOtyXn^=CdEy zef;9MZ+&8$g}lV7S81`+O_#@B&{y?8C|HGR$<ZX@sXWqp>8|R0 zsWv(^p&vc5k=gH~-{L^oM+k7G5(~w07q%vWcnG8317;B{X(hUB&0liS^rQ3XTQzk! zzGCm~#^o=L+LaBr>;54-_&o{Asjq7op0JHE{U)@*1Q(OF?(BQ&lveI)P~`o`5a7ts zsbESBGT3u0pvcBRIzrnN2F^FHa@5`)v}_ygYGRUEYC*lNOK@;fNqx2&D9^PT z=3T4=L~JYuVG~}!lvNizsU?C|r#@apP$wGWS~eOh;NZ^(Q*vcV)Mi4`Z0tW_VSx#P z<=3n%rnIJ=EQ5dYcQBtf2&yLE1WnDGI_ozhY!`Rh{gA7d)TpkvEDH?c?W*sWsn@Sm z-nYYv)#fZ2$N&PH>(2SF-S1z&jIegjk8yvL z(+=)IyKaV{cE``&w^8D$bII^oPH=4QE0Pl}2$3&HS#xM4I_+QW+9a;@<%VB4ZLW)k zYz#$x#$Ci*K@j!?=>vj6MKWQFOE)h-w*o|P7is*RMYyx)ob8%rh3F>#y=}F2wYh{9 zPtRp^;HqIE`vx`It**a9+L&!(fakVGf|INzMl@ro>``sC=$uIm#G+p*ku!WF;cS;NF`Qb$%6 z)d&mh`ciY>#cdB=NX1@IR{Ru}f+rQ^zb|)1mK1=Hw8er>6f$bn8c0I83<9H$>gnBQ zr{#_sA5Z{6NhiNkJS^&HQ+kqCFfJ*N*iN>8)+!kcZmDWMd#|+NJGUfP1ubNk1^?Nb z0S2`ywk&17q-+7Z0tq@By&bQ!fL8X|3=IDQ DENjO) diff --git a/tests/signal_handlers/package.json b/tests/signal_handlers/package.json index 543a4f15..175723bd 100644 --- a/tests/signal_handlers/package.json +++ b/tests/signal_handlers/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "description": "Tests for Jazzer.js' signal handlers", "scripts": { + "install": "cd native-signal && npm install", "fuzz": "jest --verbose", "test": "jest --verbose" }, From 467f82e656c9a554239abd7c7954483397c02b74 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 19 Sep 2023 16:47:47 +0200 Subject: [PATCH 05/17] Hopefully fix the build in CI --- tests/signal_handlers/native-signal/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/signal_handlers/native-signal/tsconfig.json b/tests/signal_handlers/native-signal/tsconfig.json index 9e7faec6..25d16bc9 100644 --- a/tests/signal_handlers/native-signal/tsconfig.json +++ b/tests/signal_handlers/native-signal/tsconfig.json @@ -105,5 +105,6 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } + }, + "exclude": ["build", "dist"] } From 69a8f0f266d4ef729e14ef74fb335bb5019e1896 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 20 Sep 2023 14:43:36 +0200 Subject: [PATCH 06/17] Try moving signal handler assignment to the same thread --- packages/fuzzer/fuzzing_async.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 749d088a..73c3978b 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -106,7 +106,7 @@ int FuzzCallbackAsync(const uint8_t *Data, size_t Size) { _Exit(libfuzzer::ExitErrorCode); } } - if (gSignalStatus) { + if (gSignalStatus > 0) { std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; exit(139); } @@ -276,8 +276,6 @@ Napi::Value StartFuzzingAsync(const Napi::CallbackInfo &info) { "function and an array of libfuzzer arguments"); } - signal(SIGSEGV, ErrorSignalHandler); - auto fuzzer_args = LibFuzzerArgs(info.Env(), info[1].As()); // Store the JS fuzz target and corresponding environment, so that our C++ @@ -308,6 +306,7 @@ Napi::Value StartFuzzingAsync(const Napi::CallbackInfo &info) { context->native_thread = std::thread( [](std::vector fuzzer_args, AsyncFuzzTargetContext *ctx) { try { + signal(SIGSEGV, ErrorSignalHandler); StartLibFuzzer(fuzzer_args, FuzzCallbackAsync); } catch (const JSException &exception) { } From a2ebfcd3848773b52ada9f6801938f58c41456bb Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 20 Sep 2023 16:41:47 +0200 Subject: [PATCH 07/17] Time to do some CI print debugging --- packages/fuzzer/fuzzing_async.cpp | 2 ++ tests/signal_handlers/signal_handlers.test.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 73c3978b..093aa8e2 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -74,6 +74,7 @@ volatile std::sig_atomic_t gSignalStatus; std::jmp_buf errorBuffer; void ErrorSignalHandler(int signum) { + std::cerr << "segfault detected" << std::endl; gSignalStatus = signum; std::longjmp(errorBuffer, signum); } @@ -306,6 +307,7 @@ Napi::Value StartFuzzingAsync(const Napi::CallbackInfo &info) { context->native_thread = std::thread( [](std::vector fuzzer_args, AsyncFuzzTargetContext *ctx) { try { + std::cerr << "assigning segfault handler" << std::endl; signal(SIGSEGV, ErrorSignalHandler); StartLibFuzzer(fuzzer_args, FuzzCallbackAsync); } catch (const JSException &exception) { diff --git a/tests/signal_handlers/signal_handlers.test.js b/tests/signal_handlers/signal_handlers.test.js index 8c8c663d..58743779 100644 --- a/tests/signal_handlers/signal_handlers.test.js +++ b/tests/signal_handlers/signal_handlers.test.js @@ -98,6 +98,7 @@ describe("SIGSEGV handlers", () => { it("stop async fuzzing on SIGSEGV", () => { const fuzzTest = fuzzTestBuilder .sync(false) + .verbose(true) .fuzzEntryPoint("SIGSEGV_ASYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); @@ -115,6 +116,7 @@ describe("SIGSEGV handlers", () => { it("stop fuzzing on native async SIGSEGV", () => { const fuzzTest = fuzzTestBuilder .sync(false) + .verbose(true) .fuzzEntryPoint("NATIVE_SIGSEGV_ASYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); From 5d965aa27dfaec088570114c4f3d26119313ce4b Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 20 Sep 2023 16:53:03 +0200 Subject: [PATCH 08/17] Add log line to setjmp --- packages/fuzzer/fuzzing_async.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 093aa8e2..c90a22a5 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -84,6 +84,7 @@ int FuzzCallbackAsync(const uint8_t *Data, size_t Size) { std::promise promise; auto input = DataType{Data, Size, &promise}; + std::cerr << "reached setjmp" << std::endl; if(setjmp(errorBuffer) == 0) { auto future = promise.get_future(); auto status = gTSFN.BlockingCall(&input); From 00f800dee3fbb679a3aad58f127686c00c32d91d Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Thu, 21 Sep 2023 16:47:17 +0200 Subject: [PATCH 09/17] Try add setjmp to CallJsFuzzCallback --- packages/fuzzer/CMakeLists.txt | 3 +- packages/fuzzer/fuzzing_async.cpp | 49 +++++++++++++++---------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/packages/fuzzer/CMakeLists.txt b/packages/fuzzer/CMakeLists.txt index e791c7ff..ee87fb53 100644 --- a/packages/fuzzer/CMakeLists.txt +++ b/packages/fuzzer/CMakeLists.txt @@ -154,8 +154,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") ${BINARY_DIR}/${LIBFUZZER_STATIC_LIB_PATH} -Wl,-no-whole-archive) elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_link_libraries( - ${PROJECT_NAME} -Wl,-all_load ${BINARY_DIR}/${LIBFUZZER_STATIC_LIB_PATH} - -Wl,-noall_load) + ${PROJECT_NAME} -Wl,-all_load ${BINARY_DIR}/${LIBFUZZER_STATIC_LIB_PATH}) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") # Force MSVC to do an MT build, suggested by cmake-js cmake_policy(SET CMP0091 NEW) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index c90a22a5..2da6ed0c 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -84,33 +84,26 @@ int FuzzCallbackAsync(const uint8_t *Data, size_t Size) { std::promise promise; auto input = DataType{Data, Size, &promise}; - std::cerr << "reached setjmp" << std::endl; - if(setjmp(errorBuffer) == 0) { - auto future = promise.get_future(); - auto status = gTSFN.BlockingCall(&input); - if (status != napi_ok) { - Napi::Error::Fatal( - "FuzzCallbackAsync", - "Napi::TypedThreadSafeNapi::Function.BlockingCall() failed"); - } - // Wait until the JavaScript fuzz target has finished. - try { - future.get(); - } catch (JSException &exception) { - throw; - } catch (std::exception &exception) { - std::cerr << "==" << (unsigned long)GetPID() - << "== Jazzer.js: unexpected Error: " << exception.what() - << std::endl; - libfuzzer::PrintCrashingInput(); - // We call exit to immediately terminates the process without performing any - // cleanup including libfuzzer exit handlers. - _Exit(libfuzzer::ExitErrorCode); - } + auto future = promise.get_future(); + auto status = gTSFN.BlockingCall(&input); + if (status != napi_ok) { + Napi::Error::Fatal( + "FuzzCallbackAsync", + "Napi::TypedThreadSafeNapi::Function.BlockingCall() failed"); } - if (gSignalStatus > 0) { - std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; - exit(139); + // Wait until the JavaScript fuzz target has finished. + try { + future.get(); + } catch (JSException &exception) { + throw; + } catch (std::exception &exception) { + std::cerr << "==" << (unsigned long)GetPID() + << "== Jazzer.js: unexpected Error: " << exception.what() + << std::endl; + libfuzzer::PrintCrashingInput(); + // We call exit to immediately terminates the process without performing any + // cleanup including libfuzzer exit handlers. + _Exit(libfuzzer::ExitErrorCode); } return EXIT_SUCCESS; } @@ -127,6 +120,10 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function jsFuzzCallback, // thread and continue with the next invocation. try { + if (setjmp(errorBuffer) != 0) { + std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; + exit(139); + } if (env != nullptr) { auto buffer = Napi::Buffer::Copy(env, data->data, data->size); From fd8a5ee6bc281edffa42f777adb5e91ff401c4fb Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Fri, 22 Sep 2023 10:08:37 +0200 Subject: [PATCH 10/17] Clean up now that it works in CI --- packages/fuzzer/fuzzing_async.cpp | 4 ---- tests/signal_handlers/signal_handlers.test.js | 2 -- 2 files changed, 6 deletions(-) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 2da6ed0c..11a61dd8 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -70,12 +70,9 @@ TSFN gTSFN; const std::string SEGFAULT_ERROR_MESSAGE = "Segmentation fault found in fuzz target"; -volatile std::sig_atomic_t gSignalStatus; std::jmp_buf errorBuffer; void ErrorSignalHandler(int signum) { - std::cerr << "segfault detected" << std::endl; - gSignalStatus = signum; std::longjmp(errorBuffer, signum); } @@ -305,7 +302,6 @@ Napi::Value StartFuzzingAsync(const Napi::CallbackInfo &info) { context->native_thread = std::thread( [](std::vector fuzzer_args, AsyncFuzzTargetContext *ctx) { try { - std::cerr << "assigning segfault handler" << std::endl; signal(SIGSEGV, ErrorSignalHandler); StartLibFuzzer(fuzzer_args, FuzzCallbackAsync); } catch (const JSException &exception) { diff --git a/tests/signal_handlers/signal_handlers.test.js b/tests/signal_handlers/signal_handlers.test.js index 58743779..8c8c663d 100644 --- a/tests/signal_handlers/signal_handlers.test.js +++ b/tests/signal_handlers/signal_handlers.test.js @@ -98,7 +98,6 @@ describe("SIGSEGV handlers", () => { it("stop async fuzzing on SIGSEGV", () => { const fuzzTest = fuzzTestBuilder .sync(false) - .verbose(true) .fuzzEntryPoint("SIGSEGV_ASYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); @@ -116,7 +115,6 @@ describe("SIGSEGV handlers", () => { it("stop fuzzing on native async SIGSEGV", () => { const fuzzTest = fuzzTestBuilder .sync(false) - .verbose(true) .fuzzEntryPoint("NATIVE_SIGSEGV_ASYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); From dc149ef979402b4c89bf1094a6b8af5f33a5833e Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 26 Sep 2023 17:17:40 +0200 Subject: [PATCH 11/17] Remove compile_commands from gitignore and ignore it in linting --- .gitignore | 2 -- package.json | 2 +- tests/signal_handlers/native-signal/compile_commands.json | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) create mode 120000 tests/signal_handlers/native-signal/compile_commands.json diff --git a/.gitignore b/.gitignore index becad648..6bdd79e7 100644 --- a/.gitignore +++ b/.gitignore @@ -54,5 +54,3 @@ node_modules/ # Dictionaries generated by Jazzer.js .JazzerJs-merged-dictionaries - -compile_commands.json diff --git a/package.json b/package.json index ef8f1bd0..f6d04024 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "packages/*" ], "lint-staged": { - "**/*": "prettier --write --ignore-unknown --allow-empty --loglevel debug" + "**/!(compile_commands.json)*": "prettier --write --ignore-unknown --allow-empty --log-level debug" }, "engines": { "node": ">= 14.0.0", diff --git a/tests/signal_handlers/native-signal/compile_commands.json b/tests/signal_handlers/native-signal/compile_commands.json new file mode 120000 index 00000000..25eb4b2b --- /dev/null +++ b/tests/signal_handlers/native-signal/compile_commands.json @@ -0,0 +1 @@ +build/compile_commands.json \ No newline at end of file From 6d5416adff9a32d7858febf858a6fd9db5c56ce6 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 26 Sep 2023 17:20:13 +0200 Subject: [PATCH 12/17] Run formatter --- packages/fuzzer/fuzzing_async.cpp | 11 +++++------ packages/fuzzer/fuzzing_sync.cpp | 28 +++++++++++++++------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 11a61dd8..63c3bc25 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -13,9 +13,9 @@ // limitations under the License. #include "napi.h" -#include -#include #include +#include +#include #include #include @@ -68,13 +68,12 @@ using FinalizerDataType = void; TSFN gTSFN; -const std::string SEGFAULT_ERROR_MESSAGE = "Segmentation fault found in fuzz target"; +const std::string SEGFAULT_ERROR_MESSAGE = + "Segmentation fault found in fuzz target"; std::jmp_buf errorBuffer; -void ErrorSignalHandler(int signum) { - std::longjmp(errorBuffer, signum); -} +void ErrorSignalHandler(int signum) { std::longjmp(errorBuffer, signum); } // The libFuzzer callback when fuzzing asynchronously. int FuzzCallbackAsync(const uint8_t *Data, size_t Size) { diff --git a/packages/fuzzer/fuzzing_sync.cpp b/packages/fuzzer/fuzzing_sync.cpp index 20686502..c8ca36a6 100644 --- a/packages/fuzzer/fuzzing_sync.cpp +++ b/packages/fuzzer/fuzzing_sync.cpp @@ -15,14 +15,15 @@ #include "fuzzing_sync.h" #include "shared/libfuzzer.h" #include "utils.h" +#include #include #include #include #include -#include namespace { -const std::string SEGFAULT_ERROR_MESSAGE = "Segmentation fault found in fuzz target"; +const std::string SEGFAULT_ERROR_MESSAGE = + "Segmentation fault found in fuzz target"; // Information about a JS fuzz target. struct FuzzTargetInfo { @@ -43,15 +44,16 @@ volatile std::sig_atomic_t gSignalStatus; std::jmp_buf errorBuffer; } // namespace -void sigintHandler(int signum) { - gSignalStatus = signum; -} +void sigintHandler(int signum) { gSignalStatus = signum; } -// This handles signals that indicate an unrecoverable error (currently only segfaults). -// Our handling of segfaults is odd because it avoids using our Javascript method to print and instead prints a message -// within C++ and exits almost immediately. This is because Node seems to really not like being called back into -// after `longjmp` jumps outside the scope Node thinks it should be in and so things in JS-land get pretty broken. -// However, catching it here, printing an ok error message, and letting libfuzzer make the crash file is good enough +// This handles signals that indicate an unrecoverable error (currently only +// segfaults). Our handling of segfaults is odd because it avoids using our +// Javascript method to print and instead prints a message within C++ and exits +// almost immediately. This is because Node seems to really not like being +// called back into after `longjmp` jumps outside the scope Node thinks it +// should be in and so things in JS-land get pretty broken. However, catching it +// here, printing an ok error message, and letting libfuzzer make the crash file +// is good enough void error_signal_handler(int signum) { gSignalStatus = signum; std::longjmp(errorBuffer, signum); @@ -88,10 +90,10 @@ int FuzzCallbackSync(const uint8_t *Data, size_t Size) { } } - if (gSignalStatus != 0) { - // if we caught a segfault, print the error message and die, letting libfuzzer print the crash file - // see the comment on `error_signal_handler` for why + // if we caught a segfault, print the error message and die, letting + // libfuzzer print the crash file see the comment on `error_signal_handler` + // for why if (gSignalStatus == SIGSEGV) { std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; exit(139); From fc14d486174b3fff4f6cc27dd6c82aa115c601be Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Tue, 26 Sep 2023 17:20:55 +0200 Subject: [PATCH 13/17] Add copyright header --- .../signal_handlers/native-signal/signal_impl.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/signal_handlers/native-signal/signal_impl.cpp b/tests/signal_handlers/native-signal/signal_impl.cpp index 0b23b046..74493db5 100644 --- a/tests/signal_handlers/native-signal/signal_impl.cpp +++ b/tests/signal_handlers/native-signal/signal_impl.cpp @@ -1,3 +1,17 @@ +// Copyright 2023 Code Intelligence GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include From baf93647664d9467f3140b7f8f760d98445e3ca3 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 27 Sep 2023 14:19:51 +0200 Subject: [PATCH 14/17] Clean up some documentation and packaging files --- packages/fuzzer/fuzzing_async.cpp | 7 +- packages/fuzzer/fuzzing_sync.cpp | 8 +- .../native-signal/package.json | 3 - .../native-signal/tsconfig.json | 98 ------------------- 4 files changed, 10 insertions(+), 106 deletions(-) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 63c3bc25..9fed5217 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -73,6 +73,7 @@ const std::string SEGFAULT_ERROR_MESSAGE = std::jmp_buf errorBuffer; +// See comment on `ErrorSignalHandler` in `fuzzing_sync.cpp` for what this is for void ErrorSignalHandler(int signum) { std::longjmp(errorBuffer, signum); } // The libFuzzer callback when fuzzing asynchronously. @@ -116,9 +117,13 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function jsFuzzCallback, // thread and continue with the next invocation. try { + // Return point for the segfault error handler + // This MUST BE called from the thread that executes the fuzz target (and + // thus is the thread with the segfault) otherwise longjmp's behavior is + // undefined if (setjmp(errorBuffer) != 0) { std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; - exit(139); + exit(EXIT_FAILURE); } if (env != nullptr) { auto buffer = Napi::Buffer::Copy(env, data->data, data->size); diff --git a/packages/fuzzer/fuzzing_sync.cpp b/packages/fuzzer/fuzzing_sync.cpp index c8ca36a6..41848f5c 100644 --- a/packages/fuzzer/fuzzing_sync.cpp +++ b/packages/fuzzer/fuzzing_sync.cpp @@ -54,7 +54,7 @@ void sigintHandler(int signum) { gSignalStatus = signum; } // should be in and so things in JS-land get pretty broken. However, catching it // here, printing an ok error message, and letting libfuzzer make the crash file // is good enough -void error_signal_handler(int signum) { +void ErrorSignalHandler(int signum) { gSignalStatus = signum; std::longjmp(errorBuffer, signum); } @@ -92,11 +92,11 @@ int FuzzCallbackSync(const uint8_t *Data, size_t Size) { if (gSignalStatus != 0) { // if we caught a segfault, print the error message and die, letting - // libfuzzer print the crash file see the comment on `error_signal_handler` + // libfuzzer print the crash file. See the comment on `ErrorSignalHandler` // for why if (gSignalStatus == SIGSEGV) { std::cerr << SEGFAULT_ERROR_MESSAGE << std::endl; - exit(139); + exit(EXIT_FAILURE); } // Non-zero exit codes will produce crash files. @@ -139,7 +139,7 @@ void StartFuzzing(const Napi::CallbackInfo &info) { info[2].As()}; signal(SIGINT, sigintHandler); - signal(SIGSEGV, error_signal_handler); + signal(SIGSEGV, ErrorSignalHandler); StartLibFuzzer(fuzzer_args, FuzzCallbackSync); // Explicitly reset the global function pointer because the JS diff --git a/tests/signal_handlers/native-signal/package.json b/tests/signal_handlers/native-signal/package.json index 8b36dca4..202a5588 100644 --- a/tests/signal_handlers/native-signal/package.json +++ b/tests/signal_handlers/native-signal/package.json @@ -1,14 +1,11 @@ { "name": "native-signal", "version": "1.0.0", - "description": "", "main": "dist/index.js", "scripts": { "install": "npm run build", "build": "cmake-js build && tsc" }, - "author": "", - "license": "ISC", "devDependencies": { "typescript": "^5.2.2" }, diff --git a/tests/signal_handlers/native-signal/tsconfig.json b/tests/signal_handlers/native-signal/tsconfig.json index 25d16bc9..d02ef81c 100644 --- a/tests/signal_handlers/native-signal/tsconfig.json +++ b/tests/signal_handlers/native-signal/tsconfig.json @@ -1,109 +1,11 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ "module": "commonjs" /* Specify what module code is generated. */, - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ "outDir": "./dist" /* Specify an output folder for all emitted files. */, - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, - - /* Type Checking */ "strict": true /* Enable all strict type-checking options. */, - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, "exclude": ["build", "dist"] From cb3b5f7a394346b1911ebe670375139a313e32c5 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 27 Sep 2023 16:14:35 +0200 Subject: [PATCH 15/17] Clean up tests a little --- tests/signal_handlers/SIGSEGV/fuzz.js | 16 +++++----- tests/signal_handlers/signal_handlers.test.js | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/tests/signal_handlers/SIGSEGV/fuzz.js b/tests/signal_handlers/SIGSEGV/fuzz.js index 2505726b..b85077af 100644 --- a/tests/signal_handlers/SIGSEGV/fuzz.js +++ b/tests/signal_handlers/SIGSEGV/fuzz.js @@ -16,14 +16,16 @@ const native = require("native-signal"); +const RUN_ON_ITERATION = 1000; + let i = 0; module.exports.SIGSEGV_SYNC = (data) => { - if (i === 1000) { + if (i === RUN_ON_ITERATION) { console.log("kill with signal"); process.kill(process.pid, "SIGSEGV"); } - if (i > 1000) { + if (i > RUN_ON_ITERATION) { console.log("Signal has not stopped the fuzzing process"); } i++; @@ -32,7 +34,7 @@ module.exports.SIGSEGV_SYNC = (data) => { module.exports.SIGSEGV_ASYNC = (data) => { // Raising SIGSEGV in async mode does not stop the fuzzer directly, // as the event is handled asynchronously in the event loop. - if (i === 1000) { + if (i === RUN_ON_ITERATION) { console.log("kill with signal"); process.kill(process.pid, "SIGSEGV"); } @@ -40,19 +42,17 @@ module.exports.SIGSEGV_ASYNC = (data) => { }; module.exports.NATIVE_SIGSEGV_SYNC = (data) => { - if (i === 1000) { - console.log("kill with signal"); + if (i === RUN_ON_ITERATION) { native.sigsegv(0); } - if (i > 1000) { + if (i > RUN_ON_ITERATION) { console.log("Signal has not stopped the fuzzing process"); } i++; }; module.exports.NATIVE_SIGSEGV_ASYNC = async (data) => { - if (i === 1000) { - console.log("kill with signal"); + if (i === RUN_ON_ITERATION) { native.sigsegv(0); } i++; diff --git a/tests/signal_handlers/signal_handlers.test.js b/tests/signal_handlers/signal_handlers.test.js index 83f72b3e..e110e4de 100644 --- a/tests/signal_handlers/signal_handlers.test.js +++ b/tests/signal_handlers/signal_handlers.test.js @@ -93,6 +93,7 @@ describe("SIGSEGV handlers", () => { .build(); expect(() => fuzzTest.execute()).toThrowError(); assertSignalMessagesLogged(fuzzTest); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); it("stop async fuzzing on SIGSEGV", () => { @@ -102,6 +103,7 @@ describe("SIGSEGV handlers", () => { .build(); expect(() => fuzzTest.execute()).toThrowError(); assertSignalMessagesLogged(fuzzTest); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); it("stop fuzzing on native SIGSEGV", () => { @@ -110,6 +112,7 @@ describe("SIGSEGV handlers", () => { .fuzzEntryPoint("NATIVE_SIGSEGV_SYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); it("stop fuzzing on native async SIGSEGV", () => { @@ -118,6 +121,7 @@ describe("SIGSEGV handlers", () => { .fuzzEntryPoint("NATIVE_SIGSEGV_ASYNC") .build(); expect(() => fuzzTest.execute()).toThrowError(); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); }); @@ -131,6 +135,7 @@ describe("SIGSEGV handlers", () => { .build(); expect(() => fuzzTest.execute()).toThrowError(); assertSignalMessagesLogged(fuzzTest); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); it("stop async fuzzing on SIGSEGV", () => { @@ -141,6 +146,29 @@ describe("SIGSEGV handlers", () => { .build(); expect(() => fuzzTest.execute()).toThrowError(); assertSignalMessagesLogged(fuzzTest); + assertFuzzingStopped(fuzzTest); + assertErrorAndCrashFileLogged(fuzzTest, errorMessage); + }); + it("stop sync fuzzing on native SIGSEGV", () => { + const fuzzTest = fuzzTestBuilder + .jestTestFile("tests.fuzz.js") + .jestTestName("^Jest Native$") + .jestRunInFuzzingMode(true) + .verbose(true) + .build(); + expect(() => fuzzTest.execute()).toThrowError(); + assertFuzzingStopped(fuzzTest); + assertErrorAndCrashFileLogged(fuzzTest, errorMessage); + }); + it("stop async fuzzing on native SIGSEGV", () => { + const fuzzTest = fuzzTestBuilder + .jestTestFile("tests.fuzz.js") + .jestTestName("^Jest Native Async$") + .jestRunInFuzzingMode(true) + .verbose(true) + .build(); + expect(() => fuzzTest.execute()).toThrowError(); + assertFuzzingStopped(fuzzTest); assertErrorAndCrashFileLogged(fuzzTest, errorMessage); }); }); @@ -148,7 +176,9 @@ describe("SIGSEGV handlers", () => { function assertSignalMessagesLogged(fuzzTest) { expect(fuzzTest.stdout).toContain("kill with signal"); +} +function assertFuzzingStopped(fuzzTest) { // Count how many times "Signal has not stopped the fuzzing process" has been printed. const matches = fuzzTest.stdout.match( /Signal has not stopped the fuzzing process/g, From 71aa6af999ffab0be88c8accb53508a62d294432 Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 27 Sep 2023 16:15:18 +0200 Subject: [PATCH 16/17] Fix formatting --- packages/fuzzer/fuzzing_async.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/fuzzer/fuzzing_async.cpp b/packages/fuzzer/fuzzing_async.cpp index 9fed5217..1d7e1780 100644 --- a/packages/fuzzer/fuzzing_async.cpp +++ b/packages/fuzzer/fuzzing_async.cpp @@ -73,7 +73,8 @@ const std::string SEGFAULT_ERROR_MESSAGE = std::jmp_buf errorBuffer; -// See comment on `ErrorSignalHandler` in `fuzzing_sync.cpp` for what this is for +// See comment on `ErrorSignalHandler` in `fuzzing_sync.cpp` for what this is +// for void ErrorSignalHandler(int signum) { std::longjmp(errorBuffer, signum); } // The libFuzzer callback when fuzzing asynchronously. From 87c6db464df947b4ed2a126413795153b89430da Mon Sep 17 00:00:00 2001 From: Brian Lewis Date: Wed, 27 Sep 2023 16:28:50 +0200 Subject: [PATCH 17/17] Add formatting for native-signal --- .../native-signal/package.json | 8 +++--- .../native-signal/signal_impl.cpp | 26 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/signal_handlers/native-signal/package.json b/tests/signal_handlers/native-signal/package.json index 202a5588..49195b69 100644 --- a/tests/signal_handlers/native-signal/package.json +++ b/tests/signal_handlers/native-signal/package.json @@ -3,11 +3,13 @@ "version": "1.0.0", "main": "dist/index.js", "scripts": { - "install": "npm run build", - "build": "cmake-js build && tsc" + "postinstall": "npm run build", + "build": "cmake-js build && tsc", + "format:fix": "clang-format -i *.cpp" }, "devDependencies": { - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "clang-format": "^1.8.0" }, "binary": { "napi_versions": [ diff --git a/tests/signal_handlers/native-signal/signal_impl.cpp b/tests/signal_handlers/native-signal/signal_impl.cpp index 74493db5..255e6985 100644 --- a/tests/signal_handlers/native-signal/signal_impl.cpp +++ b/tests/signal_handlers/native-signal/signal_impl.cpp @@ -17,23 +17,21 @@ #include -void sigsegv(const Napi::CallbackInfo &info) -{ - if (info.Length() != 1 || !info[0].IsNumber()) - { - throw Napi::Error::New(info.Env(), "Need a single integer argument"); - } - // accepts a parameter to prevent the compiler from optimizing a static segfault away - int location = info[0].ToNumber(); - int *a = (int *)location; - *a = 10; +void sigsegv(const Napi::CallbackInfo &info) { + if (info.Length() != 1 || !info[0].IsNumber()) { + throw Napi::Error::New(info.Env(), "Need a single integer argument"); + } + // accepts a parameter to prevent the compiler from optimizing a static + // segfault away + int location = info[0].ToNumber(); + int *a = (int *)location; + *a = 10; } -Napi::Object Init(Napi::Env env, Napi::Object exports) -{ - exports["sigsegv"] = Napi::Function::New(env); +Napi::Object Init(Napi::Env env, Napi::Object exports) { + exports["sigsegv"] = Napi::Function::New(env); - return exports; + return exports; } NODE_API_MODULE(signal_impl, Init);