Skip to content

Commit 2f43807

Browse files
earlephilhowerdevyte
authored andcommitted
Move BearSSL from STACK_PROXY to a real, thunked 2nd stack (#5168)
* Update to BearSSL 0.6+ release, add AES_CCM modes Pull in latest BearSSL head (0.6 + minor additions) release and add AES_CCM modes to the encryption options. * Enable the aes_ccm initialization in client/server * Initial attempt * Working code with second stack thunking * Remove #ifdefs in .S file, not needed. * Clean up thunks and remove separate stack flag * Fix PIO assembler errors * Remove #ifdef code changes, ensure same code as PC Remove "#ifdef ESP8266;...;#else;...;#endif" brackets in BearSSL to ensure the host-tested code is the same as the ESP8266-run code. * Move to latest BearSSL w/EC progmem savings * Merge with master * Add br_thunk_* calls to do ref counting, painting Add reference counting br_thunk_add/del_ref() to replace stack handling code in the class. Add in stack painting and max usage calculation. * Add in postmortem stack dump hooks When a crash occurs while in the second stack, dump the BSSL stack and then also the stack that it was called from (either cont or sys). * Update stack dump to match decoder expectations * Move thunk to code core for linkiage The thunk code needs to be visible to the core routines, so move it to the cores/esp8266 directory. Probably need to refactor the stack setup and the bearssl portion to avoid dependency on bearssl libs in cores/esp8266 * Add 2nd stack dump utility routine * Refactor once more, update stack size, add stress Make stack_thunks generic, remove bearssl include inside of cores/esp8266. Allocate the stack on a WiFiServerSecure object creation to avoid fragmentation since we will need to allocate the stack to do any connected work, anyway. A stress test is now included which checks the total BearSSL second stack usage for a variety of TLS handshake and certificate options from badssl.org. * Update to latest to-thunks branch * Add BearSSL device test using stack stress Run a series of SSL connection and transmission tests that stress BearSSL and its stack usage to the device tests. Modify device tests to include a possible SPIFFS generation and upload when a make_spiffs.py file is present in a test directory. * Use bearssl/master branch, not /to-thunks branch Update to use the merged master branch of bearssl. Should have no code changes.
1 parent 41de43a commit 2f43807

17 files changed

+563
-41
lines changed

cores/esp8266/StackThunk.c

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
StackThunk.c - Allow use second stack for BearSSL calls
3+
4+
BearSSL uses a significant amount of stack space, much larger than
5+
the default Arduino core stack. These routines handle swapping
6+
between a secondary, user-allocated stack on the heap and the real
7+
stack.
8+
9+
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved.
10+
11+
This library is free software; you can redistribute it and/or
12+
modify it under the terms of the GNU Lesser General Public
13+
License as published by the Free Software Foundation; either
14+
version 2.1 of the License, or (at your option) any later version.
15+
16+
This library is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public
22+
License along with this library; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
25+
*/
26+
27+
#include <stdint.h>
28+
#include <stdlib.h>
29+
#include "StackThunk.h"
30+
31+
uint32_t *stack_thunk_ptr = NULL;
32+
uint32_t *stack_thunk_top = NULL;
33+
uint32_t *stack_thunk_save = NULL; /* Saved A1 while in BearSSL */
34+
uint32_t stack_thunk_refcnt = 0;
35+
36+
#define _stackSize (5600/4)
37+
#define _stackPaint 0xdeadbeef
38+
39+
/* Add a reference, and allocate the stack if necessary */
40+
void stack_thunk_add_ref()
41+
{
42+
stack_thunk_refcnt++;
43+
if (stack_thunk_refcnt == 1) {
44+
stack_thunk_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t));
45+
stack_thunk_top = stack_thunk_ptr + _stackSize - 1;
46+
stack_thunk_save = NULL;
47+
stack_thunk_repaint();
48+
}
49+
}
50+
51+
/* Drop a reference, and free stack if no more in use */
52+
void stack_thunk_del_ref()
53+
{
54+
if (stack_thunk_refcnt == 0) {
55+
/* Error! */
56+
return;
57+
}
58+
stack_thunk_refcnt--;
59+
if (!stack_thunk_refcnt) {
60+
free(stack_thunk_ptr);
61+
stack_thunk_ptr = NULL;
62+
stack_thunk_top = NULL;
63+
stack_thunk_save = NULL;
64+
}
65+
}
66+
67+
void stack_thunk_repaint()
68+
{
69+
for (int i=0; i < _stackSize; i++) {
70+
stack_thunk_ptr[i] = _stackPaint;
71+
}
72+
}
73+
74+
/* Simple accessor functions used by postmortem */
75+
uint32_t stack_thunk_get_refcnt() {
76+
return stack_thunk_refcnt;
77+
}
78+
79+
uint32_t stack_thunk_get_stack_top() {
80+
return (uint32_t)stack_thunk_top;
81+
}
82+
83+
uint32_t stack_thunk_get_stack_bot() {
84+
return (uint32_t)stack_thunk_ptr;
85+
}
86+
87+
uint32_t stack_thunk_get_cont_sp() {
88+
return (uint32_t)stack_thunk_save;
89+
}
90+
91+
/* Return the number of bytes ever used since the stack was created */
92+
uint32_t stack_thunk_get_max_usage()
93+
{
94+
uint32_t cnt = 0;
95+
96+
/* No stack == no usage by definition! */
97+
if (!stack_thunk_ptr) {
98+
return 0;
99+
}
100+
101+
for (cnt=0; (cnt < _stackSize) && (stack_thunk_ptr[cnt] == _stackPaint); cnt++) {
102+
/* Noop, all work done in for() */
103+
}
104+
return 4 * (_stackSize - cnt);
105+
}
106+
107+
/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */
108+
void stack_thunk_dump_stack()
109+
{
110+
uint32_t *pos = stack_thunk_top;
111+
while (pos < stack_thunk_ptr) {
112+
if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint))
113+
break;
114+
pos += 4;
115+
}
116+
ets_printf(">>>stack>>>\n");
117+
while (pos < stack_thunk_ptr) {
118+
ets_printf("%08x: %08x %08x %08x %08x\n", pos, pos[0], pos[1], pos[2], pos[3]);
119+
pos += 4;
120+
}
121+
ets_printf("<<<stack<<<\n");
122+
}

