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