Crash when closing socket with unconsumed input after network is closed
When closing TCP and UDP sockets on the ESP32, we call pbuf_free if we hold onto network packets that we got from the underlying network layer (like wifi). This in return calls back into the network layer, but if the specific network has been closed before we get to this point, this crashes:
Guru Meditation Error: Core 1 panic'ed (InstrFetchProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x00000000 PS : 0x00060730 A0 : 0x8012564c A1 : 0x3ffd4a80
A2 : 0x3ffd6a50 A3 : 0x3ffea100 A4 : 0x3ffd4a90 A5 : 0x00000000
A6 : 0x3ffb682c A7 : 0x00000001 A8 : 0x801b87f8 A9 : 0x3ffd4a40
A10 : 0x00000000 A11 : 0x3ffea100 A12 : 0x00000000 A13 : 0x00000000
A14 : 0x3ffd39fc A15 : 0x3ffd39a8 SAR : 0x00000008 EXCCAUSE: 0x00000014
EXCVADDR: 0x00000000 LBEG : 0x400989e5 LEND : 0x400989f5 LCOUNT : 0xfffffff9
******************************************************************************
Decoding by `jag`, device has version <2.0.0-alpha.50>
******************************************************************************
Crash in native code:
Backtrace:0xfffffffd:0x3ffd4a800x40125649:0x3ffd4aa0 0x4011938d:0x3ffd4ac0 0x400dd4a8:0x3ffd4ae0 0x400dd579:0x3ffd4b00 0x400e2ebb:0x3ffd4b20 0x40118375:0x3ffd4b50
0xfffffffd: .rtc.force_slow + 0xafffef7d
0x4011938d: pbuf_free + 0x61
0x400dd4a8: toit::LwipSocket::tear_down() + 0x60
0x400dd579: std::_Function_handler<toit::Object* (), toit::SocketResourceGroup::on_unregister_resource(toit::Resource*)::{lambda()#1}>::_M_invoke(std::_Any_data const&) + 0x9
0x400e2ebb: toit::LwipEventSource::on_thread(void*) + 0x13
0x40118375: tcpip_thread + 0xa9
******************************************************************************
The behavior can be reproduced by running this code on the device:
import net
import net.tcp
main:
network := net.open
server network
server network/net.Interface:
server_socket/tcp.ServerSocket? := null
socket/tcp.Socket? := null
try:
server_socket = network.tcp_listen 9003
address := "$network.address:$server_socket.local_address.port"
print_ "listening on $address"
while true:
socket = server_socket.accept
if not socket: continue
// Give the client a chance to send something to us
// that we do not read before closing the network.
sleep --ms=1_000
break
finally:
print_ "closing network"
network.close
print_ "closing server-side sockets"
if server_socket: server_socket.close
if socket: socket.close
and connecting to it from another device or machine using something like this:
import net
import net.tcp
main:
network := net.open
socket/tcp.Socket? := null
try:
socket = network.tcp_connect "192.168.86.31" 9003
socket.write "wazzup"
sleep --ms=200
finally:
print_ "closing client-side sockets"
if socket: socket.close
print_ "closing network"
network.close
The crash happens when closing the accepted socket on the device (server side), which happens after we've closed the network.
It should be possible to come up with a similar reproduction case for UDP, but there the problematic pbufs are the ones hanging off of the queued and spare packets.