Skip to content

Conversation

@gojimmypi
Copy link
Contributor

@gojimmypi gojimmypi commented Feb 26, 2025

Description

This PR fixes a memory leak in the hash_test function of wolfcrypt/test/test.c and adds some Espressif-specific heap checking features.

Background

The memory leak was first brought to my attention by the Devin automated #8471.

I've reviewed the Espressif code. I was unable to find a memory leak.

I also had ChatGPT review the code. There were some false positives, but after I explained the multi-threaded nature of the library, ChatGPT was also unable to find any memory leak candidates.

From what I can see, there is no memory leak detected in any of the core wolfcrypt hash functions, only the test code around current line 6082 (no line as the file is too big to view online):

WOLFSSL_TEST_SUBROUTINE wc_test_ret_t hash_test(void)

The problem is manifested when #define WOLFSSL_SMALL_STACK is found in the user_settings.h.

Generally the test has been used as a "run once and exit" app, so it is unlikely anyone noticed there was a memory leak.

In my case, I typically run the tests in a loop for hours and days on embedded devices. See the #define TEST_LOOP setting.

After about 1,500+ loops, the ESP32 devices would run out of memory. I incorrectly assumed the problem was Espressif-specific.

I've run the benchmark in loops on 9 different devices for days now. Between ~5,000 and ~8,000 loops have completed successfully:

image

I've started a similar loop of the wolfcrypt test again with changes in this PR.

Changes

  • There's a new DEBUG_WOLFSSL_ESP32_HEAP now documented in esp32-crypt.h.

  • The PRINT_HEAP_CHECKPOINT(b, i) has been modified to accept two parameters: a breadcrumb string (b) and an integer index (i).

  • The existing implementation of PRINT_HEAP_CHECKPOINT was not otherwise changed. A new PRINT_HEAP_CHECKPOINT was added for the Espressif ESP-IDF environment.

  • There's a new PRINT_HEAP_ADDRESS that prints the address of a newly allocated memory segment. In this PR, the address of the hash object.

  • There's a new "placeholder" hash type created for known-but-not-implemented hash types. Previously the hash free would quietly fail due to type mismatch, contributing to the memory leak.

  • The default placeholder type is introduced in a new macro: PLACEHOLDER_TYPE. An arbitirary value of 0 is used; the first valid typesGood[] item is used when checking bad parameters.

  • More return codes are checked (e.g. on wc_HashFree) that were not previously checked.

  • The logic for "is this a valid hash?" when checking the typesNoImpl list has been revised. It is no longer sensitive to the order of items. It was also probably not fully working as intended anyhow.

  • New hash objects are created and destroyed as appropriate in the "Good type and implemented" tests.

Still outstanding

There's still another test.c memory leak in or around the PKCS12 test. See message from full log, below:

PKCS12   test passed!
I (13430) wc_test: Breadcrumb: TEST_PASS
W (13431) wc_test: Warning: this heap 308088 != last 308128

I will look at this one at a later date unless otherwise advised.

It would be nice to have the hash checks for other platforms to give similar warnings. For now the feature is Espressif-specific.

Full ESP32 Output

For reference:

I (453) wolfssl_test: --------------------------------------------------------
I (457) wolfssl_test: --------------------------------------------------------
I (465) wolfssl_test: ---------------------- BEGIN MAIN ----------------------
I (473) wolfssl_test: --------------------------------------------------------
I (481) wolfssl_test: --------------------------------------------------------
I (489) wolfssl_test: Stack Start: 0x3ffb52e0
W (494) wolfssl_test: Found WOLFSSL_ESP_NO_WATCHDOG, disabling...
I (501) wolfssl_test: CONFIG_ESP_MAIN_TASK_STACK_SIZE = 10500 bytes (2625 words)
I (509) wolfssl_test: Stack Start HWM: 9240 bytes
I (514) esp32_util: Extended Version and Platform Information.
I (521) esp32_util: Chip revision: v1.0
I (525) esp32_util: SSID and plain text WiFi password not displayed in startup logs.
I (534) esp32_util:   Define SHOW_SSID_AND_PASSWORD to enable display.
I (541) esp32_util: Using wolfSSL user_settings.h in C://workspace//wolfssl-gojimmypi-pr//IDE//Espressif//ESP-IDF//examples//wolfssl_test//components//wolfssl/include/user_settings.h
I (558) esp32_util: LIBWOLFSSL_VERSION_STRING = 5.7.6
I (563) esp32_util: LIBWOLFSSL_VERSION_HEX = 5007006
I (569) esp32_util: Stack HWM: 9160
I (573) esp32_util:
I (576) esp32_util: Macro Name                 Defined   Not Defined
I (583) esp32_util: ------------------------- --------- -------------
I (590) esp32_util: NO_ESP32_CRYPT...........                 X
I (597) esp32_util: NO_ESPIDF_DEFAULT........                 X
I (603) esp32_util: HW_MATH_ENABLED..........     X
I (609) esp32_util: WOLFSSL_SHA224...........     X
I (615) esp32_util: WOLFSSL_SHA384...........     X
I (620) esp32_util: WOLFSSL_SHA512...........     X
I (626) esp32_util: WOLFSSL_SHA3.............                 X
I (632) esp32_util: HAVE_ED25519.............     X
I (638) esp32_util: HAVE_AES_ECB.............                 X
I (644) esp32_util: HAVE_AES_DIRECT..........                 X
I (651) esp32_util: USE_FAST_MATH............     X
I (657) esp32_util: WOLFSSL_SP_MATH_ALL......                 X
I (663) esp32_util: SP_MATH..................                 X
I (670) esp32_util: WOLFSSL_HW_METRICS.......     X
I (675) esp32_util: RSA_LOW_MEM..............     X
I (681) esp32_util: SMALL_SESSION_CACHE......                 X
I (687) esp32_util: WC_NO_HARDEN.............                 X
I (694) esp32_util: TFM_TIMING_RESISTANT.....     X
I (700) esp32_util: ECC_TIMING_RESISTANT.....     X
I (705) esp32_util: WC_NO_CACHE_RESISTANT....     X
I (711) esp32_util: WC_AES_BITSLICED.........                 X
I (717) esp32_util: WOLFSSL_AES_NO_UNROLL....                 X
I (724) esp32_util: TFM_TIMING_RESISTANT.....     X
I (729) esp32_util: ECC_TIMING_RESISTANT.....     X
I (735) esp32_util: WC_RSA_BLINDING..........     X
I (741) esp32_util: NO_WRITEV................     X
I (746) esp32_util: FREERTOS.................     X
I (752) esp32_util: NO_WOLFSSL_DIR...........     X
I (757) esp32_util: WOLFSSL_NO_CURRDIR.......     X
I (763) esp32_util: WOLFSSL_LWIP.............     X
I (768) esp32_util:
I (771) esp32_util: Compiler Optimization: Default
I (777) esp32_util:
I (780) esp32_util: CONFIG_IDF_TARGET = esp32
I (785) esp32_util: Found WOLFSSL_ESP_NO_WATCHDOG
I (790) esp32_util: CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ: 240 MHz
I (796) esp32_util: Xthal_have_ccount: 1
I (801) esp32_util: CONFIG_MAIN_TASK_STACK_SIZE: 10500
I (807) esp32_util: CONFIG_ESP_MAIN_TASK_STACK_SIZE: 10500
I (813) esp32_util: CONFIG_TIMER_TASK_STACK_SIZE: 3584
I (819) esp32_util: CONFIG_TIMER_TASK_STACK_DEPTH: 2048
I (825) esp32_util: Stack HWM: 3ffb526f
I (829) esp32_util: CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ: 240 MHz
I (836) esp32_util: Xthal_have_ccount: 1
I (840) esp32_util: ESP32_CRYPT is enabled for ESP32.
I (846) esp32_util: SINGLE_THREADED
I (850) esp32_util: Boot count: 1
I (854) wolfssl_test: Stack HWM: 8936

I (859) wolfssl_test: Stack HWM: 8936

------------------------------------------------------------------------------
 wolfSSL version 5.7.6
