-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
2de142b
commit 61b2e7b
Showing
12 changed files
with
290 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* | ||
* To implement this patch, the SDK module `eap.o` from archive `libwpa2.a` must | ||
* be patched to call `z2EapFree` instead of `vPortFree`. This limits extending | ||
* the execution time of vPortFree to that module only. Not impacting other | ||
* modules. | ||
* | ||
*/ | ||
|
||
#include <string.h> | ||
#include <ets_sys.h> | ||
#include <pgmspace.h> | ||
#include "coredecls.h" | ||
#if 0 | ||
#include "esp8266_undocumented.h" | ||
#define DEBUG_PRINTF ets_uart_printf | ||
#else | ||
#define DEBUG_PRINTF(...) | ||
#endif | ||
|
||
extern "C" { | ||
|
||
// extern "C" void z2EapFree(void *ptr, const char* file, int line) __attribute__((weak, alias("vPortFree"))); | ||
|
||
/* | ||
* Limited 2-part wrapper for vPortFree calls made in SDK module `eap.o` from | ||
* archive `libwpa2.a`. | ||
* | ||
* vPortFree calls from eap.o are monitored for calls from line 799. This is | ||
* the location of the memory leak. At entry register a12 contains the structure | ||
* address which has the addresses of the allocations that will be leaked. | ||
* | ||
* Part 1 of this wrapper, z2EapFree, appends the value of register a12 as a | ||
* 4th argument to part2 of this wrapper, patch_wpa2_eap_vPortFree_a12(). Which | ||
* in turn checks and frees the additional allocations, that would have been | ||
* lost. | ||
* | ||
* extern "C" z2EapFree(void*); | ||
*/ | ||
|
||
/* | ||
* Part 1 of Limited vPortFree Wrapper | ||
*/ | ||
asm( | ||
// ".section .iram.text.z2EapFree,\"ax\",@progbits\n\t" | ||
".section .text.z2EapFree,\"ax\",@progbits\n\t" | ||
".literal_position\n\t" | ||
".literal .patch_wpa2_eap_vPortFree_a12, patch_wpa2_eap_vPortFree_a12\n\t" | ||
".align 4\n\t" | ||
".global z2EapFree\n\t" | ||
".type z2EapFree, @function\n\t" | ||
"\n" | ||
"z2EapFree:\n\t" | ||
"addi a1, a1, -16\n\t" | ||
"s32i a0, a1, 0\n\t" | ||
"mov a5, a12\n\t" | ||
"l32r a0, .patch_wpa2_eap_vPortFree_a12\n\t" | ||
"callx0 a0\n\t" | ||
"l32i a0, a1, 0\n\t" | ||
"addi a1, a1, 16\n\t" | ||
"ret\n\t" | ||
".size z2EapFree, .-z2EapFree\n\t" | ||
); | ||
|
||
/* | ||
* While some insight can be gained from the ESP32 repo for this structure. | ||
* It does not match exactly. This alternate structure focuses on correct offset | ||
* rather than trying to exactly reconstruct the original labels. | ||
*/ | ||
struct StateMachine { // size 200 bytes | ||
void* befoeConfig[16]; | ||
void* config[26]; | ||
// 0 - mov a2, a12, 64 // username / Identity | ||
// 1 - mov a2, a12, 68 | ||
// 2 - mov a2, a12, 72 // anonymous Identity | ||
// 3 - mov a2, a12, 76 | ||
// 4 - mov a2, a12, 80 // password | ||
// 21 - mov a2, a12, 148 // ?? | ||
void* afterConfig[8]; | ||
}; | ||
|
||
/* | ||
* Part 2 of Limited vPortFree Wrapper | ||
* | ||
* Presently, all SDKs have the same memory leaks in the same module at the | ||
* same line. | ||
*/ | ||
void patch_wpa2_eap_vPortFree_a12(void *ptr, const char* file, int line, void* a12) { | ||
if (799 == line) { | ||
struct StateMachine* sm = (struct StateMachine*)a12; | ||
if (ptr == sm->config[0]) { | ||
// Fix leaky frunction - eap.o only frees one out of 4 config items | ||
// finish the other 3 first | ||
vPortFree(sm->config[2], file, line); | ||
vPortFree(sm->config[4], file, line); | ||
vPortFree(sm->config[21], file, line); | ||
// ptr is sm->config[0], let fall through handle it | ||
} | ||
DEBUG_PRINTF("\nz2EapFree/vPortFree patch working\n"); | ||
} | ||
vPortFree(ptr, file, line); | ||
} | ||
|
||
}; | ||
|
||
/* | ||
* This will minimize code space for non-wifi enterprise sketches which do not | ||
* need the patch and disable_extra4k_at_link_time(). | ||
*/ | ||
void enable_wifi_enterprise_patch(void) { | ||
/* | ||
* Calling this from setup or anywhere ensures that the patch code is | ||
* included in the build. | ||
* | ||
* Also, WiFi Enterprise uses a lot of system stack space and may crash | ||
* unless we: | ||
*/ | ||
disable_extra4k_at_link_time(); | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#!/bin/bash | ||
# set -e | ||
|
||
add_path_ifexist() { | ||
if [[ -d $1 ]]; then | ||
export PATH=$( realpath $1 ):$PATH | ||
return 0 | ||
fi | ||
return 1 | ||
} | ||
|
||
if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then | ||
add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" | ||
fi | ||
|
||
|
||
list_sdks() { | ||
cat <<EOF | ||
NONOSDK22x_190313 | ||
NONOSDK22x_190703 | ||
NONOSDK22x_191024 | ||
NONOSDK22x_191105 | ||
NONOSDK22x_191122 | ||
NONOSDK221 | ||
NONOSDK3V0 | ||
EOF | ||
} | ||
|
||
remove_ifexist() { | ||
[[ -f $1 ]] && rm $1 | ||
} | ||
|
||
cleanup() { | ||
remove_ifexist old.txt | ||
remove_ifexist old2.txt | ||
remove_ifexist new.txt | ||
for sdk in `list_sdks`; do | ||
remove_ifexist $sdk/eap/o | ||
done | ||
} | ||
|
||
unasm() { | ||
xtensa-lx106-elf-objdump -d $* | ||
} | ||
|
||
analyze() { | ||
cleanup | ||
|
||
for sdk in `list_sdks`; do | ||
pushd $sdk | ||
xtensa-lx106-elf-ar x libwpa2.a eap.o | ||
popd | ||
done | ||
echo "" | ||
|
||
find . -name eap.o -exec md5sum {} \; | sort | ||
echo "" | ||
|
||
unset prev_sdk | ||
for sdk in `list_sdks`; do | ||
unasm -j ".text.eap_peer_config_deinit" ${sdk}/eap.o >new.txt | ||
if [[ -f old.txt ]]; then | ||
echo "eap_peer_config_deinit: diff $prev_sdk $sdk" | ||
diff old.txt new.txt | ||
echo "" | ||
fi | ||
mv new.txt old.txt | ||
prev_sdk=${sdk} | ||
done | ||
|
||
unset prev_sdk | ||
for sdk in `list_sdks`; do | ||
unasm -j ".text.wpa2_sm_rx_eapol" ${sdk}/eap.o >new.txt | ||
if [[ -f old2.txt ]]; then | ||
echo "wpa2_sm_rx_eapol: diff $prev_sdk $sdk" | ||
diff old2.txt new.txt | ||
echo "" | ||
fi | ||
mv new.txt old2.txt | ||
prev_sdk=${sdk} | ||
done | ||
|
||
cleanup | ||
} | ||
|
||
|
||
patch_all() { | ||
for sdk in `list_sdks`; do | ||
pushd $sdk | ||
../fix_sdk_libs.sh | ||
popd | ||
done | ||
} | ||
|
||
# analyze | ||
patch_all | ||
exit 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,98 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
export PATH=../../xtensa-lx106-elf/bin:$PATH | ||
add_path_ifexist() { | ||
if [[ -d $1 ]]; then | ||
export PATH=$( realpath $1 ):$PATH | ||
return 0 | ||
fi | ||
return 1 | ||
} | ||
|
||
if ! which xtensa-lx106-elf-ar | grep "tools/xtensa-lx106-elf/bin" >>/dev/null; then | ||
add_path_ifexist "../../../xtensa-lx106-elf/bin" || add_path_ifexist "../../xtensa-lx106-elf/bin" | ||
fi | ||
WORK_SPACE=${PWD} | ||
|
||
VERSION=$(basename ${PWD}) | ||
|
||
addSymbol_system_func1() { | ||
ADDRESS=$1 | ||
xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o | ||
if ! xtensa-lx106-elf-nm user_interface.o | grep -q " T system_func1"; then # Don't add symbol if it already exists | ||
ADDRESS=$1 | ||
xtensa-lx106-elf-objcopy --add-symbol system_func1=.irom0.text:${ADDRESS},function,global user_interface.o | ||
fi | ||
} | ||
|
||
patchFile() { | ||
FILE=$1 | ||
ADDRESS=$2 # DO NOT PASS AS HEX! | ||
LENGTH=$3 # DO NOT PASS AS HEX! | ||
EXPECTED=$4 | ||
REPLACEWITH=$5 | ||
if [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$EXPECTED" ]]; then | ||
echo "Patching $VERSION $1 ..." | ||
echo $5 | base64 -d | dd of=$FILE bs=1 count=$LENGTH seek=$ADDRESS conv=notrunc | ||
elif ! [[ "$(dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0)" = "$REPLACEWITH" ]]; then | ||
echo "PATCH FAILED!" | ||
echo "dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0" | ||
dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | hexdump -C | ||
dd if=$FILE bs=1 count=$LENGTH skip=$ADDRESS status=none | base64 -w0 | ||
echo "" | ||
exit 0 | ||
fi | ||
} | ||
|
||
# # xtensa-lx106-elf-ar x libwpa2.a eap.o | ||
if [[ "--shell" == "$1" ]]; then | ||
# need to poke around a bit | ||
bash --rcfile <(echo '. ~/.bashrc; cd ${WORK_SPACE}') | ||
exit 0 | ||
fi | ||
|
||
if [[ ! -f libmain.a ]]; then | ||
echo -e "\n\n*** Archive libmain.a is missing ***\n\n" | ||
exit 1 | ||
fi | ||
|
||
# Remove mem_manager.o from libmain.a to use custom heap implementation, | ||
# and time.o to fix redefinition of time-related functions: | ||
xtensa-lx106-elf-ar d libmain.a mem_manager.o | ||
xtensa-lx106-elf-ar d libmain.a time.o | ||
|
||
# Patch WPA2-Enterprise double-free | ||
xtensa-lx106-elf-ar x libwpa2.a eap.o | ||
eapcs=$(sha256sum eap.o | awk '{print $1}') | ||
|
||
# Rename `hostname` and `default_hostname` symbols: | ||
xtensa-lx106-elf-ar x libmain.a eagle_lwip_if.o user_interface.o | ||
xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o | ||
xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o | ||
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o | ||
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o | ||
lwipcs=$(sha256sum eagle_lwip_if.o | awk '{print $1}') | ||
uics=$(sha256sum user_interface.o | awk '{print $1}') | ||
xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname user_interface.o | ||
xtensa-lx106-elf-objcopy --redefine-sym hostname=wifi_station_hostname eagle_lwip_if.o | ||
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname user_interface.o | ||
xtensa-lx106-elf-objcopy --redefine-sym default_hostname=wifi_station_default_hostname eagle_lwip_if.o | ||
|
||
|
||
if [[ ${VERSION} == "NONOSDK221" ]]; then | ||
addSymbol_system_func1 "0x60" | ||
patchFile "eap.o" "3055" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 | ||
patchFile "eap.o" "26352" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory | ||
elif [[ ${VERSION} == "NONOSDK22x"* ]]; then | ||
addSymbol_system_func1 "0x54" | ||
patchFile "eap.o" "3059" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 | ||
patchFile "eap.o" "26356" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory | ||
elif [[ ${VERSION} == "NONOSDK3"* ]]; then | ||
addSymbol_system_func1 "0x60" | ||
patchFile "eap.o" "3059" "2" "wAA=" "8CA=" # WPA2-Enterprise patch which replaces a double-free with nop, see #8082 | ||
patchFile "eap.o" "26356" "9" "dlBvcnRGcmVl" "ejJFYXBGcmVl" # special vPortFree to recover leaked memory | ||
else | ||
echo "WARN: Unknown address for system_func1() called by system_restart_local()" | ||
fi | ||
|
||
xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o | ||
rm -f eagle_lwip_if.o user_interface.o | ||
if [[ $(sha256sum eap.o | awk '{print $1}') != $eapcs ]]; then | ||
xtensa-lx106-elf-ar r libwpa2.a eap.o | ||
fi | ||
if [[ $(sha256sum user_interface.o | awk '{print $1}') != $uics || $(sha256sum eagle_lwip_if.o | awk '{print $1}') != $lwipcs ]]; then | ||
xtensa-lx106-elf-ar r libmain.a eagle_lwip_if.o user_interface.o | ||
fi | ||
rm -f eagle_lwip_if.o user_interface.o eap.o |