11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * iovec manipulation routines. 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU General Public License 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 2 of the License, or (at your option) any later version. 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Fixes: 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Andrew Lunn : Errors in iovec copying. 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Pedro Roque : Added memcpy_fromiovecend and 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * csum_..._fromiovecend. 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Andi Kleen : fixed error handling for 2.1 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Alexey Kuznetsov: 2.1 optimisations 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Andi Kleen : Fix csum*fromiovecend for IPv6. 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/net.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/in6.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/checksum.h> 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/sock.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Verify iovec. The caller must ensure that the iovec is big enough 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to hold the message iovec. 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 34e49332bd12e92da2df6d002f857ec62675ba2648Jesper Juhl * Save time not doing access_ok. copy_*_user will make this work 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in any case. 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3843db362d3adda9e0a915ddb9a8d1a41186e19179Maciej Żenczykowskiint verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 408acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller int size, ct, err; 414ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki 4240eea803c6b2cfaab092f053248cbeab3f368412Andrey Ryabinin if (m->msg_name && m->msg_namelen) { 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mode == VERIFY_READ) { 44a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim void __user *namep; 45a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim namep = (void __user __force *) m->msg_name; 46a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim err = move_addr_to_kernel(namep, m->msg_namelen, 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds address); 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err < 0) 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5140eea803c6b2cfaab092f053248cbeab3f368412Andrey Ryabinin m->msg_name = address; 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds m->msg_name = NULL; 5440eea803c6b2cfaab092f053248cbeab3f368412Andrey Ryabinin m->msg_namelen = 0; 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = m->msg_iovlen * sizeof(struct iovec); 58a700d8be733bd593ea4797dfde17aed4f35213c0Namhyung Kim if (copy_from_user(iov, (void __user __force *) m->msg_iov, size)) 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds m->msg_iov = iov; 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = 0; 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (ct = 0; ct < m->msg_iovlen; ct++) { 658acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller size_t len = iov[ct].iov_len; 668acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller 678acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller if (len > INT_MAX - err) { 688acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller len = INT_MAX - err; 698acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller iov[ct].iov_len = len; 708acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller } 718acfe468b0384e834a303f08ebc4953d72fb690aDavid S. Miller err += len; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * And now for the all-in-one: copy and checksum from a user iovec 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * directly to a datagram 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Calls to csum_partial but the last must be in 32 bit chunks 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ip_build_xmit must ensure that when fragmenting only the last 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * call to this function will be unaligned also. 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, 8644bb93633f57a55979f3c2589b10fd6a2bfc7c08Al Viro int offset, unsigned int len, __wsum *csump) 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8844bb93633f57a55979f3c2589b10fd6a2bfc7c08Al Viro __wsum csum = *csump; 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int partial_cnt = 0, err = 0; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Skip over the finished iovecs */ 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (offset >= iov->iov_len) { 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset -= iov->iov_len; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iov++; 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (len > 0) { 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 __user *base = iov->iov_base + offset; 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int copy = min_t(unsigned int, len, iov->iov_len - offset); 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset = 0; 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* There is a remnant from previous iov. */ 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (partial_cnt) { 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int par_len = 4 - partial_cnt; 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* iov component is too short ... */ 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (par_len > copy) { 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_from_user(kdata, base, copy)) 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out_fault; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kdata += copy; 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base += copy; 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds partial_cnt += copy; 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= copy; 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iov++; 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len) 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *csump = csum_partial(kdata - partial_cnt, 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds partial_cnt, csum); 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_from_user(kdata, base, par_len)) 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out_fault; 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds csum = csum_partial(kdata - partial_cnt, 4, csum); 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kdata += par_len; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base += par_len; 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds copy -= par_len; 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= par_len; 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds partial_cnt = 0; 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len > copy) { 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds partial_cnt = copy % 4; 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (partial_cnt) { 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds copy -= partial_cnt; 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy_from_user(kdata + copy, base + copy, 1374ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki partial_cnt)) 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out_fault; 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (copy) { 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds csum = csum_and_copy_from_user(base, kdata, copy, 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds csum, &err); 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err) 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len -= copy + partial_cnt; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kdata += copy + partial_cnt; 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iov++; 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1524ec93edb14fe5fdee9fae6335f2cbba204627eacYOSHIFUJI Hideaki *csump = csum; 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout: 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_fault: 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = -EFAULT; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(csum_partial_copy_fromiovecend); 161b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang 162b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wangunsigned long iov_pages(const struct iovec *iov, int offset, 163b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang unsigned long nr_segs) 164b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang{ 165b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang unsigned long seg, base; 166b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang int pages = 0, len, size; 167b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang 168b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang while (nr_segs && (offset >= iov->iov_len)) { 169b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang offset -= iov->iov_len; 170b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang ++iov; 171b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang --nr_segs; 172b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang } 173b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang 174b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang for (seg = 0; seg < nr_segs; seg++) { 175b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang base = (unsigned long)iov[seg].iov_base + offset; 176b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang len = iov[seg].iov_len - offset; 177b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; 178b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang pages += size; 179b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang offset = 0; 180b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang } 181b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang 182b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang return pages; 183b4bf07771faaf959b0a916d35b1b930c030e30a8Jason Wang} 184b4bf07771faaf959b0a916d35b1b930c030e30a8Jason WangEXPORT_SYMBOL(iov_pages); 185