------------------------------------------------------------------------------
error    test passed!
MEMORY   test passed!
base64   test passed!
asn      test passed!
RANDOM   test passed!
MD5      test passed!
MD4      test passed!
SHA      test passed!
SHA-224  test passed!
SHA-256  test passed!
SHA-384  test passed!
SHA-512  test passed!
SHA-512/224  test passed!
SHA-512/256  test passed!
Hash     test passed!
HMAC-MD5 test passed!
HMAC-SHA test passed!
HMAC-SHA224 test passed!
HMAC-SHA256 test passed!
HMAC-SHA384 test passed!
HMAC-SHA512 test passed!
HMAC-KDF    test passed!
PRF         test passed!
TLSv1.3 KDF test passed!
GMAC     test passed!
DES      test passed!
DES3     test passed!
AES      test passed!
AES192   test passed!
AES256   test passed!
AES-CBC  test passed!
AES-CTR  test passed!
AES-GCM  test passed!
RSA      test passed!
PWDBASED test passed!
PKCS12   test passed!
I (13430) wc_test: Breadcrumb: TEST_PASS
W (13431) wc_test: Warning: this heap 308088 != last 308128
ECC      test passed!
ECC buffer test passed!
CURVE25519 test passed!
ED25519  test passed!
mp       test passed!
logging  test passed!
time     test passed!
mutex    test passed!
I (84369) wolfssl_esp32_mp:
I (84370) wolfssl_esp32_mp: esp_mp_mul HW acceleration enabled.
I (84376) wolfssl_esp32_mp: Number of calls to esp_mp_mul: 1039421
I (84383) wolfssl_esp32_mp: Number of calls to esp_mp_mul with tiny operands: 1072
I (84391) wolfssl_esp32_mp: Number of calls to esp_mp_mul HW operand exceeded: 0
I (84399) wolfssl_esp32_mp: Success: no esp_mp_mul() errors.
I (84406) wolfssl_esp32_mp:
I (84409) wolfssl_esp32_mp: esp_mp_mulmod HW acceleration enabled.
I (84416) wolfssl_esp32_mp: Number of calls to esp_mp_mulmod: 1587
I (84423) wolfssl_esp32_mp: Number of calls to esp_mp_mulmod HW operand exceeded: 0
I (84431) wolfssl_esp32_mp: Number of fallback to SW mp_mulmod: 205
I (84438) wolfssl_esp32_mp: Success: no esp_mp_mulmod errors.
I (84445) wolfssl_esp32_mp: Success: no esp_mp_mulmod even mod.
I (84451) wolfssl_esp32_mp: Success: no esp_mp_mulmod small x or y.
I (84458) wolfssl_esp32_mp:
I (84462) wolfssl_esp32_mp: Number of calls to esp_mp_exptmod: 135
I (84468) wolfssl_esp32_mp: Number of calls to esp_mp_exptmod HW operand exceeded: 0
I (84477) wolfssl_esp32_mp: Number of fallback to SW mp_exptmod: 8
I (84484) wolfssl_esp32_mp: Success: no esp_mp_exptmod errors.
I (84490) wolfssl_esp32_mp: Max N->used: esp_mp_max_used = 128
I (84497) wolfssl_esp32_mp: Max hw wait timeout: esp_mp_max_wait_timeout = 1
I (84504) wolfssl_esp32_mp: Max calc timeout: esp_mp_max_timeout = 0x0012a665
Test complete
I (84514) wc_test: Exiting main with return code:  0

