Skip to content

Commit f8675fa

Browse files
committed
Patch eap.o memory leak
WiFi Enterprise option can leak up to 3 allocations per connect/disconnect cycle: anonymous Identity, password, and some unidentified allocation. This solution patches eap.o from libwpa2 to call a special 2 part wrapper instead of vPortFree for cleanup.
1 parent 2de142b commit f8675fa

File tree

12 files changed

+294
-10
lines changed

12 files changed

+294
-10
lines changed

cores/esp8266/coredecls.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ void esp_schedule();
2121
void esp_yield();
2222
void tune_timeshift64 (uint64_t now_us);
2323
void disable_extra4k_at_link_time (void) __attribute__((noinline));
24+
void enable_wifi_enterprise_patch(void) __attribute__((noinline));
2425
bool sntp_set_timezone_in_seconds(int32_t timezone);
2526
void __disableWiFiAtBootTime (void) __attribute__((noinline));
2627
void __real_system_restart_local() __attribute__((noreturn));

cores/esp8266/heap.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@
55

66
#include <stdlib.h>
77
#include "umm_malloc/umm_malloc.h"
8-
extern "C" size_t umm_umul_sat(const size_t a, const size_t b);;
8+
extern "C" size_t umm_umul_sat(const size_t a, const size_t b);
9+
10+
// z2EapFree: See wpa2_eap_patch.cpp for details
11+
extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree"), nothrow));
12+
// I don't understand all the compiler noise around this alias.
13+
// Adding "__attribute__ ((nothrow))" seems to resolve the issue.
14+
// This may be relevant: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81824
915

1016
// Need FORCE_ALWAYS_INLINE to put HeapSelect class constructor/deconstructor in IRAM
1117
#define FORCE_ALWAYS_INLINE_HEAP_SELECT

cores/esp8266/wpa2_eap_patch.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must
3+
* be patched to call `z2EapFree` instead of `vPortFree`. This limits extending
4+
* the execution time of vPortFree to that module only. Not impacting other
5+
* modules.
6+
*
7+
*/
8+
9+
#include <string.h>
10+
#include <ets_sys.h>
11+
#include <pgmspace.h>
12+
#include "coredecls.h"
13+
#if 0
14+
#include "esp8266_undocumented.h"
15+
#define DEBUG_PRINTF ets_uart_printf
16+
#else
17+
#define DEBUG_PRINTF(...)
18+
#endif
19+
20+
extern "C" {
21+
22+
// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree")));
23+
24+
/*
25+
* Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from
26+
* archive `libwpa2.a`.
27+
*
28+
* vPortFree calls from eap.o are monitored for calls from line 799. This is
29+
* the location of the memory leak. At entry register a12 contains the structure
30+
* address which has the addresses of the allocations that will be leaked.
31+
*
32+
* Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a
33+
* 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which
34+
* in turn checks and frees the additional allocations, that would have been
35+
* lost.
36+
*
37+
* extern "C" z2EapFree(void*);
38+
*/
39+
40+
/*
41+
* Part 1 of Limited vPortFree Wrapper
42+
*/
43+
asm(
44+
// ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t"
45+
".section .text.z2EapFree,\"ax\",@progbits\n\t"
46+
".literal_position\n\t"
47+
".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t"
48+
".align 4\n\t"
49+
".global z2EapFree\n\t"
50+
".type z2EapFree, @function\n\t"
51+
"\n"
52+
"z2EapFree:\n\t"
53+
"addi a1, a1, -16\n\t"
54+
"s32i a0, a1, 0\n\t"
55+
"mov a5, a12\n\t"
56+
"l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t"
57+
"callx0 a0\n\t"
58+
"l32i a0, a1, 0\n\t"
59+
"addi a1, a1, 16\n\t"
60+
"ret\n\t"
61+
".size z2EapFree, .-z2EapFree\n\t"
62+
);
63+
64+
/*
65+
* While some insight can be gained from the ESP32 repo for this structure.
66+
* It does not match exactly. This alternate structure focuses on correct offset
67+
* rather than trying to exactly reconstruct the original labels.
68+
*/
69+
struct StateMachine { // size 200 bytes
70+
void* befoeConfig[16];
71+
void* config[26];
72+
// 0 - mov a2, a12, 64 // username / Identity
73+
// 1 - mov a2, a12, 68
74+
// 2 - mov a2, a12, 72 // anonymous Identity
75+
// 3 - mov a2, a12, 76
76+
// 4 - mov a2, a12, 80 // password
77+
// 21 - mov a2, a12, 148 // ??
78+
void* afterConfig[8];
79+
};
80+
81+
/*
82+
* Part 2 of Limited vPortFree Wrapper
83+
*
84+
* Presently, all SDKs have the same memory leaks in the same module at the
85+
* same line.
86+
*/
87+
void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) {
88+
if (799 == line) {
89+
struct StateMachine* sm = (struct StateMachine*)a12;
90+
if (ptr == sm->config[0]) {
91+
// Fix leaky frunction - eap.o only frees one out of 4 config items
92+
// finish the other 3 first
93+
vPortFree(sm->config[2], file, line);
94+
vPortFree(sm->config[4], file, line);
95+
vPortFree(sm->config[21], file, line);
96+
// ptr is sm->config[0], let fall through handle it
97+
}
98+
DEBUG_PRINTF("\nz2EapFree/vPortFree patch working\n");
99+
}
100+
vPortFree(ptr, file, line);
101+
}
102+
103+
};
104+
105+
/*
106+
* This will minimize code space for non-wifi enterprise sketches which do not
107+
* need the patch and disable_extra4k_at_link_time().
108+
*/
109+
void enable_wifi_enterprise_patch(void) {
110+
/*
111+
* Calling this from setup or anywhere ensures that the patch code is
112+
* included in the build.
113+
*
114+
* Also, WiFi Enterprise uses a lot of system stack space and may crash
115+
* unless we:
116+
*/
117+
disable_extra4k_at_link_time();
118+
}

tools/sdk/lib/NONOSDK221/libwpa2.a

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

tools/sdk/lib/NONOSDK3V0/libwpa2.a

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)