Skip to content

Commit 515e006

Browse files
committed
Better handle TCP/IP stack crashes in the NETAPI compartment.
When the TCP/IP stack crashes, API calls to the compartment return `-ECOMPARTMENTFAIL`. Currently `-ECOMPARTMENTFAIL` failures are not considered by the NetAPI compartment. This leads to erratic behavior when the network stack crashes, such as adding nonsensical entries to the firewall. Unfortunately these checks are really tricky to add in the case of `socket_network_kind` and `network_host_resolve` because they return structs. This commit reworks the API so that these functions instead take a pointer to a struct, and return an integer error code. Building on this, add all the necessary checks. Signed-off-by: Hugo Lefeuvre <[email protected]>
1 parent ef3fe19 commit 515e006

File tree

3 files changed

+86
-37
lines changed

3 files changed

+86
-37
lines changed

lib/netapi/NetAPI.cc

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,37 @@ SObj network_socket_connect_tcp(Timeout *timeout,
5555
Debug::log("Host capability does not authorise a TCP connection");
5656
return nullptr;
5757
}
58+
59+
NetworkAddress address{NetworkAddress::AddressKindInvalid};
60+
CHERI::Capability addressPtr = &address;
61+
addressPtr.permissions() &= {CHERI::Permission::Store};
5862
firewall_permit_dns();
59-
NetworkAddress address = network_host_resolve(host->hostname, UseIPv6);
63+
int ret = network_host_resolve(host->hostname, UseIPv6, addressPtr);
6064
firewall_permit_dns(false);
61-
if (address.kind == NetworkAddress::AddressKindInvalid)
65+
if ((ret < 0) || (address.kind == NetworkAddress::AddressKindInvalid))
6266
{
6367
Debug::log("Failed to resolve host");
6468
return nullptr;
6569
}
66-
bool isIPv6 = address.kind == NetworkAddress::AddressKindIPv6;
67-
auto sealedSocket = network_socket_create_and_bind(
70+
bool isIPv6 = address.kind == NetworkAddress::AddressKindIPv6;
71+
72+
CHERI::Capability sealedSocket = network_socket_create_and_bind(
6873
timeout, mallocCapability, isIPv6, ConnectionTypeTCP);
69-
auto kind = network_socket_kind(sealedSocket);
74+
if (!sealedSocket.is_valid())
75+
{
76+
Debug::log("Failed to create socket");
77+
return nullptr;
78+
}
79+
80+
SocketKind kind;
81+
CHERI::Capability kindPtr = &kind;
82+
kindPtr.permissions() &= {CHERI::Permission::Store};
83+
if (network_socket_kind(sealedSocket, kindPtr) < 0)
84+
{
85+
Debug::log("Failed to retrieve socket kind");
86+
return nullptr;
87+
}
88+
7089
// FIXME: IPv6
7190
if (isIPv6)
7291
{
@@ -78,10 +97,13 @@ SObj network_socket_connect_tcp(Timeout *timeout,
7897
firewall_add_tcpipv4_endpoint(
7998
address.ipv4, kind.localPort, ntohs(host->port));
8099
}
100+
81101
if (network_socket_connect_tcp_internal(
82102
timeout, sealedSocket, address, host->port) != 0)
83103
{
84104
Timeout t{UnlimitedTimeout};
105+
// We pass an unlimited timeout, so this cannot fail in any
106+
// actionable manner. Don't check the return value.
85107
network_socket_close(&t, mallocCapability, sealedSocket);
86108
timeout->elapse(t.elapsed);
87109
if (isIPv6)
@@ -118,7 +140,14 @@ NetworkAddress network_socket_udp_authorise_host(Timeout *timeout,
118140
Debug::log("Host capability does not authorise a UDP connection");
119141
return address;
120142
}
121-
auto kind = network_socket_kind(socket);
143+
144+
SocketKind kind;
145+
CHERI::Capability kindPtr = &kind;
146+
kindPtr.permissions() &= {CHERI::Permission::Store};
147+
// No need to check the return value here, potential errors will be
148+
// detected in the switch.
149+
network_socket_kind(socket, kindPtr);
150+
122151
bool isIPv6 = false;
123152
switch (kind.protocol)
124153
{
@@ -133,10 +162,13 @@ NetworkAddress network_socket_udp_authorise_host(Timeout *timeout,
133162
isIPv6 = true;
134163
break;
135164
}
165+
166+
CHERI::Capability addressPtr = &address;
167+
addressPtr.permissions() &= {CHERI::Permission::Store};
136168
firewall_permit_dns();
137-
address = network_host_resolve(host->hostname, UseIPv6);
169+
int ret = network_host_resolve(host->hostname, UseIPv6, addressPtr);
138170
firewall_permit_dns(false);
139-
if (address.kind == NetworkAddress::AddressKindInvalid)
171+
if ((ret < 0) || (address.kind == NetworkAddress::AddressKindInvalid))
140172
{
141173
Debug::log("Failed to resolve host");
142174
return address;
@@ -146,6 +178,7 @@ NetworkAddress network_socket_udp_authorise_host(Timeout *timeout,
146178
Debug::log("Host address does not match socket type");
147179
return address;
148180
}
181+
149182
if (isIPv6)
150183
{
151184
firewall_add_udpipv6_endpoint(
@@ -161,6 +194,7 @@ NetworkAddress network_socket_udp_authorise_host(Timeout *timeout,
161194
firewall_add_udpipv4_endpoint(
162195
address.ipv4, kind.localPort, ntohs(host->port));
163196
}
197+
164198
return address;
165199
}
166200

lib/tcpip/network-internal.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@
1717
* Resolve a host name to an IPv4 or IPv6 address. If `useIPv6` is true, then
1818
* this will first attempt to find a IPv6 address and fall back to IPv4 if none
1919
* is found.
20+
*
21+
* The result of the resolve is stored in `outAddress`.
22+
*
23+
* This returns zero for success, or a negative value on error.
2024
*/
21-
NetworkAddress __cheri_compartment("TCPIP")
22-
network_host_resolve(const char *hostname, bool useIPv6);
25+
__cheri_compartment("TCPIP") int network_host_resolve(
26+
const char *hostname,
27+
bool useIPv6,
28+
NetworkAddress *outAddress);
2329

2430
/**
2531
* Create a socket and bind it to the given address. The socket will be
@@ -72,6 +78,9 @@ struct SocketKind
7278
};
7379

7480
/**
75-
* Returns information about the given socket.
81+
* Returns information about the given socket in `kind`.
82+
*
83+
* This returns zero for success, or a negative value on error.
7684
*/
77-
SocketKind __cheri_compartment("TCPIP") network_socket_kind(SObj socket);
85+
int __cheri_compartment("TCPIP")
86+
network_socket_kind(SObj socket, SocketKind *kind);

lib/tcpip/network_wrapper.cc

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ namespace
214214
* will favour IPv6 addresses, but can still return IPv4 addresses if no
215215
* IPv6 address is available.
216216
*/
217-
NetworkAddress host_resolve(const char *hostname, bool useIPv6 = UseIPv6)
217+
int
218+
host_resolve(const char *hostname, bool useIPv6, NetworkAddress *address)
218219
{
219220
struct AddrinfoHints hints;
220221
hints.family = useIPv6 ? FREERTOS_AF_INET6 : FREERTOS_AF_INET;
@@ -229,31 +230,31 @@ namespace
229230
// Try with IPv4 if the lookup failed with IPv6
230231
if (useIPv6)
231232
{
232-
return host_resolve(hostname, false);
233+
return host_resolve(hostname, false, address);
233234
}
234235
Debug::log("DNS request returned: {}", ret);
235-
return {0, NetworkAddress::AddressKindInvalid};
236+
address->kind = NetworkAddress::AddressKindInvalid;
237+
address->ipv4 = 0;
238+
return ret;
236239
}
237240

238-
NetworkAddress address;
239-
bool isIPv6 = false;
241+
bool isIPv6 = false;
240242
for (freertos_addrinfo *r = results; r != nullptr; r = r->ai_next)
241243
{
242244
Debug::log("Canonical name: {}", r->ai_canonname);
243245
if (r->ai_family == FREERTOS_AF_INET6)
244246
{
245247
memcpy(
246-
address.ipv6, r->ai_addr->sin_address.xIP_IPv6.ucBytes, 16);
247-
address.kind = NetworkAddress::AddressKindIPv6;
248+
address->ipv6, r->ai_addr->sin_address.xIP_IPv6.ucBytes, 16);
249+
address->kind = NetworkAddress::AddressKindIPv6;
248250
Debug::log("Got IPv6 address");
249251
}
250252
else
251253
{
252-
address.ipv4 = r->ai_addr->sin_address.ulIP_IPv4;
253-
address.kind = NetworkAddress::AddressKindIPv4;
254-
Debug::log("Got IPv4 address");
254+
address->ipv4 = r->ai_addr->sin_address.ulIP_IPv4;
255+
address->kind = NetworkAddress::AddressKindIPv4;
255256
Debug::log(
256-
"Got address: {}.{}.{}.{}",
257+
"Got IPv4 address: {}.{}.{}.{}",
257258
static_cast<int>(r->ai_addr->sin_address.ulIP_IPv4) & 0xff,
258259
static_cast<int>(r->ai_addr->sin_address.ulIP_IPv4) >> 8 &
259260
0xff,
@@ -265,7 +266,7 @@ namespace
265266
}
266267

267268
FreeRTOS_freeaddrinfo(results);
268-
return address;
269+
return 0;
269270
}
270271

271272
/**
@@ -422,9 +423,11 @@ namespace
422423
}
423424
} // namespace
424425

425-
NetworkAddress network_host_resolve(const char *hostname, bool useIPv6)
426+
int network_host_resolve(const char *hostname,
427+
bool useIPv6,
428+
NetworkAddress *address)
426429
{
427-
return host_resolve(hostname, useIPv6);
430+
return host_resolve(hostname, useIPv6, address);
428431
}
429432

430433
SObj network_socket_create_and_bind(Timeout *timeout,
@@ -959,27 +962,30 @@ ssize_t network_socket_send_to(Timeout *timeout,
959962
socket);
960963
}
961964

962-
SocketKind network_socket_kind(SObj socket)
965+
int network_socket_kind(SObj socket, SocketKind *kind)
963966
{
964-
SocketKind kind = {SocketKind::Invalid, 0};
965-
with_sealed_socket(
967+
kind->protocol = SocketKind::Invalid;
968+
kind->localPort = 0;
969+
970+
int ret = with_sealed_socket(
966971
[&](SealedSocket *socket) {
967972
if (socket->socket->ucProtocol == FREERTOS_IPPROTO_TCP)
968973
{
969-
kind.protocol = socket->socket->bits.bIsIPv6
970-
? SocketKind::TCPIPv6
971-
: SocketKind::TCPIPv4;
974+
kind->protocol = socket->socket->bits.bIsIPv6
975+
? SocketKind::TCPIPv6
976+
: SocketKind::TCPIPv4;
972977
}
973978
else
974979
{
975-
kind.protocol = socket->socket->bits.bIsIPv6
976-
? SocketKind::UDPIPv6
977-
: SocketKind::UDPIPv4;
980+
kind->protocol = socket->socket->bits.bIsIPv6
981+
? SocketKind::UDPIPv6
982+
: SocketKind::UDPIPv4;
978983
}
979-
kind.localPort = listGET_LIST_ITEM_VALUE(
984+
kind->localPort = listGET_LIST_ITEM_VALUE(
980985
(&((socket->socket)->xBoundSocketListItem)));
981986
return 0;
982987
},
983988
socket);
984-
return kind;
989+
990+
return ret;
985991
}

0 commit comments

Comments
 (0)