I (84519) wolf_hw_sha: --------------------------------------------------------
I (84527) wolf_hw_sha: -------------  wolfSSL ESP HW SHA Metrics  -------------
I (84535) wolf_hw_sha: --------------------------------------------------------
I (84543) wolf_hw_sha: esp_sha_hw_copy_ct            = 4
I (84549) wolf_hw_sha: esp_sha1_hw_usage_ct          = 0
I (84555) wolf_hw_sha: esp_sha1_sw_fallback_usage_ct = 0
I (84561) wolf_hw_sha: esp_sha_reverse_words_ct      = 0
I (84567) wolf_hw_sha: esp_sha1_hw_hash_usage_ct     = 2863
I (84574) wolf_hw_sha: esp_sha2_224_hw_hash_usage_ct = 0
I (84580) wolf_hw_sha: esp_sha2_256_hw_hash_usage_ct = 58591
I (84586) wolf_hw_sha: esp_byte_reversal_checks_ct   = 0
I (84592) wolf_hw_sha: esp_byte_reversal_needed_ct   = 0
I (84598) wolfssl_esp32_mp:
I (84601) wolfssl_esp32_mp: esp_mp_mul HW acceleration enabled.
I (84608) wolfssl_esp32_mp: Number of calls to esp_mp_mul: 1039421
I (84615) wolfssl_esp32_mp: Number of calls to esp_mp_mul with tiny operands: 1072
I (84623) wolfssl_esp32_mp: Number of calls to esp_mp_mul HW operand exceeded: 0
I (84631) wolfssl_esp32_mp: Success: no esp_mp_mul() errors.
I (84638) wolfssl_esp32_mp:
I (84641) wolfssl_esp32_mp: esp_mp_mulmod HW acceleration enabled.
I (84648) wolfssl_esp32_mp: Number of calls to esp_mp_mulmod: 1587
I (84655) wolfssl_esp32_mp: Number of calls to esp_mp_mulmod HW operand exceeded: 0
I (84663) wolfssl_esp32_mp: Number of fallback to SW mp_mulmod: 205
I (84670) wolfssl_esp32_mp: Success: no esp_mp_mulmod errors.
I (84677) wolfssl_esp32_mp: Success: no esp_mp_mulmod even mod.
I (84683) wolfssl_esp32_mp: Success: no esp_mp_mulmod small x or y.
I (84690) wolfssl_esp32_mp:
I (84694) wolfssl_esp32_mp: Number of calls to esp_mp_exptmod: 135
I (84700) wolfssl_esp32_mp: Number of calls to esp_mp_exptmod HW operand exceeded: 0
I (84709) wolfssl_esp32_mp: Number of fallback to SW mp_exptmod: 8
I (84716) wolfssl_esp32_mp: Success: no esp_mp_exptmod errors.
I (84722) wolfssl_esp32_mp: Max N->used: esp_mp_max_used = 128
I (84729) wolfssl_esp32_mp: Max hw wait timeout: esp_mp_max_wait_timeout = 1
I (84737) wolfssl_esp32_mp: Max calc timeout: esp_mp_max_timeout = 0x0012a665
I (84744) wolf_hw_aes: --------------------------------------------------------
I (84752) wolf_hw_aes: -------------  wolfSSL ESP HW AES Metrics  -------------
I (84760) wolf_hw_aes: --------------------------------------------------------
I (84768) wolf_hw_aes: esp_aes_unsupported_length_usage_ct = 2
I (84775) wolfssl_test: Stack HWM: 7688

I (84779) wolfssl_test: loops = 1
I (84783) wolfssl_test: Stack HWM: 7688
I (84788) wolfssl_test: Stack used: 2812
I (84793) wolfssl_test:

Device: esp32

Exit code: 0

Success!

WOLFSSL_COMPLETE

If running from idf.py monitor, press twice: Ctrl+]

Fixes zd# n/a

Testing

How did you test?

ESP32 with and without HW encryption, with and without WOLFSSL_SMALL_STACK on ESP-IDF v5.2.

./autogen.sh
./configure --enable-smallstack --enable-all
make clean
make -j$(nproc)
./wolfcrypt/test/testwolfcrypt

And

./autogen.sh
./configure --enable-all
make clean
make -j$(nproc)
./wolfcrypt/test/testwolfcrypt

Update:

Tests have been running on my 9-device jig since yesterday. Screen snip of memory shows no memory leak for the default tests running on the 8x ESP32 + ESP8266

image

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

@gojimmypi
Copy link
Contributor Author

Jenkins retest this please

@gojimmypi
Copy link
Contributor Author

Jenkins retest this please.

to retry external tests

wolfSSL_connect error -308, error state on socket
wolfSSL error: wolfSSL_connect failed

@dgarske
Copy link
Contributor

dgarske commented Feb 26, 2025

