vconn_add_bundle_error() stores a maximum of 64 bytes of an
OpenFlow packet, however ofperr_decode_msg() assumes that the
entire packet is present. This leads to a buffer read overrun
when the the packet is copied to another buffer using the full
packet size.

Fix by adding a parameter to ofperr_decode_msg() indicating the
size of the buffer containing the OpenFlow packet.

Found via gcc's address sanitizer.

Fixes: 506c1ddb3404 ("vconn: Better bundle error management.")
Signed-off-by: Lance Richardson <[email protected]>
---
 include/openvswitch/ofp-errors.h | 1 +
 lib/ofp-errors.c                 | 6 ++++--
 lib/ofp-print.c                  | 2 +-
 lib/vconn.c                      | 2 +-
 ovn/controller/ofctrl.c          | 5 +++--
 utilities/ovs-ofctl.c            | 3 ++-
 6 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/include/openvswitch/ofp-errors.h b/include/openvswitch/ofp-errors.h
index aeb58e0..1b28084 100644
--- a/include/openvswitch/ofp-errors.h
+++ b/include/openvswitch/ofp-errors.h
@@ -809,6 +809,7 @@ bool ofperr_is_valid(enum ofperr);
 enum ofperr ofperr_from_name(const char *);
 
 enum ofperr ofperr_decode_msg(const struct ofp_header *,
+                              size_t ofp_header_buf_len,
                               struct ofpbuf *payload);
 struct ofpbuf *ofperr_encode_reply(enum ofperr, const struct ofp_header *);
 struct ofpbuf *ofperr_encode_hello(enum ofperr, enum ofp_version ofp_version,
diff --git a/lib/ofp-errors.c b/lib/ofp-errors.c
index adc9295..8f65c51 100644
--- a/lib/ofp-errors.c
+++ b/lib/ofp-errors.c
@@ -282,7 +282,8 @@ ofperr_get_code(enum ofperr error, enum ofp_version version)
  * aligned).  The caller must free the payload (with ofpbuf_uninit()) when it
  * is no longer needed.  On failure, '*payload' is cleared. */
 enum ofperr
-ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload)
+ofperr_decode_msg(const struct ofp_header *oh, size_t oh_buflen,
+                  struct ofpbuf *payload)
 {
     const struct ofp_error_msg *oem;
     enum ofpraw raw;
@@ -294,7 +295,8 @@ ofperr_decode_msg(const struct ofp_header *oh, struct 
ofpbuf *payload)
     }
 
     /* Pull off the error message. */
-    struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+    size_t oh_size = MIN(oh_buflen, ntohs(oh->length));
+    struct ofpbuf b = ofpbuf_const_initializer(oh, oh_size);
     enum ofperr error = ofpraw_pull(&raw, &b);
     if (error) {
         return 0;
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index 423df31..a341ce0 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1596,7 +1596,7 @@ ofp_print_error_msg(struct ds *string, const struct 
ofp_header *oh,
     enum ofperr error;
     char *s;
 
-    error = ofperr_decode_msg(oh, &payload);
+    error = ofperr_decode_msg(oh, len, &payload);
     if (!error) {
         ds_put_cstr(string, "***decode error***");
         ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true);
diff --git a/lib/vconn.c b/lib/vconn.c
index 6997eaa..32cb80d 100644
--- a/lib/vconn.c
+++ b/lib/vconn.c
@@ -1079,7 +1079,7 @@ vconn_bundle_reply_validate(struct ofpbuf *reply,
 
     if (type == OFPTYPE_ERROR) {
         vconn_add_bundle_error(oh, errors);
-        return ofperr_decode_msg(oh, NULL);
+        return ofperr_decode_msg(oh, ntohs(oh->length), NULL);
     }
     if (type != OFPTYPE_BUNDLE_CONTROL) {
         return OFPERR_OFPBRC_BAD_TYPE;
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
index 277d3d7..b9988d1 100644
--- a/ovn/controller/ofctrl.c
+++ b/ovn/controller/ofctrl.c
@@ -283,8 +283,9 @@ recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, 
enum ofptype type,
                      ofperr_to_string(error));
         }
     } else if (type == OFPTYPE_ERROR) {
+        enum ofperr error = ofperr_decode_msg(oh, ntohs(oh->length), NULL);
         VLOG_ERR("switch refused to allocate Geneve option (%s)",
-                 ofperr_to_string(ofperr_decode_msg(oh, NULL)));
+                 ofperr_to_string(error));
     } else {
         char *s = ofp_to_string(oh, ntohs(oh->length), NULL, 1);
         VLOG_ERR("unexpected reply to TLV table request (%s)", s);
@@ -327,7 +328,7 @@ recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum 
ofptype type,
     } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) {
         state = S_CLEAR_FLOWS;
     } else if (oh->xid == xid && type == OFPTYPE_ERROR) {
-        enum ofperr error = ofperr_decode_msg(oh, NULL);
+        enum ofperr error = ofperr_decode_msg(oh, ntohs(oh->length), NULL);
         if (error == OFPERR_NXTTMFC_ALREADY_MAPPED ||
             error == OFPERR_NXTTMFC_DUP_ENTRY) {
             VLOG_INFO("raced with another controller adding "
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index dca9be3..d60e5d5 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -708,7 +708,8 @@ bundle_print_errors(struct ovs_list *errors, struct 
ovs_list *requests,
         enum ofperr ofperr;
         struct ofpbuf payload;
 
-        ofperr = ofperr_decode_msg(&error->ofp_msg, &payload);
+        ofperr = ofperr_decode_msg(&error->ofp_msg, sizeof error->ofp_msg_data,
+                                   &payload);
         if (!ofperr) {
             fprintf(stderr, "***decode error***");
         } else {
-- 
2.9.4

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to