Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WifiClientSecure runs out of memory after multiple MQTT publish operations when WiFiClientSecure::loadCACert is used #9636

Open
1 task done
the-butcher opened this issue May 15, 2024 · 4 comments
Assignees
Labels
Area: WiFi Issue related to WiFi Status: Awaiting triage Issue is waiting for triage

Comments

@the-butcher
Copy link

Board

featheresp32-s2

Device Description

Adafruit ESP32-S2 Feather with BME280 Sensor
https://www.adafruit.com/product/5303

platformio.ini as follows:

[env:featheresp32-s2]
platform = espressif32
board = featheresp32-s2
framework = arduino
board_build.f_cpu = 80000000L
board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.partitions = min_spiffs.csv
monitor_speed = 115200
lib_deps =
adafruit/Adafruit NeoPixel@^1.12.0
adafruit/Adafruit BusIO@^1.15.0
SPI@^2.0.0
adafruit/SdFat - Adafruit Fork@^2.2.3
sensirion/Sensirion I2C SCD4x@^0.4.0
adafruit/Adafruit EPD@^4.5.4
adafruit/Adafruit BME280 Library@^2.2.4
adafruit/Adafruit LC709203F@^1.3.4
adafruit/RTClib@^2.1.3
bblanchon/ArduinoJson@5.13.4
ottowinter/ESPAsyncWebServer-esphome@^3.1.0
ricmoo/QRCode@^0.0.1
knolleary/PubSubClient@^2.8
build_flags = -DCORE_DEBUG_LEVEL=0

Hardware Configuration

Yes

  • Adafruit 2.9" Grayscale eInk / ePaper Display FeatherWing
  • Adafruit SCD-41 - True CO2 Temperature and Humidity Sensor
  • Adafruit PCF8523 Real Time Clock Breakout Board
    image

Version

other

IDE Name

PlatformIO

Operating System

Windows 10

Flash frequency

80mHz

PSRAM enabled

no

Upload speed

not specified

Description

A CO2 measuring device tries to publish it's measurements through MQTT in certain intervals, i.e. 3 minutes. The device deep-sleeps between measurements, therefore WifiClient and PubSubClient need to be instantiated and destroyed every time new measurements need to be published.

Since the user can upload the mqtt broker certificate it needs to be kept in a file on SD-card. Each tim a new instance of WifiClientSecure is created, the certificate file is opened and passed to that client through:
WiFiClientSecure::loadCACert(Stream& stream, size_t size)

ESP.getFreeHeap() returns ~70000bytes when first measurements are published, but reduces with each publish operation, until at ~60.000bytes the following error shows:

[start_ssl_client():273]: (-32512) SSL - Memory allocation failed

With SSL not correctly set up any more, further connections fail and subsequently mqtt publish fails too.


Workaround: free heap roughly reduces by the size of the certificate file each time. Looking at the code of WifiClientSecure it seems that a certificate, once loaded, never is freed when the client instance is destroyed.

When loading the certificate manually, then using.
WiFiClientSecure::setCACert (const char *rootCA)
rather than
WiFiClientSecure::loadCACert(Stream& stream, size_t size)
and freeing the loaded data before destroying the client, the memory leak appears to be fixed.

Sketch

//--------------------------------------------------------------------------------
// first solution (memory leak)

// instantiate wifi-client
WiFiClient* wifiClient;
char* certFileData = NULL;
if (crt != "" && ModuleCard::existsPath(crt)) {
	wifiClient = new WiFiClientSecure();
	File32 certFile;
	certFile.open(crt, O_RDONLY);
	((WiFiClientSecure*)wifiClient)->loadCACert(certFile, certFile.size());
	certFile.close();
} else {
	wifiClient = new WiFiClient();
}

// instantiate mqtt-client
PubSubClient* mqttClient;
mqttClient = new PubSubClient(*wifiClient);
mqttClient->setServer(srv, prt);
if (usr != "" && pwd != "") {
	mqttClient->connect(cli, usr, pwd, 0, 0, 0, 0, 0);
} else {
	mqttClient->connect(cli);
}