Retest this please: "ERROR: Cannot delete workspace :Unable to delete"

@dgarske dgarske assigned wolfSSL-Bot and unassigned dgarske Feb 26, 2025
@dgarske dgarske requested review from SparkiDev and removed request for dgarske February 26, 2025 23:15
@gojimmypi gojimmypi requested a review from SparkiDev February 27, 2025 01:24
@dgarske dgarske self-requested a review March 3, 2025 23:18
Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all the changes in this PR needed?

@gojimmypi
Copy link
Contributor Author

gojimmypi commented Mar 5, 2025

Are all the changes in this PR needed?

Yes, I believe so. They are all good tests, even if unlikely to be encountered.

The eyebrow raising code is surrounding the hash algos that are not implemented.

For instance, it is unlikely (but not impossible) that someone might try to call wc_HashInit, wc_HashUpdate, wc_HashFinal on a bad hash value. See details., above.

If you agree with the logic and scope, there's one more check that is needed that I overlooked: what if the first item in typesGood is not implemented? The valid desired placeholder type may not always be the first item in typesGood.

@gojimmypi
Copy link
Contributor Author

Do not run any tests for algorithms that are disabled at build time. Please find a better way to fix this

I plan an update for only compiled-in algorithms and only reference the respective enum values.

For future reference: the original code in question was introduced in 2016.

Specifically commit 4edcbc79c19a32cd8e9c25ce7e76e905b20232e3, via:

git log -S 'for (i = 0, j = 0; i < (int)(sizeof(typesGood)/sizeof(*typesGood)); i++) {' -- wolfcrypt/test/test.c

It is this "if (typesGood[i] == typesNoImpl[j])" functionality that I plan to remove, and which was the root cause of the memory leak in test.c :

image

btw - I've now run thousands of iterations of the wolf_test_task() in a loops on 9 different Espressif devices for many days. No memory leaks, at least for the configuration I'm using.

@gojimmypi gojimmypi marked this pull request as draft March 7, 2025 02:36
@gojimmypi
Copy link
Contributor Author

Hi @dgarske - I've updated the hash_test() in test.c and left as a separate commit for your review.

Tested in a loop on an ESP32 monitoring heap and:

make distclean
./configure --enable-crypttests
make -j$(nproc) wolfcrypt/test/testwolfcrypt
./wolfcrypt/test/testwolfcrypt

@dgarske dgarske removed their assignment Apr 8, 2025
@dgarske dgarske removed the request for review from SparkiDev April 8, 2025 22:48
@dgarske
Copy link
Contributor

dgarske commented Apr 8, 2025

FYI: PRB Test is failing with:

[check-source-text] [3 of 39] [79a5ee50c4]
    autogen.sh 79a5ee50c4...   real 0m5.364s  user 0m4.111s  sys 0m0.286s
    configure...   real 0m8.394s  user 0m5.417s  sys 0m3.431s
unescaped error code operands (missing WC_NO_ERR_TRACE()):
wolfcrypt/test/test.c:6369:        if (exp_ret == HASH_TYPE_E) {

@gojimmypi gojimmypi requested a review from dgarske April 9, 2025 15:23
@gojimmypi gojimmypi assigned dgarske and unassigned dgarske Apr 9, 2025
@dgarske dgarske assigned wolfSSL-Bot and unassigned dgarske Apr 9, 2025
@dgarske dgarske marked this pull request as ready for review April 9, 2025 17:28
@gojimmypi gojimmypi requested a review from dgarske April 9, 2025 19:03
@gojimmypi gojimmypi requested a review from SparkiDev April 10, 2025 12:31
@dgarske
Copy link
Contributor

dgarske commented Apr 11, 2025

Retest this please: "PRB-single-flag.txt_110" "Found unhandled org.jenkinsci.plugins.workflow.support.steps.AgentOfflineException exception:"

@gojimmypi
Copy link
Contributor Author

@dgarske now that this is approved, I can squash the commits.

@dgarske dgarske merged commit 8ee7d38 into wolfSSL:master Apr 11, 2025
185 checks passed
@gojimmypi gojimmypi mentioned this pull request Apr 13, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants