Author: tuexen
Date: Sat Apr  7 20:05:25 2018
New Revision: 332220
URL: https://svnweb.freebsd.org/changeset/base/332220

Log:
  MFC r325046:
  
  Fix parsing error when processing cmsg in SCTP send calls. The bug is
  related to a signed/unsigned mismatch.
  This should most likely fix the issue in sctp_sosend reported by
  Dmitry Vyukov on the freebsd-hackers mailing list and found by
  running syzkaller.

Modified:
  stable/11/sys/netinet/sctp_output.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/netinet/sctp_output.c
==============================================================================
--- stable/11/sys/netinet/sctp_output.c Sat Apr  7 20:04:03 2018        
(r332219)
+++ stable/11/sys/netinet/sctp_output.c Sat Apr  7 20:05:25 2018        
(r332220)
@@ -3465,32 +3465,35 @@ static int
 sctp_find_cmsg(int c_type, void *data, struct mbuf *control, size_t cpsize)
 {
        struct cmsghdr cmh;
-       int tlen, at, found;
        struct sctp_sndinfo sndinfo;
        struct sctp_prinfo prinfo;
        struct sctp_authinfo authinfo;
+       int tot_len, rem_len, cmsg_data_len, cmsg_data_off, off;
+       int found;
 
-       tlen = SCTP_BUF_LEN(control);
-       at = 0;
-       found = 0;
        /*
         * Independent of how many mbufs, find the c_type inside the control
         * structure and copy out the data.
         */
-       while (at < tlen) {
-               if ((tlen - at) < (int)CMSG_ALIGN(sizeof(cmh))) {
+       found = 0;
+       tot_len = SCTP_BUF_LEN(control);
+       for (off = 0; off < tot_len; off += CMSG_ALIGN(cmh.cmsg_len)) {
+               rem_len = tot_len - off;
+               if (rem_len < (int)CMSG_ALIGN(sizeof(cmh))) {
                        /* There is not enough room for one more. */
                        return (found);
                }
-               m_copydata(control, at, sizeof(cmh), (caddr_t)&cmh);
+               m_copydata(control, off, sizeof(cmh), (caddr_t)&cmh);
                if (cmh.cmsg_len < CMSG_ALIGN(sizeof(cmh))) {
                        /* We dont't have a complete CMSG header. */
                        return (found);
                }
-               if (((int)cmh.cmsg_len + at) > tlen) {
+               if ((cmh.cmsg_len > INT_MAX) || ((int)cmh.cmsg_len > rem_len)) {
                        /* We don't have the complete CMSG. */
                        return (found);
                }
+               cmsg_data_len = (int)cmh.cmsg_len - CMSG_ALIGN(sizeof(cmh));
+               cmsg_data_off = off + CMSG_ALIGN(sizeof(cmh));
                if ((cmh.cmsg_level == IPPROTO_SCTP) &&
                    ((c_type == cmh.cmsg_type) ||
                    ((c_type == SCTP_SNDRCV) &&
@@ -3498,11 +3501,14 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *co
                    (cmh.cmsg_type == SCTP_PRINFO) ||
                    (cmh.cmsg_type == SCTP_AUTHINFO))))) {
                        if (c_type == cmh.cmsg_type) {
-                               if ((size_t)(cmh.cmsg_len - 
CMSG_ALIGN(sizeof(cmh))) < cpsize) {
+                               if (cpsize > INT_MAX) {
                                        return (found);
                                }
+                               if (cmsg_data_len < (int)cpsize) {
+                                       return (found);
+                               }
                                /* It is exactly what we want. Copy it out. */
-                               m_copydata(control, at + 
CMSG_ALIGN(sizeof(cmh)), (int)cpsize, (caddr_t)data);
+                               m_copydata(control, cmsg_data_off, (int)cpsize, 
(caddr_t)data);
                                return (1);
                        } else {
                                struct sctp_sndrcvinfo *sndrcvinfo;
@@ -3516,10 +3522,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *co
                                }
                                switch (cmh.cmsg_type) {
                                case SCTP_SNDINFO:
-                                       if ((size_t)(cmh.cmsg_len - 
CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_sndinfo)) {
+                                       if (cmsg_data_len < (int)sizeof(struct 
sctp_sndinfo)) {
                                                return (found);
                                        }
-                                       m_copydata(control, at + 
CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_sndinfo), (caddr_t)&sndinfo);
+                                       m_copydata(control, cmsg_data_off, 
sizeof(struct sctp_sndinfo), (caddr_t)&sndinfo);
                                        sndrcvinfo->sinfo_stream = 
sndinfo.snd_sid;
                                        sndrcvinfo->sinfo_flags = 
sndinfo.snd_flags;
                                        sndrcvinfo->sinfo_ppid = 
sndinfo.snd_ppid;
@@ -3527,10 +3533,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *co
                                        sndrcvinfo->sinfo_assoc_id = 
sndinfo.snd_assoc_id;
                                        break;
                                case SCTP_PRINFO:
-                                       if ((size_t)(cmh.cmsg_len - 
CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_prinfo)) {
+                                       if (cmsg_data_len < (int)sizeof(struct 
sctp_prinfo)) {
                                                return (found);
                                        }
-                                       m_copydata(control, at + 
CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_prinfo), (caddr_t)&prinfo);
+                                       m_copydata(control, cmsg_data_off, 
sizeof(struct sctp_prinfo), (caddr_t)&prinfo);
                                        if (prinfo.pr_policy != 
SCTP_PR_SCTP_NONE) {
                                                sndrcvinfo->sinfo_timetolive = 
prinfo.pr_value;
                                        } else {
@@ -3539,10 +3545,10 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *co
                                        sndrcvinfo->sinfo_flags |= 
prinfo.pr_policy;
                                        break;
                                case SCTP_AUTHINFO:
-                                       if ((size_t)(cmh.cmsg_len - 
CMSG_ALIGN(sizeof(cmh))) < sizeof(struct sctp_authinfo)) {
+                                       if (cmsg_data_len < (int)sizeof(struct 
sctp_authinfo)) {
                                                return (found);
                                        }
-                                       m_copydata(control, at + 
CMSG_ALIGN(sizeof(cmh)), sizeof(struct sctp_authinfo), (caddr_t)&authinfo);
+                                       m_copydata(control, cmsg_data_off, 
sizeof(struct sctp_authinfo), (caddr_t)&authinfo);
                                        sndrcvinfo->sinfo_keynumber_valid = 1;
                                        sndrcvinfo->sinfo_keynumber = 
authinfo.auth_keynumber;
                                        break;
@@ -3552,7 +3558,6 @@ sctp_find_cmsg(int c_type, void *data, struct mbuf *co
                                found = 1;
                        }
                }
-               at += CMSG_ALIGN(cmh.cmsg_len);
        }
        return (found);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to