if (mqttClient->connected()) {
	
	// mqtt publishing happening here, around 100bytes/measurement, 300bytes total
	
	mqttClient->disconnect();  // calls stop() on wificlient
	
}

// redundant call to wifi-client stop
wifiClient->stop();

delete mqttClient; // releases some buffer
delete wifiClient; // calls stop (again) and deletes an internal sslclient instance
mqttClient = NULL;
wifiClient = NULL;

//--------------------------------------------------------------------------------
// alternative solution (no memory leak)

// instantiate wifi-client
WiFiClient* wifiClient;
char* certFileData = NULL;
if (mqtt.crt != "" && ModuleCard::existsPath(mqtt.crt)) {
	wifiClient = new WiFiClientSecure();
	File32 certFile;
	certFile.open(mqtt.crt, O_RDONLY);
	certFileData = (char*)malloc(certFile.size() + 1);
	certFile.readBytes(certFileData, certFile.size());
	((WiFiClientSecure*)wifiClient)->setCACert(certFileData);
	certFile.close();
} else {
	wifiClient = new WiFiClient();
}

// instantiate mqtt-client
PubSubClient* mqttClient;
mqttClient = new PubSubClient(*wifiClient);
mqttClient->setServer(mqtt.srv, mqtt.prt);
if (mqtt.usr != "" && mqtt.pwd != "") {
	mqttClient->connect(mqtt.cli, mqtt.usr, mqtt.pwd, 0, 0, 0, 0, 0);
} else {
	mqttClient->connect(mqtt.cli);  // connect without credentials
}

if (mqttClient->connected()) {

	// mqtt publishing happening here, around 100bytes/measurement, 300bytes total

	mqttClient->disconnect();  // calls stop() on wificlient
	
}

// redundant call to wifi-client stop
wifiClient->stop(); 
if (certFileData != NULL) {
	free(const_cast<char*>(certFileData));  // free the memory used to hold the certificate
	certFileData = NULL;
}

delete mqttClient;  // releases some buffer
delete wifiClient;  // calls stop (again) and deletes an internal sslclient instance
mqttClient = NULL;
wifiClient = NULL;

Debug Message

[start_ssl_client():273]: (-32512) SSL - Memory allocation failed

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

  • I confirm I have checked existing issues, online documentation and Troubleshooting guide.
@the-butcher the-butcher added the Status: Awaiting triage Issue is waiting for triage label May 15, 2024
@VojtechBartoska
Copy link
Collaborator

Hello Hannes, what version of Arduino Core are you running? I do not see it in your issue report.

@the-butcher
Copy link
Author

Sorry for not including in the first place. Is the information below sufficient?

PACKAGES:

  • framework-arduinoespressif32 @ 3.20016.0 (2.0.16)
  • tool-esptoolpy @ 1.40501.0 (4.5.1)
  • toolchain-riscv32-esp @ 8.4.0+2021r2-patch5
  • toolchain-xtensa-esp32s2 @ 8.4.0+2021r2-patch5

@JAndrassy
Copy link
Contributor

JAndrassy commented May 15, 2024

@VojtechBartoska the 3.0.0 has the problem too.

EDIT: or doesn't. but has a problem related to the fix of that problem. If setCaCert is first used with the cert on stack or global, and the load is invoked, it will attempt to delete the instance on stack or global

@VojtechBartoska VojtechBartoska added Area: WiFi Issue related to WiFi and removed Status: Awaiting triage Issue is waiting for triage labels May 15, 2024
@VojtechBartoska VojtechBartoska added the Status: Awaiting triage Issue is waiting for triage label May 15, 2024
@VojtechBartoska
Copy link
Collaborator

@me-no-dev can you please take a look on this? Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: WiFi Issue related to WiFi Status: Awaiting triage Issue is waiting for triage
Projects
None yet
Development

No branches or pull requests

4 participants