Skip to content

Commit 2948041

Browse files
committed
mctpd: improve handling for out-of-spec Set Endpoint ID responses
We may have endpoints reporting strange EID values in their set endpoint ID response, even when reporting that the assignement was accepted. Particularly, if the EID is in the invalid range, we will still attempt to add neighour and route entries for this invalid EID. We still want to accommodate valid IDs that were not the same as the one assigned, but ensure the EID is valid before processing. Signed-off-by: Jeremy Kerr <[email protected]>
1 parent 380608e commit 2948041

File tree

5 files changed

+140
-5
lines changed

5 files changed

+140
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
3131
`au.com.codecontruct.MCTP.Interface1` interface, allowing link-to-network
3232
lookups
3333

34+
5. mctpd: Better handling of strange cases of Set Endpoint ID responses,
35+
where the reported endpoint EID may either be different from expected,
36+
or invalid
37+
3438
### Changed
3539

3640
1. tests are now run with address sanitizer enabled (-fsanitize=address)

src/mctp-util.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,8 @@ char* bytes_to_uuid(const uint8_t u[16])
156156
u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
157157
return buf;
158158
}
159+
160+
bool mctp_eid_is_valid_unicast(mctp_eid_t eid)
161+
{
162+
return eid >= 8 && eid < 0xff;
163+
}

src/mctp-util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
#include <stdbool.h>
12
#include <stdint.h>
23

4+
#include "mctp.h"
5+
36
#define max(a, b) ((a) > (b) ? (a) : (b))
47
#define min(a, b) ((a) < (b) ? (a) : (b))
58

@@ -13,3 +16,4 @@ int parse_uint32(const char *str, uint32_t *out);
1316
int parse_int32(const char *str, int32_t *out);
1417
/* Returns a malloced pointer */
1518
char* bytes_to_uuid(const uint8_t u[16]);
19+
bool mctp_eid_is_valid_unicast(mctp_eid_t eid);

src/mctpd.c

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,7 +1299,8 @@ static int endpoint_query_phys(struct ctx *ctx, const dest_phys *dest,
12991299
}
13001300

