@@ -144,13 +144,17 @@ struct SyncFuzzTargetContext {
144144 LibAflOptions options;
145145 SyncWatchdogState watchdog;
146146 volatile std::sig_atomic_t signal_status = 0 ;
147+ volatile std::sig_atomic_t execution_active = 0 ;
147148 volatile int sigints = 0 ;
148149 std::jmp_buf execution_context;
149150};
150151
151152struct AsyncExecutionState {
152153 std::promise<int > promise;
153154 std::atomic<bool > settled = false ;
155+ bool done_called = false ;
156+ bool done_succeeded = false ;
157+ bool callback_invocation_completed = false ;
154158};
155159
156160struct AsyncDataType {
@@ -173,8 +177,8 @@ struct AsyncFuzzTargetContext {
173177 LibAflOptions options;
174178 std::unique_ptr<ScopedLibAflRuntime> runtime_guard;
175179 bool is_resolved = false ;
176- bool is_done_called = false ;
177180 int run_status = kRuntimeOk ;
181+ volatile std::sig_atomic_t execution_active = 0 ;
178182 volatile int sigints = 0 ;
179183 std::jmp_buf execution_context;
180184};
@@ -227,29 +231,77 @@ void SettleLibAflRun(Napi::Env env, Napi::Promise::Deferred &deferred,
227231 }
228232}
229233
230- bool TrySetExecutionStatus (const std::shared_ptr<AsyncExecutionState> &state,
231- int status) {
234+ bool IsExecutionSettled (const std::shared_ptr<AsyncExecutionState> &state) {
235+ return state->settled .load (std::memory_order_acquire);
236+ }
237+
238+ bool TryClaimExecution (const std::shared_ptr<AsyncExecutionState> &state) {
232239 bool expected = false ;
233240 if (!state->settled .compare_exchange_strong (expected, true ,
234241 std::memory_order_acq_rel,
235242 std::memory_order_acquire)) {
236243 return false ;
237244 }
245+ return true ;
246+ }
247+
248+ void PublishExecutionStatus (const std::shared_ptr<AsyncExecutionState> &state,
249+ int status) {
238250 state->promise .set_value (status);
251+ }
252+
253+ bool TryPublishExecutionStatus (
254+ const std::shared_ptr<AsyncExecutionState> &state, int status) {
255+ if (!TryClaimExecution (state)) {
256+ return false ;
257+ }
258+ PublishExecutionStatus (state, status);
239259 return true ;
240260}
241261
262+ Napi::Value NormalizeAsyncError (Napi::Env env, const Napi::Value &error) {
263+ if (error.IsObject ()) {
264+ return error;
265+ }
266+ return Napi::Error::New (env, error.ToString ()).Value ();
267+ }
268+
242269void ReportAsyncFinding (AsyncFuzzTargetContext *context, Napi::Env env,
243270 const std::shared_ptr<AsyncExecutionState> &state,
244271 const Napi::Value &error,
245272 const std::vector<uint8_t > &input) {
246- StoreDeferredRejection (context, error);
247- if (TrySetExecutionStatus (state, kExecutionFinding )) {
248- const auto artifact =
249- WriteArtifact (context->options .artifact_prefix , " crash" , input.data (),
250- input.size (), false );
251- RecordFindingInfo (&gFindingInfo , artifact, DescribeJsError (env, error));
273+ if (!TryClaimExecution (state)) {
274+ return ;
275+ }
276+
277+ auto normalized_error = NormalizeAsyncError (env, error);
278+ auto summary = std::string (" The LibAFL backend found a crashing input" );
279+ const auto artifact = WriteArtifact (context->options .artifact_prefix , " crash" ,
280+ input.data (), input.size (), false );
281+ try {
282+ summary = DescribeJsError (env, normalized_error);
283+ } catch (const std::exception &exception) {
284+ normalized_error =
285+ Napi::Error::New (env, std::string (" Internal fuzzer error - " ) +
286+ exception.what ())
287+ .Value ();
288+ summary = normalized_error.ToString ().Utf8Value ();
289+ }
290+
291+ RecordFindingInfo (&gFindingInfo , artifact, summary);
292+ StoreDeferredRejection (context, normalized_error);
293+ PublishExecutionStatus (state, kExecutionFinding );
294+ }
295+
296+ void ReportAsyncInternalError (AsyncFuzzTargetContext *context, Napi::Env env,
297+ const std::shared_ptr<AsyncExecutionState> &state,
298+ const std::string &message) {
299+ if (!TryClaimExecution (state)) {
300+ return ;
252301 }
302+
303+ StoreDeferredRejection (context, Napi::Error::New (env, message).Value ());
304+ PublishExecutionStatus (state, kExecutionFatal );
253305}
254306
255307void SettleAsyncLibAflRun (Napi::Env env, AsyncFuzzTargetContext *context) {
@@ -366,17 +418,26 @@ class ScopedSyncWatchdog {
366418};
367419
368420void SyncSigintHandler (int signum) {
369- std::cerr << std::endl;
370- gActiveSyncContext ->signal_status = signum;
371- if (gActiveSyncContext ->sigints > 0 ) {
421+ auto *context = gActiveSyncContext ;
422+ if (context == nullptr ) {
423+ _Exit (libfuzzer::RETURN_CONTINUE);
424+ }
425+
426+ context->signal_status = signum;
427+ if (context->sigints > 0 ) {
372428 _Exit (libfuzzer::RETURN_CONTINUE);
373429 }
374- gActiveSyncContext ->sigints ++;
430+ context ->sigints ++;
375431}
376432
377433void SyncErrorSignalHandler (int signum) {
378- gActiveSyncContext ->signal_status = signum;
379- std::longjmp (gActiveSyncContext ->execution_context , signum);
434+ auto *context = gActiveSyncContext ;
435+ if (context == nullptr || context->execution_active == 0 ) {
436+ _Exit (libfuzzer::EXIT_ERROR_SEGV);
437+ }
438+
439+ context->signal_status = signum;
440+ std::longjmp (context->execution_context , signum);
380441}
381442
382443int ExecuteSyncInput (void *user_data, const uint8_t *data, size_t size) {
@@ -389,15 +450,19 @@ int ExecuteSyncInput(void *user_data, const uint8_t *data, size_t size) {
389450
390451 try {
391452 auto buffer = Napi::Buffer<uint8_t >::Copy (context->env , data, size);
392- if (setjmp (context->execution_context ) == 0 ) {
453+ context->execution_active = 1 ;
454+ const auto signal_status = setjmp (context->execution_context );
455+ if (signal_status == 0 ) {
393456 auto result = context->target .Call ({buffer});
394457 if (result.IsPromise ()) {
395458 AsyncReturnsHandler ();
396459 } else {
397460 SyncReturnsHandler ();
398461 }
399462 }
463+ context->execution_active = 0 ;
400464 } catch (const Napi::Error &error) {
465+ context->execution_active = 0 ;
401466 if (!context->is_resolved ) {
402467 const auto artifact = WriteArtifact (context->options .artifact_prefix ,
403468 " crash" , data, size, false );
@@ -408,6 +473,7 @@ int ExecuteSyncInput(void *user_data, const uint8_t *data, size_t size) {
408473 }
409474 return kExecutionFinding ;
410475 } catch (const std::exception &exception) {
476+ context->execution_active = 0 ;
411477 ExitWithUnexpectedError (exception);
412478 }
413479
@@ -439,19 +505,23 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function js_fuzz_callback,
439505
440506 try {
441507 if (context->sigints > 0 ) {
442- TrySetExecutionStatus (state, kExecutionStop );
508+ TryPublishExecutionStatus (state, kExecutionStop );
443509 return ;
444510 }
445511
446- if (setjmp (context->execution_context ) == SIGSEGV) {
512+ context->execution_active = 1 ;
513+ const auto signal_status = setjmp (context->execution_context );
514+ if (signal_status == SIGSEGV) {
515+ context->execution_active = 0 ;
447516 std::cerr << " ==" << static_cast <unsigned long >(GetPID ())
448517 << " == Segmentation Fault" << std::endl;
449518 libfuzzer::PrintCrashingInput ();
450519 _Exit (libfuzzer::EXIT_ERROR_SEGV);
451520 }
452521
453522 if (env == nullptr ) {
454- TrySetExecutionStatus (state, kExecutionFatal );
523+ context->execution_active = 0 ;
524+ TryPublishExecutionStatus (state, kExecutionFatal );
455525 return ;
456526 }
457527
@@ -463,13 +533,12 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function js_fuzz_callback,
463533 .Int32Value ();
464534
465535 if (parameter_count > 1 ) {
466- context->is_done_called = false ;
467536 auto done = Napi::Function::New (env, [=](const Napi::CallbackInfo &info) {
468- if (context-> is_resolved ) {
537+ if (IsExecutionSettled (state) ) {
469538 return ;
470539 }
471540
472- if (context-> is_done_called ) {
541+ if (state-> done_called ) {
473542 auto error =
474543 Napi::Error::New (env, " Expected done to be called once, but it "
475544 " was called multiple times." )
@@ -478,21 +547,24 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function js_fuzz_callback,
478547 return ;
479548 }
480549
481- context-> is_done_called = true ;
550+ state-> done_called = true ;
482551 const auto has_error =
483552 info.Length () > 0 && !(info[0 ].IsNull () || info[0 ].IsUndefined ());
484553 if (has_error) {
485- auto error = info[0 ];
486- if (!error.IsObject ()) {
487- error = Napi::Error::New (env, error.ToString ()).Value ();
488- }
489- ReportAsyncFinding (context, env, state, error, current_input);
554+ ReportAsyncFinding (context, env, state, info[0 ], current_input);
555+ return ;
556+ }
557+
558+ if (state->callback_invocation_completed ) {
559+ TryPublishExecutionStatus (state, kExecutionContinue );
490560 } else {
491- TrySetExecutionStatus ( state, kExecutionContinue ) ;
561+ state-> done_succeeded = true ;
492562 }
493563 });
494564
495565 auto result = js_fuzz_callback.Call ({buffer, done});
566+ state->callback_invocation_completed = true ;
567+ context->execution_active = 0 ;
496568 if (result.IsPromise ()) {
497569 AsyncReturnsHandler ();
498570 auto error =
@@ -502,19 +574,23 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function js_fuzz_callback,
502574 ReportAsyncFinding (context, env, state, error, current_input);
503575 } else {
504576 SyncReturnsHandler ();
577+ if (state->done_succeeded ) {
578+ TryPublishExecutionStatus (state, kExecutionContinue );
579+ }
505580 }
506581 return ;
507582 }
508583
509584 auto result = js_fuzz_callback.Call ({buffer});
585+ context->execution_active = 0 ;
510586 if (result.IsPromise ()) {
511587 AsyncReturnsHandler ();
512588 auto js_promise = result.As <Napi::Object>();
513589 auto then = js_promise.Get (" then" ).As <Napi::Function>();
514590 then.Call (js_promise,
515591 {Napi::Function::New (env,
516592 [=](const Napi::CallbackInfo &) {
517- TrySetExecutionStatus (
593+ TryPublishExecutionStatus (
518594 state, kExecutionContinue );
519595 }),
520596 Napi::Function::New (env, [=](const Napi::CallbackInfo &info) {
@@ -523,37 +599,43 @@ void CallJsFuzzCallback(Napi::Env env, Napi::Function js_fuzz_callback,
523599 ? info[0 ]
524600 : Napi::Error::New (env, " Unknown promise rejection" )
525601 .Value ();
526- if (!error.IsObject ()) {
527- error = Napi::Error::New (env, error.ToString ()).Value ();
528- }
529602 ReportAsyncFinding (context, env, state, error,
530603 current_input);
531604 })});
532605 } else {
533606 SyncReturnsHandler ();
534- TrySetExecutionStatus (state, kExecutionContinue );
607+ TryPublishExecutionStatus (state, kExecutionContinue );
535608 }
536609 } catch (const Napi::Error &error) {
610+ context->execution_active = 0 ;
537611 ReportAsyncFinding (context, env, state, error.Value (), current_input);
538612 } catch (const std::exception &exception) {
539- if (TrySetExecutionStatus (state, kExecutionFatal )) {
540- auto message =
541- std::string (" Internal fuzzer error - " ).append (exception.what ());
542- StoreDeferredRejection (context, Napi::Error::New (env, message).Value ());
543- }
613+ context->execution_active = 0 ;
614+ ReportAsyncInternalError (
615+ context, env, state,
616+ std::string (" Internal fuzzer error - " ).append (exception.what ()));
544617 }
545618}
546619
547620void AsyncSigintHandler (int signum) {
548- std::cerr << std::endl ;
549- if (gActiveAsyncContext -> sigints > 0 ) {
621+ auto *context = gActiveAsyncContext ;
622+ if (context == nullptr ) {
550623 _Exit (libfuzzer::RETURN_CONTINUE);
551624 }
552- gActiveAsyncContext ->sigints = signum;
625+
626+ if (context->sigints > 0 ) {
627+ _Exit (libfuzzer::RETURN_CONTINUE);
628+ }
629+ context->sigints = signum;
553630}
554631
555632void AsyncErrorSignalHandler (int signum) {
556- std::longjmp (gActiveAsyncContext ->execution_context , signum);
633+ auto *context = gActiveAsyncContext ;
634+ if (context == nullptr || context->execution_active == 0 ) {
635+ _Exit (libfuzzer::EXIT_ERROR_SEGV);
636+ }
637+
638+ std::longjmp (context->execution_context , signum);
557639}
558640
559641int ExecuteAsyncInput (void *user_data, const uint8_t *data, size_t size) {
0 commit comments