cores/esp8266/StackThunk.h

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
StackThunk.h - Allow use second stack for BearSSL calls
3+
4+
BearSSL uses a significant amount of stack space, much larger than
5+
the default Arduino core stack. These routines handle swapping
6+
between a secondary, user-allocated stack on the heap and the real
7+
stack.
8+
9+
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved.
10+
11+
This library is free software; you can redistribute it and/or
12+
modify it under the terms of the GNU Lesser General Public
13+
License as published by the Free Software Foundation; either
14+
version 2.1 of the License, or (at your option) any later version.
15+
16+
This library is distributed in the hope that it will be useful,
17+
but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19+
Lesser General Public License for more details.
20+
21+
You should have received a copy of the GNU Lesser General Public
22+
License along with this library; if not, write to the Free Software
23+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24+
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
25+
*/
26+
27+
#ifndef _STACKTHUNK_H
28+
#define _STACKTHUNK_H
29+
30+
#ifdef __cplusplus
31+
extern "C" {
32+
#endif
33+
34+
extern void stack_thunk_add_ref();
35+
extern void stack_thunk_del_ref();
36+
extern void stack_thunk_repaint();
37+
38+
extern uint32_t stack_thunk_get_refcnt();
39+
extern uint32_t stack_thunk_get_stack_top();
40+
extern uint32_t stack_thunk_get_stack_bot();
41+
extern uint32_t stack_thunk_get_cont_sp();
42+
extern uint32_t stack_thunk_get_max_usage();
43+
extern void stack_thunk_dump_stack();
44+
45+
// Globals required for thunking operation
46+
extern uint32_t *stack_thunk_ptr;
47+
extern uint32_t *stack_thunk_top;
48+
extern uint32_t *stack_thunk_save;
49+
extern uint32_t stack_thunk_refcnt;
50+
51+
// Thunking macro
52+
#define make_stack_thunk(fcnToThunk) \
53+
__asm("\n\
54+
.text\n\
55+
.literal_position\n\
56+
\n\
57+
.text\n\
58+
.global thunk_"#fcnToThunk"\n\
59+
.type thunk_"#fcnToThunk", @function\n\
60+
.align 4\n\
61+
thunk_"#fcnToThunk":\n\
62+
addi a1, a1, -16 /* Allocate space for saved registers on stack */\n\
63+
s32i a0, a1, 12 /* Store A0, trounced by calls */\n\
64+
s32i a15, a1, 8 /* Store A15 (our temporary one) */\n\
65+
movi a15, stack_thunk_save /* Store A1(SP) in temp space */\n\
66+
s32i a1, a15, 0\n\
67+
movi a15, stack_thunk_top /* Load A1(SP) with thunk stack */\n\
68+
l32i.n a1, a15, 0\n\
69+
call0 "#fcnToThunk" /* Do the call */\n\
70+
movi a15, stack_thunk_save /* Restore A1(SP) */\n\
71+
l32i.n a1, a15, 0\n\
72+
l32i.n a15, a1, 8 /* Restore the saved registers */\n\
73+
l32i.n a0, a1, 12\n\
74+
addi a1, a1, 16 /* Free up stack and return to caller */\n\
75+
ret\n\
76+
.size thunk_"#fcnToThunk", . - thunk_"#fcnToThunk"\n");
77+
78+
#ifdef __cplusplus
79+
}
80+
#endif
81+
82+
#endif

cores/esp8266/core_esp8266_postmortem.c

+14-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "cont.h"
3030
#include "pgmspace.h"
3131
#include "gdb_hooks.h"
32+
#include "StackThunk.h"
3233

3334
extern void __real_system_restart_local();
3435

@@ -147,6 +148,17 @@ void __wrap_system_restart_local() {
147148
offset = 0x10;
148149
}
149150

151+
ets_printf_P("\n>>>stack>>>\n");
152+
153+
if (sp > stack_thunk_get_stack_bot() && sp <= stack_thunk_get_stack_top()) {
154+
// BearSSL we dump the BSSL second stack and then reset SP back to the main cont stack
155+
ets_printf_P("\nctx: bearssl \n");
156+
ets_printf_P("sp: %08x end: %08x offset: %04x\n", sp, stack_thunk_get_stack_top(), offset);
157+
print_stack(sp + offset, stack_thunk_get_stack_top());
158+
offset = 0; // No offset needed anymore, the exception info was stored in the bssl stack
159+
sp = stack_thunk_get_cont_sp();
160+
}
161+
150162
if (sp > cont_stack_start && sp < cont_stack_end) {
151163
ets_printf_P("\nctx: cont \n");
152164
stack_end = cont_stack_end;
@@ -162,6 +174,8 @@ void __wrap_system_restart_local() {
162174

163175
print_stack(sp + offset, stack_end);
164176

177+
ets_printf_P("<<<stack<<<\n");
178+
165179
// Use cap-X formatting to ensure the standard EspExceptionDecoder doesn't match the address
166180
if (umm_last_fail_alloc_addr) {
167181
ets_printf_P("\nlast failed alloc call: %08X(%d)\n", (uint32_t)umm_last_fail_alloc_addr, umm_last_fail_alloc_size);
@@ -175,7 +189,6 @@ void __wrap_system_restart_local() {
175189

176190

177191
static void ICACHE_RAM_ATTR print_stack(uint32_t start, uint32_t end) {
178-
ets_printf_P("\n>>>stack>>>\n");
179192
for (uint32_t pos = start; pos < end; pos += 0x10) {
180193
uint32_t* values = (uint32_t*)(pos);
181194

@@ -185,7 +198,6 @@ static void ICACHE_RAM_ATTR print_stack(uint32_t start, uint32_t end) {
185198
ets_printf_P("%08x: %08x %08x %08x %08x %c\n",
186199
pos, values[0], values[1], values[2], values[3], (looksLikeStackFrame)?'<':' ');
187200
}
188-
ets_printf_P("<<<stack<<<\n");
189201
}
190202

191203
static void uart_write_char_d(char c) {

libraries/ESP8266WiFi/src/BearSSLHelpers.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <stdlib.h>
2828
#include <string.h>
2929
#include <Arduino.h>
30+
#include <StackThunk.h>
3031
#include "BearSSLHelpers.h"
3132

3233
namespace brssl {
@@ -825,5 +826,14 @@ bool X509List::append(const uint8_t *derCert, size_t derLen) {
825826
return true;
826827
}
827828

828-
};
829-
829+
// Second stack thunked helpers
830+
make_stack_thunk(br_ssl_engine_recvapp_ack);
831+
make_stack_thunk(br_ssl_engine_recvapp_buf);
832+
make_stack_thunk(br_ssl_engine_recvrec_ack);
833+
make_stack_thunk(br_ssl_engine_recvrec_buf);
834+
make_stack_thunk(br_ssl_engine_sendapp_ack);
835+
make_stack_thunk(br_ssl_engine_sendapp_buf);
836+
make_stack_thunk(br_ssl_engine_sendrec_ack);
837+
make_stack_thunk(br_ssl_engine_sendrec_buf);
838+
839+
};

libraries/ESP8266WiFi/src/BearSSLHelpers.h

+12
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ class Session {
136136
br_ssl_session_parameters _session;
137137
};
138138

139+
// Stack thunked versions of calls
140+
extern "C" {
141+
extern unsigned char *thunk_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);
142+
extern void thunk_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len);
143+
extern unsigned char *thunk_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len);
144+
extern void thunk_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len);
145+
extern unsigned char *thunk_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len);
146+
extern void thunk_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);
147+
extern unsigned char *thunk_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len);
148+
extern void thunk_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len);
149+
};
150+
139151
};
140152