13011301
/* returns -ECONNREFUSED if the endpoint returns failure. */
1302-
static int endpoint_send_set_endpoint_id(const struct peer *peer, mctp_eid_t *new_eid)
1302+
static int endpoint_send_set_endpoint_id(const struct peer *peer,
1303+
mctp_eid_t *new_eidp)
13031304
{
13041305
struct sockaddr_mctp_ext addr;
13051306
struct mctp_ctrl_cmd_set_eid req = {0};
@@ -1309,6 +1310,7 @@ static int endpoint_send_set_endpoint_id(const struct peer *peer, mctp_eid_t *ne
13091310
size_t buf_size;
13101311
uint8_t iid, stat, alloc;
13111312
const dest_phys *dest = &peer->phys;
1313+
mctp_eid_t new_eid;
13121314

13131315
rc = -1;
13141316

@@ -1331,18 +1333,34 @@ static int endpoint_send_set_endpoint_id(const struct peer *peer, mctp_eid_t *ne
13311333
resp = (void*)buf;
13321334

13331335
stat = resp->status >> 4 & 0x3;
1336+
new_eid = resp->eid_set;
1337+
1338+
// For both accepted and rejected cases, we learn the new EID of the
1339+
// endpoint. If this is a valid ID, we are likely to be able to handle
1340+
// this, as the caller may be able to change_peer_eid() to the
1341+
// newly-reported eid
13341342
if (stat == 0x01) {
1335-
// changed eid
1343+
if (!mctp_eid_is_valid_unicast(new_eid)) {
1344+
warnx("%s rejected assignment eid %d, and reported invalid eid %d",
1345+
dest_phys_tostr(dest), peer->eid, new_eid);
1346+
rc = -ECONNREFUSED;
1347+
goto out;
1348+
}
13361349
} else if (stat == 0x00) {
1337-
if (resp->eid_set != peer->eid) {
1350+
if (!mctp_eid_is_valid_unicast(new_eid)) {
1351+
warnx("%s eid %d replied with invalid eid %d, but 'accepted'",
1352+
dest_phys_tostr(dest), peer->eid, new_eid);
1353+
rc = -ECONNREFUSED;
1354+
goto out;
1355+
} else if (new_eid != peer->eid) {
13381356
warnx("%s eid %d replied with different eid %d, but 'accepted'",
1339-
dest_phys_tostr(dest), peer->eid, resp->eid_set);
1357+
dest_phys_tostr(dest), peer->eid, new_eid);
13401358
}
13411359
} else {
13421360
warnx("%s unexpected status 0x%02x",
13431361
dest_phys_tostr(dest), resp->status);
13441362
}
1345-
*new_eid = resp->eid_set;
1363+
*new_eidp = new_eid;
13461364

13471365
alloc = resp->status & 0x3;
13481366
if (alloc != 0) {
@@ -1519,6 +1537,9 @@ static int change_peer_eid(struct peer *peer, mctp_eid_t new_eid)
15191537
struct net *n = NULL;
15201538
int rc;
15211539

1540+
if (!mctp_eid_is_valid_unicast(new_eid))
1541+
return -EINVAL;
1542+
15221543
n = lookup_net(peer->ctx, peer->net);
15231544
if (!n) {
15241545
bug_warn("%s: Bad net %u", __func__, peer->net);

tests/test_mctpd.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,107 @@ async def handle_mctp_control(self, sock, src_addr, msg):
446446

447447
assert str(ex.value) == "Request failed"
448448

449+
""" During a SetupEndpoint's Set Endpoint ID exchange, return a response
450+
that indicates that the EID has been set, but report an invalid (0) EID
451+
in the response."""
452+
async def test_setup_endpoint_invalid_set_eid_response(dbus, mctpd):
453+
class InvalidEndpoint(Endpoint):
454+
async def handle_mctp_control(self, sock, src_addr, msg):
455+
flags, opcode = msg[0:2]
456+
if opcode != 1:
457+
return await super().handle_mctp_control(sock, src_addr, msg)
458+
dst_addr = MCTPSockAddr.for_ep_resp(self, src_addr, sock.addr_ext)
459+
self.eid = msg[3]
460+
msg = bytes([
461+
flags & 0x1f, # Rsp
462+
0x01, # opcode: Set Endpoint ID
463+
0x00, # cc: success
464+
0x00, # assignment accepted, no pool
465+
0x00, # set EID: invalid
466+
0x00, # pool size: 0
467+
])
468+
await sock.send(dst_addr, msg)
469+
470+
iface = mctpd.system.interfaces[0]
471+
ep = InvalidEndpoint(iface, bytes([0x1e]), eid = 0)
472+
mctpd.network.add_endpoint(ep)
473+
mctp = await mctpd_mctp_iface_obj(dbus, iface)
474+
475+
with pytest.raises(asyncdbus.errors.DBusError) as ex:
476+
rc = await mctp.call_setup_endpoint(ep.lladdr)
477+
478+
assert str(ex.value) == "Endpoint returned failure to Set Endpoint ID"
479+
480+
""" During a SetupEndpoint's Set Endpoint ID exchange, return a response
481+
that indicates that the EID has been set, but report a different set EID
482+
in the response."""
483+
async def test_setup_endpoint_vary_set_eid_response(dbus, mctpd):
484+
class VaryEndpoint(Endpoint):
485+
async def handle_mctp_control(self, sock, src_addr, msg):
486+
flags, opcode = msg[0:2]
487+
if opcode != 1:
488+
return await super().handle_mctp_control(sock, src_addr, msg)
489+
dst_addr = MCTPSockAddr.for_ep_resp(self, src_addr, sock.addr_ext)
490+
self.eid = msg[3] + 1
491+
msg = bytes([
492+
flags & 0x1f, # Rsp
493+
0x01, # opcode: Set Endpoint ID
494+
0x00, # cc: success
495+
0x00, # assignment accepted, no pool
496+
self.eid, # set EID: valid, but not what was assigned
497+
0x00, # pool size: 0
498+
])
499+
await sock.send(dst_addr, msg)
500+
501+
iface = mctpd.system.interfaces[0]
502+
ep = VaryEndpoint(iface, bytes([0x1e]))
503+
mctpd.network.add_endpoint(ep)
504+
mctp = await mctpd_mctp_iface_obj(dbus, iface)
505+
506+
(eid, _, _, _) = await mctp.call_setup_endpoint(ep.lladdr)
507+
508+
assert eid == ep.eid
509+
510+
""" During a SetupEndpoint's Set Endpoint ID exchange, return a response
511+
that indicates that the EID has been set, but report a different set EID
512+
in the response, which conflicts with another endpoint"""
513+
async def test_setup_endpoint_conflicting_set_eid_response(dbus, mctpd):
514+
515+
class ConflictingEndpoint(Endpoint):
516+
def __init__(self, iface, lladdr, conflict_eid):
517+
super().__init__(iface, lladdr)
518+
self.conflict_eid = conflict_eid
519+
520+
async def handle_mctp_control(self, sock, src_addr, msg):
521+
flags, opcode = msg[0:2]
522+
if opcode != 1:
523+
return await super().handle_mctp_control(sock, src_addr, msg)
524+
dst_addr = MCTPSockAddr.for_ep_resp(self, src_addr, sock.addr_ext)
525+
# reject reality, use a conflicting eid
526+
self.eid = self.conflict_eid
527+
msg = bytes([
528+
flags & 0x1f, # Rsp
529+
0x01, # opcode: Set Endpoint ID
530+
0x00, # cc: success
531+
0x00, # assignment accepted, no pool
532+
self.eid, # set EID: valid, but not what was assigned
533+
0x00, # pool size: 0
534+
])
535+
await sock.send(dst_addr, msg)
536+
537+
iface = mctpd.system.interfaces[0]
538+
ep1 = mctpd.network.endpoints[0]
539+
mctp = await mctpd_mctp_iface_obj(dbus, iface)
540+
(eid1, _, _, _) = await mctp.call_setup_endpoint(ep1.lladdr)
541+
assert eid1 == ep1.eid
542+
543+
ep2 = ConflictingEndpoint(iface, bytes([0x1f]), ep1.eid)
544+
mctpd.network.add_endpoint(ep2)
545+
with pytest.raises(asyncdbus.errors.DBusError) as ex:
546+
await mctp.call_setup_endpoint(ep2.lladdr)
547+
548+
assert "already used" in str(ex.value)
549+
449550
""" Ensure a response with an invalid IID is discarded """
450551
async def test_learn_endpoint_invalid_response_iid(dbus, mctpd):
451552
class InvalidIIDEndpoint(Endpoint):

0 commit comments

Comments
 (0)