Skip to content

Commit 4ed32fc

Browse files
authored
Fix network control handler to ensure proper offset calculation and prevent buffer overrun (bacnet-stack#1387)
* Fix out-of-bounds read in network control handler by validating NPDU length and adjusting offsets * Refactor network control handler to use dedicated processing functions for I-Am-Router and Init Routing Table messages, improving code clarity and maintainability.
1 parent 5ff4afd commit 4ed32fc

6 files changed

Lines changed: 285 additions & 82 deletions

File tree

apps/router-ipv6/main.c

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -638,9 +638,7 @@ static void network_control_handler(
638638
uint8_t *npdu,
639639
uint16_t npdu_len)
640640
{
641-
uint16_t npdu_offset = 0;
642641
uint16_t dnet = 0;
643-
uint16_t len = 0;
644642
const char *msg_name = NULL;
645643

646644
msg_name = bactext_network_layer_msg_name(npdu_data->network_message_type);
@@ -652,19 +650,8 @@ static void network_control_handler(
652650
break;
653651
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
654652
/* add its DNETs to our routing table */
655-
fprintf(stderr, "for Networks: ");
656-
len = 2;
657-
while (npdu_len >= len) {
658-
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
659-
fprintf(stderr, "%hu", dnet);
660-
dnet_add(snet, dnet, src);
661-
npdu_len -= len;
662-
npdu_offset += len;
663-
if (npdu_len) {
664-
fprintf(stderr, ", ");
665-
}
666-
}
667-
fprintf(stderr, ".\n");
653+
npdu_i_am_router_to_network_process(
654+
snet, src, npdu, npdu_len, dnet_add);
668655
break;
669656
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
670657
/* Do nothing, same as previous case. */
@@ -709,32 +696,10 @@ static void network_control_handler(
709696
case NETWORK_MESSAGE_INIT_RT_TABLE:
710697
/* If sent with Number of Ports == 0, we respond with
711698
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
712-
* reachable networks.
713-
*/
714-
if (npdu_len > 0) {
715-
/* If Number of Ports is 0, broadcast our "full" table */
716-
if (npdu[0] == 0) {
717-
send_initialize_routing_table_ack(snet, NULL);
718-
} else {
719-
/* they sent us a list */
720-
int net_count = npdu[0];
721-
while (net_count--) {
722-
int i = 1;
723-
/* DNET */
724-
decode_unsigned16(&npdu[i], &dnet);
725-
/* update routing table */
726-
dnet_add(snet, dnet, src);
727-
if (npdu[i + 3] > 0) {
728-
/* find next NET value */
729-
i = npdu[i + 3] + 4;
730-
} else {
731-
i += 4;
732-
}
733-
}
734-
send_initialize_routing_table_ack(snet, NULL);
735-
}
736-
break;
737-
}
699+
* reachable networks. */
700+
npdu_init_routing_table_process(
701+
snet, src, npdu, npdu_len, dnet_add);
702+
send_initialize_routing_table_ack(snet, NULL);
738703
break;
739704
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
740705
/* Do nothing with the routing table info, since don't support

apps/router-mstp/main.c

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,7 @@ static void network_control_handler(
659659
uint8_t *npdu,
660660
uint16_t npdu_len)
661661
{
662-
uint16_t npdu_offset = 0;
663662
uint16_t dnet = 0;
664-
uint16_t len = 0;
665663
const char *msg_name = NULL;
666664

667665
(void)src;
@@ -675,19 +673,8 @@ static void network_control_handler(
675673
break;
676674
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
677675
/* add its DNETs to our routing table */
678-
fprintf(stderr, "for Networks: ");
679-
len = 2;
680-
while (npdu_len >= len) {
681-
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
682-
fprintf(stderr, "%hu", dnet);
683-
dnet_add(snet, dnet, src);
684-
npdu_len -= len;
685-
npdu_offset += len;
686-
if (npdu_len) {
687-
fprintf(stderr, ", ");
688-
}
689-
}
690-
fprintf(stderr, ".\n");
676+
npdu_i_am_router_to_network_process(
677+
snet, src, npdu, npdu_len, dnet_add);
691678
break;
692679
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
693680
/* Do nothing, same as previous case. */
@@ -734,30 +721,9 @@ static void network_control_handler(
734721
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
735722
* reachable networks.
736723
*/
737-
if (npdu_len > 0) {
738-
/* If Number of Ports is 0, broadcast our "full" table */
739-
if (npdu[0] == 0) {
740-
send_initialize_routing_table_ack(snet, NULL);
741-
} else {
742-
/* they sent us a list */
743-
int net_count = npdu[0];
744-
while (net_count--) {
745-
int i = 1;
746-
/* DNET */
747-
decode_unsigned16(&npdu[i], &dnet);
748-
/* update routing table */
749-
dnet_add(snet, dnet, src);
750-
if (npdu[i + 3] > 0) {
751-
/* find next NET value */
752-
i = npdu[i + 3] + 4;
753-
} else {
754-
i += 4;
755-
}
756-
}
757-
send_initialize_routing_table_ack(snet, NULL);
758-
}
759-
break;
760-
}
724+
npdu_init_routing_table_process(
725+
snet, src, npdu, npdu_len, dnet_add);
726+
send_initialize_routing_table_ack(snet, NULL);
761727
break;
762728
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
763729
/* Do nothing with the routing table info, since don't support

ports/linux/dlmstp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ bool dlmstp_init(const char *ifname)
925925
RS485_Set_Interface(ifname);
926926
debug_fprintf(stderr, "MS/TP Interface: %s\n", ifname);
927927
} else {
928-
ifname = (char *)RS485_Interface();
928+
ifname = RS485_Interface();
929929
}
930930
pthread_condattr_init(&attr);
931931
if ((rv = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) != 0) {

src/bacnet/npdu.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,3 +890,108 @@ bool npdu_is_data_expecting_reply(
890890
request_pdu, request_pdu_len, &request_address, reply_pdu,
891891
reply_pdu_len, &reply_address);
892892
}
893+
894+
/**
895+
* @brief Process the NPDU portion of an I-Am-Router-To-Network message, which
896+
* contains a list of BACnet network numbers that the router is connected to.
897+
* @param snet [in] The source network number of the I-Am-Router-To
898+
* Network message, which is the network number of the router sending the
899+
* message.
900+
* @param src [in] The source address of the I-Am-Router-To-Network message,
901+
* which is the address of the router sending the message.
902+
* @param npdu [in] The buffer containing the NPDU portion of the
903+
* I-Am-Router-To-Network message, which contains the list of BACnet network
904+
* numbers that the router is connected to.
905+
* @param npdu_size [in] The size of the npdu buffer in bytes.
906+
* @param dnet_add [in] A callback function that will be called for each BACnet
907+
* network number (DNET)
908+
*/
909+
void npdu_i_am_router_to_network_process(
910+
uint16_t snet,
911+
const BACNET_ADDRESS *src,
912+
const uint8_t *npdu,
913+
uint16_t npdu_size,
914+
npdu_dnet_add_callback_t dnet_add)
915+
{
916+
int len = 2;
917+
uint16_t dnet = 0;
918+
uint16_t npdu_offset = 0;
919+
uint16_t npdu_len = npdu_size;
920+
921+
while (npdu_len >= len) {
922+
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
923+
if (dnet_add) {
924+
dnet_add(snet, dnet, src);
925+
}
926+
npdu_len -= len;
927+
npdu_offset += len;
928+
}
929+
}
930+
931+
/**
932+
* @brief Process the NPDU portion of an Initialize-Routing-Table message, which
933+
* contains a list of BACnet network numbers (DNETs) and per-port information.
934+
* @param snet [in] The source network number of the Initialize-Routing-Table
935+
* message, which is the network number of the router sending the message.
936+
* @param src [in] The source address of the I-Have-Router-To-Network message,
937+
* which is the address of the router sending the message.
938+
* @param npdu [in] The buffer containing the NPDU portion of the
939+
* I-Have-Router-To-Network message, which contains the list of BACnet network
940+
* numbers that the router is connected to, along with port information.
941+
* @param npdu_size [in] The size of the npdu buffer in bytes.
942+
* @param dnet_add [in] Optional callback invoked for each decoded DNET value.
943+
* The callback receives the source network, decoded DNET, and source address.
944+
*/
945+
void npdu_init_routing_table_process(
946+
uint16_t snet,
947+
const BACNET_ADDRESS *src,
948+
const uint8_t *npdu,
949+
uint16_t npdu_size,
950+
npdu_dnet_add_callback_t dnet_add)
951+
{
952+
int len = 2;
953+
uint16_t dnet = 0;
954+
uint16_t npdu_offset = 0;
955+
uint8_t port_id = 0;
956+
uint8_t port_info_len = 0;
957+
uint8_t net_count;
958+
uint16_t npdu_len = npdu_size;
959+
960+
if (npdu_len <= 1) {
961+
/* malformed message */
962+
return;
963+
}
964+
net_count = npdu[npdu_offset];
965+
npdu_offset += 1;
966+
npdu_len -= 1;
967+
if (net_count == 0) {
968+
/* no networks, nothing to do */
969+
return;
970+
}
971+
/* DNET(2) + PortID(1) + PortInfoLen(1) = 4 bytes */
972+
while ((npdu_len >= 4) && (net_count--)) {
973+
/* DNET */
974+
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
975+
npdu_offset += len;
976+
npdu_len -= len;
977+
/* update routing table */
978+
if (dnet_add) {
979+
dnet_add(snet, dnet, src);
980+
}
981+
/* skip port_id & port_info */
982+
port_id = npdu[npdu_offset];
983+
npdu_offset += 1;
984+
npdu_len -= 1;
985+
port_info_len = npdu[npdu_offset];
986+
npdu_offset += 1;
987+
npdu_len -= 1;
988+
if (npdu_len >= port_info_len) {
989+
npdu_offset += port_info_len;
990+
npdu_len -= port_info_len;
991+
} else {
992+
/* malformed message */
993+
break;
994+
}
995+
(void)port_id;
996+
}
997+
}

src/bacnet/npdu.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ typedef struct router_port_t {
5454
struct router_port_t *next; /**< Point to next in linked list */
5555
} BACNET_ROUTER_PORT;
5656

57+
typedef void (*npdu_dnet_add_callback_t)(
58+
uint16_t snet, uint16_t net, const BACNET_ADDRESS *addr);
59+
5760
#define NETWORK_NUMBER_LEARNED 0
5861
#define NETWORK_NUMBER_CONFIGURED 1
5962

@@ -123,13 +126,28 @@ bool npdu_is_expected_reply(
123126
const uint8_t *reply_pdu,
124127
uint16_t reply_pdu_len,
125128
BACNET_ADDRESS *reply_address);
129+
BACNET_STACK_EXPORT
126130
bool npdu_is_data_expecting_reply(
127131
const uint8_t *request_pdu,
128132
uint16_t request_pdu_len,
129133
uint8_t request_mac,
130134
const uint8_t *reply_pdu,
131135
uint16_t reply_pdu_len,
132136
uint8_t reply_mac);
137+
BACNET_STACK_EXPORT
138+
void npdu_i_am_router_to_network_process(
139+
uint16_t snet,
140+
const BACNET_ADDRESS *src,
141+
const uint8_t *npdu,
142+
uint16_t npdu_size,
143+
npdu_dnet_add_callback_t dnet_add);
144+
BACNET_STACK_EXPORT
145+
void npdu_init_routing_table_process(
146+
uint16_t snet,
147+
const BACNET_ADDRESS *src,
148+
const uint8_t *npdu,
149+
uint16_t npdu_size,
150+
npdu_dnet_add_callback_t dnet_add);
133151

134152
#ifdef __cplusplus
135153
}

0 commit comments

Comments
 (0)