Message ID | 20221219192712.210350-1-aconole@redhat.com |
---|---|
State | Superseded |
Headers | show |
Series | [ovs-dev] lldp: fix bugs when parsing malformed AutoAttach | expand |
Context | Check | Description |
---|---|---|
ovsrobot/apply-robot | success | apply and check: success |
ovsrobot/github-robot-_Build_and_Test | success | github build: passed |
ovsrobot/intel-ovs-compilation | success | test: success |
On 12/19/22 20:27, Aaron Conole wrote: > From: Qian Chen <cq674350529@163.com> > > The OVS LLDP implementation includes support for AutoAttach standard, which > the 'upstream' lldpd project does not include. As part of adding this > support, the message parsing for these TLVs did not include proper length > checks for the LLDP_TLV_AA_ELEMENT_SUBTYPE and the > LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE elements. The result is that a message > without a proper boundary will cause an over read of memory, and lead to > undefined results, including crashes or other unidentified behavior. > > The fix is to introduce proper bounds checking for these elements. Introduce > a unit test to ensure that we have some proper rejection in this code > base in the future. > > Fixes: be53a5c447c3 ("auto-attach: Initial support for Auto-Attach standard") > Signed-off-by: Qian Chen <cq674350529@163.com> > Co-authored-by: Aaron Conole <aconole@redhat.com> > Signed-off-by: Aaron Conole <aconole@redhat.com> > --- > NOTES: This bug is publicly known and disclosed at > https://github.com/openvswitch/ovs/pull/405 which makes this mostly > a repost. > I have modified the test case to ensure that it would run > correctly when doing both 'make check-kernel' and > 'make check-system-userspace' > > lib/lldp/lldp.c | 2 ++ > tests/system-traffic.at | 27 +++++++++++++++++++++++++++ > 2 files changed, 29 insertions(+) > > diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c > index dfeb2a8002..6fdcfef569 100644 > --- a/lib/lldp/lldp.c > +++ b/lib/lldp/lldp.c > @@ -583,6 +583,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, > > switch(tlv_subtype) { > case LLDP_TLV_AA_ELEMENT_SUBTYPE: > + CHECK_TLV_SIZE(50, "ELEMENT"); > PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); > > aa_element_dword = PEEK_UINT32; > @@ -629,6 +630,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, > break; > > case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE: > + CHECK_TLV_SIZE(36, "ISID_VLAN_ASGNS"); > PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); > > /* Subtract off tlv type and length (2Bytes) + OUI (3B) + > diff --git a/tests/system-traffic.at b/tests/system-traffic.at > index e5403519f2..0928bfe540 100644 > --- a/tests/system-traffic.at > +++ b/tests/system-traffic.at > @@ -7440,3 +7440,30 @@ OVS_WAIT_UNTIL([cat p2.pcap | grep -E "0x0050: *0000 *0000 *5002 *2000 *b85e *00 > > OVS_TRAFFIC_VSWITCHD_STOP > AT_CLEANUP > + > +AT_SETUP([autoattach - malformed lldp]) > +OVS_TRAFFIC_VSWITCHD_START() > + > +ADD_NAMESPACES(at_ns0) > + > +dnl Set up simple bridge port to receive lldp packets > +ADD_VETH(p0, at_ns0, br0, "172.31.1.1/24", "f6:b4:26:aa:5f:00") > + > +NETNS_DAEMONIZE([at_ns0], [tcpdump -l -n -xx -U -i p0 > p0.pcap], [tcpdump.pid]) > +sleep 1 > + > +dnl Enable lldp > +AT_CHECK([ovs-vsctl set interface ovs-p0 lldp:enable=true]) > + > +dnl Send a malformed lldp packet > +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 01 80 c2 00 00 0e f6 b4 26 aa 5f 00 88 cc 02 07 04 f6 b4 26 aa 5f 00 04 03 05 76 32 06 02 00 78 0c 50 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 fe 05 00 04 0d 0c 01 00 00 >/dev/null]) > + > +dnl Check the expected lldp packet by looking for the end > +OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0070: *4546 *fe05 *0004 *0d0c *0100 *00" 2>&1 1>/dev/null]) > + > +AT_CHECK([grep -o "ISID_VLAN_ASGNS TLV too short" ovs-vswitchd.log], [0], [dnl > +ISID_VLAN_ASGNS TLV too short > +]) > + > +OVS_TRAFFIC_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on ovs-p0/d"]) > +AT_CLEANUP Do we actually need a system test here? It looks like it can be converted to a simple unit test. E.g.: diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index eb4cd1896..41741d324 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -11966,3 +11966,25 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) OVS_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([ofproto-dpif - malformed lldp]) +OVS_VSWITCHD_START +add_of_ports br0 1 + +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) + +dnl Enable lldp. +AT_CHECK([ovs-vsctl set interface p1 lldp:enable=true]) + +dnl Send a malformed lldp packet. +packet="0180c200000ef6b426aa5f0088cc020704f6b426aa5f000403057632060200780c"dnl +"5044454144424545464445414442454546444541444245454644454144424545464445414"dnl +"4424545464445414442454546444541444245454644454144424545464445414442454546"dnl +"4445414442454546fe0500040d0c010000" +AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$packet"], [0], [stdout]) + +dnl Check that the packet was flagged as invalid. +OVS_WAIT_UNTIL([grep -q 'ISID_VLAN_ASGNS TLV too short' ovs-vswitchd.log]) + +OVS_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on p1/d"]) +AT_CLEANUP --- What do you think? Best regards, Ilya Maximets.
Ilya Maximets <i.maximets@ovn.org> writes: > On 12/19/22 20:27, Aaron Conole wrote: >> From: Qian Chen <cq674350529@163.com> >> >> The OVS LLDP implementation includes support for AutoAttach standard, which >> the 'upstream' lldpd project does not include. As part of adding this >> support, the message parsing for these TLVs did not include proper length >> checks for the LLDP_TLV_AA_ELEMENT_SUBTYPE and the >> LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE elements. The result is that a message >> without a proper boundary will cause an over read of memory, and lead to >> undefined results, including crashes or other unidentified behavior. >> >> The fix is to introduce proper bounds checking for these elements. Introduce >> a unit test to ensure that we have some proper rejection in this code >> base in the future. >> >> Fixes: be53a5c447c3 ("auto-attach: Initial support for Auto-Attach standard") >> Signed-off-by: Qian Chen <cq674350529@163.com> >> Co-authored-by: Aaron Conole <aconole@redhat.com> >> Signed-off-by: Aaron Conole <aconole@redhat.com> >> --- >> NOTES: This bug is publicly known and disclosed at >> https://github.com/openvswitch/ovs/pull/405 which makes this mostly >> a repost. >> I have modified the test case to ensure that it would run >> correctly when doing both 'make check-kernel' and >> 'make check-system-userspace' >> >> lib/lldp/lldp.c | 2 ++ >> tests/system-traffic.at | 27 +++++++++++++++++++++++++++ >> 2 files changed, 29 insertions(+) >> >> diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c >> index dfeb2a8002..6fdcfef569 100644 >> --- a/lib/lldp/lldp.c >> +++ b/lib/lldp/lldp.c >> @@ -583,6 +583,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, >> >> switch(tlv_subtype) { >> case LLDP_TLV_AA_ELEMENT_SUBTYPE: >> + CHECK_TLV_SIZE(50, "ELEMENT"); >> PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); >> >> aa_element_dword = PEEK_UINT32; >> @@ -629,6 +630,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, >> break; >> >> case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE: >> + CHECK_TLV_SIZE(36, "ISID_VLAN_ASGNS"); >> PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); >> >> /* Subtract off tlv type and length (2Bytes) + OUI (3B) + >> diff --git a/tests/system-traffic.at b/tests/system-traffic.at >> index e5403519f2..0928bfe540 100644 >> --- a/tests/system-traffic.at >> +++ b/tests/system-traffic.at >> @@ -7440,3 +7440,30 @@ OVS_WAIT_UNTIL([cat p2.pcap | grep -E "0x0050: *0000 *0000 *5002 *2000 *b85e *00 >> >> OVS_TRAFFIC_VSWITCHD_STOP >> AT_CLEANUP >> + >> +AT_SETUP([autoattach - malformed lldp]) >> +OVS_TRAFFIC_VSWITCHD_START() >> + >> +ADD_NAMESPACES(at_ns0) >> + >> +dnl Set up simple bridge port to receive lldp packets >> +ADD_VETH(p0, at_ns0, br0, "172.31.1.1/24", "f6:b4:26:aa:5f:00") >> + >> +NETNS_DAEMONIZE([at_ns0], [tcpdump -l -n -xx -U -i p0 > p0.pcap], [tcpdump.pid]) >> +sleep 1 >> + >> +dnl Enable lldp >> +AT_CHECK([ovs-vsctl set interface ovs-p0 lldp:enable=true]) >> + >> +dnl Send a malformed lldp packet >> +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 01 80 c2 00 00 0e f6 b4 26 aa 5f 00 88 cc 02 07 04 f6 b4 26 aa 5f 00 04 03 05 76 32 06 02 00 78 0c 50 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 fe 05 00 04 0d 0c 01 00 00 >/dev/null]) >> + >> +dnl Check the expected lldp packet by looking for the end >> +OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0070: *4546 *fe05 *0004 *0d0c *0100 *00" 2>&1 1>/dev/null]) >> + >> +AT_CHECK([grep -o "ISID_VLAN_ASGNS TLV too short" ovs-vswitchd.log], [0], [dnl >> +ISID_VLAN_ASGNS TLV too short >> +]) >> + >> +OVS_TRAFFIC_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on ovs-p0/d"]) >> +AT_CLEANUP > > Do we actually need a system test here? > It looks like it can be converted to a simple unit test. E.g.: > > diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at > index eb4cd1896..41741d324 100644 > --- a/tests/ofproto-dpif.at > +++ b/tests/ofproto-dpif.at > @@ -11966,3 +11966,25 @@ AT_CHECK([test 1 = `ovs-ofctl parse-pcap p2-tx.pcap | wc -l`]) > > OVS_VSWITCHD_STOP > AT_CLEANUP > + > +AT_SETUP([ofproto-dpif - malformed lldp]) > +OVS_VSWITCHD_START > +add_of_ports br0 1 > + > +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) > + > +dnl Enable lldp. > +AT_CHECK([ovs-vsctl set interface p1 lldp:enable=true]) > + > +dnl Send a malformed lldp packet. > +packet="0180c200000ef6b426aa5f0088cc020704f6b426aa5f000403057632060200780c"dnl > +"5044454144424545464445414442454546444541444245454644454144424545464445414"dnl > +"4424545464445414442454546444541444245454644454144424545464445414442454546"dnl > +"4445414442454546fe0500040d0c010000" > +AT_CHECK([ovs-appctl netdev-dummy/receive p1 "$packet"], [0], [stdout]) > + > +dnl Check that the packet was flagged as invalid. > +OVS_WAIT_UNTIL([grep -q 'ISID_VLAN_ASGNS TLV too short' ovs-vswitchd.log]) > + > +OVS_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on p1/d"]) > +AT_CLEANUP > --- > > What do you think? Makes sense to me. Submitting v2 now. > Best regards, Ilya Maximets.
diff --git a/lib/lldp/lldp.c b/lib/lldp/lldp.c index dfeb2a8002..6fdcfef569 100644 --- a/lib/lldp/lldp.c +++ b/lib/lldp/lldp.c @@ -583,6 +583,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, switch(tlv_subtype) { case LLDP_TLV_AA_ELEMENT_SUBTYPE: + CHECK_TLV_SIZE(50, "ELEMENT"); PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); aa_element_dword = PEEK_UINT32; @@ -629,6 +630,7 @@ lldp_decode(struct lldpd *cfg OVS_UNUSED, char *frame, int s, break; case LLDP_TLV_AA_ISID_VLAN_ASGNS_SUBTYPE: + CHECK_TLV_SIZE(36, "ISID_VLAN_ASGNS"); PEEK_BYTES(&msg_auth_digest, sizeof msg_auth_digest); /* Subtract off tlv type and length (2Bytes) + OUI (3B) + diff --git a/tests/system-traffic.at b/tests/system-traffic.at index e5403519f2..0928bfe540 100644 --- a/tests/system-traffic.at +++ b/tests/system-traffic.at @@ -7440,3 +7440,30 @@ OVS_WAIT_UNTIL([cat p2.pcap | grep -E "0x0050: *0000 *0000 *5002 *2000 *b85e *00 OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP + +AT_SETUP([autoattach - malformed lldp]) +OVS_TRAFFIC_VSWITCHD_START() + +ADD_NAMESPACES(at_ns0) + +dnl Set up simple bridge port to receive lldp packets +ADD_VETH(p0, at_ns0, br0, "172.31.1.1/24", "f6:b4:26:aa:5f:00") + +NETNS_DAEMONIZE([at_ns0], [tcpdump -l -n -xx -U -i p0 > p0.pcap], [tcpdump.pid]) +sleep 1 + +dnl Enable lldp +AT_CHECK([ovs-vsctl set interface ovs-p0 lldp:enable=true]) + +dnl Send a malformed lldp packet +NS_CHECK_EXEC([at_ns0], [$PYTHON3 $srcdir/sendpkt.py p0 01 80 c2 00 00 0e f6 b4 26 aa 5f 00 88 cc 02 07 04 f6 b4 26 aa 5f 00 04 03 05 76 32 06 02 00 78 0c 50 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 44 45 41 44 42 45 45 46 fe 05 00 04 0d 0c 01 00 00 >/dev/null]) + +dnl Check the expected lldp packet by looking for the end +OVS_WAIT_UNTIL([cat p0.pcap | grep -E "0x0070: *4546 *fe05 *0004 *0d0c *0100 *00" 2>&1 1>/dev/null]) + +AT_CHECK([grep -o "ISID_VLAN_ASGNS TLV too short" ovs-vswitchd.log], [0], [dnl +ISID_VLAN_ASGNS TLV too short +]) + +OVS_TRAFFIC_VSWITCHD_STOP(["/|WARN|ISID_VLAN_ASGNS TLV too short received on ovs-p0/d"]) +AT_CLEANUP