Fix memory leak

This commit is contained in:
Antonio Antonino 2026-06-09 11:11:43 +02:00
parent 523091e17c
commit 969d9e2cda
No known key found for this signature in database
GPG Key ID: 70CC1DF6BCF7E76D
6 changed files with 25 additions and 16 deletions

View File

@ -4,6 +4,17 @@
#include <vector>
#include <sstream>
#include <stdexcept>
#include <mutex>
Circom_Circuit* getCachedCircuit(const ConstBytes& circuit_bytes) {
// The circuit is immutable, compiled-in data and has no destructor that
// frees its internal buffers, so load it once and keep it for the process
// lifetime instead of allocating (and leaking) it on every call.
static Circom_Circuit* cached = nullptr;
static std::once_flag once;
std::call_once(once, [&]() { cached = loadCircuit(circuit_bytes); });
return cached;
}
Circom_Circuit* loadCircuit(const ConstBytes& circuit_bytes) {
Circom_Circuit* circuit = new Circom_Circuit;

View File

@ -7,6 +7,16 @@
// Return value
Circom_Circuit* loadCircuit(const ConstBytes& circuit);
// Returns a process-wide, lazily-loaded circuit for this library.
//
// `loadCircuit` allocates the circuit's internal buffers (input hash map,
// witness->signal list, constants, IO field defs) but `Circom_Circuit` has no
// destructor, so `delete circuit` leaks all of them. The circuit is immutable
// data derived from the compiled-in `.dat`, so we load it exactly once and
// reuse it for every (including concurrent) witness-generation call.
Circom_Circuit* getCachedCircuit(const ConstBytes& circuit);
void loadJson(Circom_CalcWit *ctx, const char* inputs_json);
void writeBinWitness(Circom_CalcWit *ctx, Bytes* output_witness);

View File

@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes*
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_Circuit* circuit = getCachedCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
try {
loadJson(ctx, input->inputs_json);
} catch (...) {
delete ctx;
delete circuit;
throw;
}
if (ctx->getRemaingInputsToBeSet()!=0) {
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
delete ctx;
delete circuit;
return status_new(StatusCode_InvalidInput, message.c_str());
}
writeBinWitness(ctx, output);
delete ctx;
delete circuit;
return status_ok();
}

View File

@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes*
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_Circuit* circuit = getCachedCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
try {
loadJson(ctx, input->inputs_json);
} catch (...) {
delete ctx;
delete circuit;
throw;
}
if (ctx->getRemaingInputsToBeSet()!=0) {
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
delete ctx;
delete circuit;
return status_new(StatusCode_InvalidInput, message.c_str());
}
writeBinWitness(ctx, output);
delete ctx;
delete circuit;
return status_ok();
}

View File

@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes*
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_Circuit* circuit = getCachedCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
try {
loadJson(ctx, input->inputs_json);
} catch (...) {
delete ctx;
delete circuit;
throw;
}
if (ctx->getRemaingInputsToBeSet()!=0) {
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
delete ctx;
delete circuit;
return status_new(StatusCode_InvalidInput, message.c_str());
}
writeBinWitness(ctx, output);
delete ctx;
delete circuit;
return status_ok();
}

View File

@ -89,26 +89,23 @@ static Status validate_witness_arguments(const WitnessInput* input, const Bytes*
static Status generate_witness_impl(const WitnessInput* input, Bytes* output) {
const ConstBytes& circuit_bytes = input->dat;
Circom_Circuit* circuit = loadCircuit(circuit_bytes);
Circom_Circuit* circuit = getCachedCircuit(circuit_bytes);
Circom_CalcWit* ctx = new Circom_CalcWit(circuit);
try {
loadJson(ctx, input->inputs_json);
} catch (...) {
delete ctx;
delete circuit;
throw;
}
if (ctx->getRemaingInputsToBeSet()!=0) {
const std::string message = "Not all inputs have been set. Only " + std::to_string(get_main_input_signal_no()-ctx->getRemaingInputsToBeSet()) + " out of " + std::to_string(get_main_input_signal_no()) + ".";
delete ctx;
delete circuit;
return status_new(StatusCode_InvalidInput, message.c_str());
}
writeBinWitness(ctx, output);
delete ctx;
delete circuit;
return status_ok();
}