141153
#endif

libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp

+16-26
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern "C" {
3434
#include "ESP8266WiFi.h"
3535
#include "WiFiClient.h"
3636
#include "WiFiClientSecureBearSSL.h"
37+
#include "StackThunk.h"
3738
#include "lwip/opt.h"
3839
#include "lwip/ip.h"
3940
#include "lwip/tcp.h"
@@ -43,14 +44,17 @@ extern "C" {
4344
#include "c_types.h"
4445
#include "coredecls.h"
4546

46-
namespace BearSSL {
47-
48-
// BearSSL needs a very large stack, larger than the entire ESP8266 Arduino
49-
// default one. This shared_pointer is allocated on first use and cleared
50-
// on last cleanup, with only one stack no matter how many SSL objects.
51-
std::shared_ptr<uint8_t> WiFiClientSecure::_bearssl_stack = nullptr;
52-
47+
// The BearSSL thunks in use for now
48+
#define br_ssl_engine_recvapp_ack thunk_br_ssl_engine_recvapp_ack
49+
#define br_ssl_engine_recvapp_buf thunk_br_ssl_engine_recvapp_buf
50+
#define br_ssl_engine_recvrec_ack thunk_br_ssl_engine_recvrec_ack
51+
#define br_ssl_engine_recvrec_buf thunk_br_ssl_engine_recvrec_buf
52+
#define br_ssl_engine_sendapp_ack thunk_br_ssl_engine_sendapp_ack
53+
#define br_ssl_engine_sendapp_buf thunk_br_ssl_engine_sendapp_buf
54+
#define br_ssl_engine_sendrec_ack thunk_br_ssl_engine_sendrec_ack
55+
#define br_ssl_engine_sendrec_buf thunk_br_ssl_engine_sendrec_buf
5356

57+
namespace BearSSL {
5458

5559
void WiFiClientSecure::_clear() {
5660
// TLS handshake may take more than the 5 second default timeout
@@ -91,16 +95,7 @@ WiFiClientSecure::WiFiClientSecure() : WiFiClient() {
9195
_clear();
9296
_clearAuthenticationSettings();
9397
_certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived
94-
_ensureStackAvailable();
95-
_local_bearssl_stack = _bearssl_stack;
96-
}
97-
98-
void WiFiClientSecure::_ensureStackAvailable() {
99-
if (!_bearssl_stack) {
100-
const int stacksize = 4500; // Empirically determined stack for EC and RSA connections
101-
_bearssl_stack = std::shared_ptr<uint8_t>(new uint8_t[stacksize], std::default_delete<uint8_t[]>());
102-
br_esp8266_stack_proxy_init(_bearssl_stack.get(), stacksize);
103-
}
98+
stack_thunk_add_ref();
10499
}
105100

106101
WiFiClientSecure::~WiFiClientSecure() {
@@ -110,11 +105,8 @@ WiFiClientSecure::~WiFiClientSecure() {
110105
}
111106
free(_cipher_list);
112107
_freeSSL();
113-
_local_bearssl_stack = nullptr;
114-
// If there are no other uses than the initial creation, free the stack
115-
if (_bearssl_stack.use_count() == 1) {
116-
_bearssl_stack = nullptr;
117-
}
108+
// Serial.printf("Max stack usage: %d bytes\n", br_thunk_get_max_usage());
109+
stack_thunk_del_ref();
118110
if (_deleteChainKeyTA) {
119111
delete _ta;
120112
delete _chain;
@@ -127,8 +119,7 @@ WiFiClientSecure::WiFiClientSecure(ClientContext* client,
127119
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) {
128120
_clear();
129121
_clearAuthenticationSettings();
130-
_ensureStackAvailable();
131-
_local_bearssl_stack = _bearssl_stack;
122+
stack_thunk_add_ref();
132123
_iobuf_in_size = iobuf_in_size;
133124
_iobuf_out_size = iobuf_out_size;
134125
_client = client;
@@ -146,8 +137,7 @@ WiFiClientSecure::WiFiClientSecure(ClientContext *client,
146137
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) {
147138
_clear();
148139
_clearAuthenticationSettings();
149-
_ensureStackAvailable();
150-
_local_bearssl_stack = _bearssl_stack;
140+
stack_thunk_add_ref();
151141
_iobuf_in_size = iobuf_in_size;
152142
_iobuf_out_size = iobuf_out_size;
153143
_client = client;

0 commit comments

Comments
 (0)