Netdev List
 help / color / mirror / Atom feed
From: cndougla@linux.vnet.ibm.com
To: netdev@vger.kernel.org
Subject: SUSv3 recvmsg, sendmsg compliance
Date: Tue,  4 Aug 2009 10:14:11 -0400	[thread overview]
Message-ID: <20090804101411.y5fmtgh2skwkw4gc@imap.linux.ibm.com> (raw)

While running the LSB testsuite I noticed that on ppc 32-bit  
userland/64-bit kernel recvmsg returned EPERM (-1) instead of EMSGSIZE  
in errno when the sole iovec's iov_len was set to -1. According to the  
SUSv3 standard, iov_len must be positive and less than IOV_MAX:

http://www.opengroup.org/onlinepubs/000095399/functions/recvmsg.html

In Linux, iov_len is defined as an unsigned value. This does not seem  
to be forbidden by the standard, so that means we can't trip EMSGSIZE  
just by setting iov_len to -1. However, Linux also doesn't have  
IOV_MAX. In fact, Linux doesn't care how big you set iov_len since in  
memcpy_toiovec() the size is bounded by the size of the packet being  
copied:

                if (iov->iov_len) {
                        int copy = min_t(unsigned int, iov->iov_len, len);
                        if (copy_to_user(iov->iov_base, kdata, copy))
                                return -EFAULT;
                        kdata += copy;
                        len -= copy;
                        iov->iov_len -= copy;
                        iov->iov_base += copy;
                }
                iov++;

So far, it looks like Linux is compliant to SUSv3 because it handles  
all iov_len passed in. However, before we get to memcpy_toiovec(),  
verify_iovec() or verify_compat_iovec() are called. These functions  
don't really do any verification of arguments from user-space.  
Instead, they merely copy them to kernel-space and determine the total  
length requested by adding up all the iovec iov_len sizes.  
Unfortunately, the return value is used both for error checking and  
for returning the total length. Thus, if the total length overflows a  
signed integer the functions will return an error. This is where  
verify_iovec() and verify_compat_iovec() differ. In verify_iovec(), if  
the total length is negative at any point, the function returns  
-EMSGSIZE:

        int err;
<snip>
        for (ct = 0; ct < m->msg_iovlen; ct++) {
                err += iov[ct].iov_len;
                /*
                 * Goal is not to verify user data, but to prevent returning
                 * negative value, which is interpreted as errno.
                 * Overflow is still possible, but it is harmless.
                 */
                if (err < 0)
                        return -EMSGSIZE;
        }

In verify_compat_iovec(), the total size is returned no matter what,  
and it doesn't return -EMSGSIZE if the total size is negative:

        int tot_len = 0;

        while (niov > 0) {
                compat_uptr_t buf;
                compat_size_t len;

                if (get_user(len, &uiov32->iov_len) ||
                   get_user(buf, &uiov32->iov_base)) {
                        tot_len = -EFAULT;
                        break;
                }
                tot_len += len;
                kiov->iov_base = compat_ptr(buf);
                kiov->iov_len = (__kernel_size_t) len;
                uiov32++;
                kiov++;
                niov--;
        }
        return tot_len;

The returned value from verify_compat_iovec() is then passed back to  
userspace. This explains why I received EPERM (-1) when I set iov_len  
to -1. This breaks SUSv3 compliance. Any issues with iov_len should  
set errno to EMSGSIZE.

Also, verify_iovec() can be defeated by using iov_lens of 2 and -1,  
and verify_iovec_compat() can be defeated by using the same values or  
the same values swapped in order.

A correct solution to this issue would be to save the total length in  
a variable passed by reference into verify*iovec(), and return 0 on  
success or an errno if the total length overflows. The total length  
could then be saved as a size_t, which doubles the total amount able  
to be received due to its unsigned nature.

Before I work up a patch and test it, I would like some feedback as to  
whether this approach is acceptable for upstream.

Thanks,
Chase Douglas


                 reply	other threads:[~2009-08-04 14:14 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090804101411.y5fmtgh2skwkw4gc@imap.linux.ibm.com \
    --to=cndougla@linux.vnet.ibm.com \
    --cc=netdev@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox