Skip to content

Commit ddfb651

Browse files
finding 2: add WASM-compatible error recovery boundary in bbapi
In WASM builds, BB_NO_EXCEPTIONS is defined which compiles out the try-catch in bbapi() and routes throw_or_abort to std::abort(). This kills the WASM process on any error with no recovery. Add a setjmp/longjmp recovery boundary: bbapi() calls setjmp before execute(), and throw_or_abort_impl checks if recovery is active before aborting. On error, longjmp returns control to bbapi() which returns an ErrorResponse via the normal msgpack path. longjmp skips C++ destructors on unwound stack frames, which can leak memory. In WASM this is bounded by linear memory and acceptable vs the alternative of process termination. Enable the CBind.CatchesExceptionAndReturnsErrorResponse test for BB_NO_EXCEPTIONS builds, since error recovery now works in both native (try-catch) and no-exceptions (setjmp/longjmp) configurations.
1 parent 4bb627d commit ddfb651

File tree

4 files changed

+59
-13
lines changed

4 files changed

+59
-13
lines changed

barretenberg/cpp/src/barretenberg/bbapi/c_bind.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#ifndef NO_MULTITHREADING
77
#include <mutex>
88
#endif
9+
#ifdef BB_NO_EXCEPTIONS
10+
#include <csetjmp>
11+
#endif
912

1013
namespace bb::bbapi {
1114

@@ -15,6 +18,35 @@ namespace {
1518
BBApiRequest global_request;
1619
} // namespace
1720

21+
#ifdef BB_NO_EXCEPTIONS
22+
// Recovery state for builds where C++ exceptions are disabled (WASM).
23+
// When throw_or_abort is called during command execution, longjmp returns
24+
// control to the setjmp point in bbapi() so it can return an ErrorResponse
25+
// instead of aborting the process.
26+
// Note: longjmp skips C++ destructors on the unwound stack frames. In WASM
27+
// this causes bounded memory leaks (no kernel resources to leak), which is
28+
// strictly better than the alternative of process termination via std::abort().
29+
namespace {
30+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
31+
thread_local std::jmp_buf error_jmp_buf;
32+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
33+
thread_local std::string error_message;
34+
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
35+
thread_local bool recovery_active = false;
36+
} // namespace
37+
38+
bool bbapi_recovery_active()
39+
{
40+
return recovery_active;
41+
}
42+
43+
void bbapi_recover_with_message(const char* msg)
44+
{
45+
error_message = msg;
46+
std::longjmp(error_jmp_buf, 1); // NOLINT(cert-err52-cpp)
47+
}
48+
#endif
49+
1850
/**
1951
* @brief Main API function that processes commands and returns responses
2052
*
@@ -25,13 +57,19 @@ CommandResponse bbapi(Command&& command)
2557
{
2658
#ifndef BB_NO_EXCEPTIONS
2759
try {
28-
#endif
29-
// Execute the command using the global request and return the response
3060
return execute(global_request, std::move(command));
31-
#ifndef BB_NO_EXCEPTIONS
3261
} catch (const std::exception& e) {
3362
return ErrorResponse{ .message = e.what() };
3463
}
64+
#else
65+
recovery_active = true;
66+
if (setjmp(error_jmp_buf) != 0) {
67+
recovery_active = false;
68+
return ErrorResponse{ .message = std::move(error_message) };
69+
}
70+
auto result = execute(global_request, std::move(command));
71+
recovery_active = false;
72+
return result;
3573
#endif
3674
}
3775

barretenberg/cpp/src/barretenberg/bbapi/c_bind.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
namespace bb::bbapi {
77
// Function declaration for CLI usage
88
CommandResponse bbapi(Command&& command);
9+
10+
#ifdef BB_NO_EXCEPTIONS
11+
// Recovery boundary for builds without C++ exceptions (WASM).
12+
// throw_or_abort_impl checks bbapi_recovery_active() and calls
13+
// bbapi_recover_with_message() to longjmp back to bbapi() instead of aborting.
14+
bool bbapi_recovery_active();
15+
[[noreturn]] void bbapi_recover_with_message(const char* msg);
16+
#endif
917
} // namespace bb::bbapi
1018

1119
// Forward declaration for CBIND

barretenberg/cpp/src/barretenberg/bbapi/c_bind_exception.test.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77

88
using namespace bb::bbapi;
99

10-
#ifndef BB_NO_EXCEPTIONS
11-
12-
// Test that exceptions thrown during command execution are caught and converted to ErrorResponse
10+
// Test that exceptions/errors during command execution are caught and converted to ErrorResponse.
11+
// Works in both native builds (via try-catch) and BB_NO_EXCEPTIONS builds (via setjmp/longjmp recovery).
1312
TEST(CBind, CatchesExceptionAndReturnsErrorResponse)
1413
{
1514
// Create an SrsInitSrs command with invalid data that will cause an exception
@@ -58,10 +57,3 @@ TEST(CBind, ValidOperationReturnsSuccess)
5857
bool is_shutdown = std::holds_alternative<Shutdown::Response>(response.get());
5958
EXPECT_TRUE(is_shutdown) << "Expected Shutdown::Response variant";
6059
}
61-
62-
#else
63-
TEST(CBind, ExceptionsDisabled)
64-
{
65-
GTEST_SKIP() << "Skipping exception handling tests when BB_NO_EXCEPTIONS is defined";
66-
}
67-
#endif

barretenberg/cpp/src/barretenberg/env/throw_or_abort_impl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#ifdef STACKTRACES
55
#include <backward.hpp>
66
#endif
7+
#ifdef BB_NO_EXCEPTIONS
8+
#include "barretenberg/bbapi/c_bind.hpp"
9+
#endif
710

811
inline void abort_with_message [[noreturn]] (std::string const& err)
912
{
@@ -24,6 +27,11 @@ WASM_EXPORT void throw_or_abort_impl [[noreturn]] (const char* err)
2427
#ifndef BB_NO_EXCEPTIONS
2528
throw std::runtime_error(err);
2629
#else
30+
// If bbapi() has set up a recovery boundary, longjmp back to it
31+
// so it can return an ErrorResponse instead of aborting.
32+
if (bb::bbapi::bbapi_recovery_active()) {
33+
bb::bbapi::bbapi_recover_with_message(err);
34+
}
2735
abort_with_message(err);
2836
#endif
2937
}

0 commit comments

Comments
 (0)