1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 203bc8b0fSChristophe Leroy /* 303bc8b0fSChristophe Leroy * 403bc8b0fSChristophe Leroy * Copyright (C) IBM Corporation, 2010 503bc8b0fSChristophe Leroy * 603bc8b0fSChristophe Leroy * Author: Anton Blanchard <anton@au.ibm.com> 703bc8b0fSChristophe Leroy */ 803bc8b0fSChristophe Leroy #include <linux/export.h> 903bc8b0fSChristophe Leroy #include <linux/compiler.h> 1003bc8b0fSChristophe Leroy #include <linux/types.h> 1103bc8b0fSChristophe Leroy #include <asm/checksum.h> 127c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 1303bc8b0fSChristophe Leroy 1403bc8b0fSChristophe Leroy __wsum csum_and_copy_from_user(const void __user *src, void *dst, 1503bc8b0fSChristophe Leroy int len, __wsum sum, int *err_ptr) 1603bc8b0fSChristophe Leroy { 1703bc8b0fSChristophe Leroy unsigned int csum; 1803bc8b0fSChristophe Leroy 1903bc8b0fSChristophe Leroy might_sleep(); 20de78a9c4SChristophe Leroy allow_read_from_user(src, len); 2103bc8b0fSChristophe Leroy 2203bc8b0fSChristophe Leroy *err_ptr = 0; 2303bc8b0fSChristophe Leroy 2403bc8b0fSChristophe Leroy if (!len) { 2503bc8b0fSChristophe Leroy csum = 0; 2603bc8b0fSChristophe Leroy goto out; 2703bc8b0fSChristophe Leroy } 2803bc8b0fSChristophe Leroy 2996d4f267SLinus Torvalds if (unlikely((len < 0) || !access_ok(src, len))) { 3003bc8b0fSChristophe Leroy *err_ptr = -EFAULT; 3103bc8b0fSChristophe Leroy csum = (__force unsigned int)sum; 3203bc8b0fSChristophe Leroy goto out; 3303bc8b0fSChristophe Leroy } 3403bc8b0fSChristophe Leroy 3503bc8b0fSChristophe Leroy csum = csum_partial_copy_generic((void __force *)src, dst, 3603bc8b0fSChristophe Leroy len, sum, err_ptr, NULL); 3703bc8b0fSChristophe Leroy 3803bc8b0fSChristophe Leroy if (unlikely(*err_ptr)) { 3903bc8b0fSChristophe Leroy int missing = __copy_from_user(dst, src, len); 4003bc8b0fSChristophe Leroy 4103bc8b0fSChristophe Leroy if (missing) { 4203bc8b0fSChristophe Leroy memset(dst + len - missing, 0, missing); 4303bc8b0fSChristophe Leroy *err_ptr = -EFAULT; 4403bc8b0fSChristophe Leroy } else { 4503bc8b0fSChristophe Leroy *err_ptr = 0; 4603bc8b0fSChristophe Leroy } 4703bc8b0fSChristophe Leroy 4803bc8b0fSChristophe Leroy csum = csum_partial(dst, len, sum); 4903bc8b0fSChristophe Leroy } 5003bc8b0fSChristophe Leroy 5103bc8b0fSChristophe Leroy out: 52de78a9c4SChristophe Leroy prevent_read_from_user(src, len); 5303bc8b0fSChristophe Leroy return (__force __wsum)csum; 5403bc8b0fSChristophe Leroy } 5503bc8b0fSChristophe Leroy EXPORT_SYMBOL(csum_and_copy_from_user); 5603bc8b0fSChristophe Leroy 5703bc8b0fSChristophe Leroy __wsum csum_and_copy_to_user(const void *src, void __user *dst, int len, 5803bc8b0fSChristophe Leroy __wsum sum, int *err_ptr) 5903bc8b0fSChristophe Leroy { 6003bc8b0fSChristophe Leroy unsigned int csum; 6103bc8b0fSChristophe Leroy 6203bc8b0fSChristophe Leroy might_sleep(); 63de78a9c4SChristophe Leroy allow_write_to_user(dst, len); 6403bc8b0fSChristophe Leroy 6503bc8b0fSChristophe Leroy *err_ptr = 0; 6603bc8b0fSChristophe Leroy 6703bc8b0fSChristophe Leroy if (!len) { 6803bc8b0fSChristophe Leroy csum = 0; 6903bc8b0fSChristophe Leroy goto out; 7003bc8b0fSChristophe Leroy } 7103bc8b0fSChristophe Leroy 7296d4f267SLinus Torvalds if (unlikely((len < 0) || !access_ok(dst, len))) { 7303bc8b0fSChristophe Leroy *err_ptr = -EFAULT; 7403bc8b0fSChristophe Leroy csum = -1; /* invalid checksum */ 7503bc8b0fSChristophe Leroy goto out; 7603bc8b0fSChristophe Leroy } 7703bc8b0fSChristophe Leroy 7803bc8b0fSChristophe Leroy csum = csum_partial_copy_generic(src, (void __force *)dst, 7903bc8b0fSChristophe Leroy len, sum, NULL, err_ptr); 8003bc8b0fSChristophe Leroy 8103bc8b0fSChristophe Leroy if (unlikely(*err_ptr)) { 8203bc8b0fSChristophe Leroy csum = csum_partial(src, len, sum); 8303bc8b0fSChristophe Leroy 8403bc8b0fSChristophe Leroy if (copy_to_user(dst, src, len)) { 8503bc8b0fSChristophe Leroy *err_ptr = -EFAULT; 8603bc8b0fSChristophe Leroy csum = -1; /* invalid checksum */ 8703bc8b0fSChristophe Leroy } 8803bc8b0fSChristophe Leroy } 8903bc8b0fSChristophe Leroy 9003bc8b0fSChristophe Leroy out: 91de78a9c4SChristophe Leroy prevent_write_to_user(dst, len); 9203bc8b0fSChristophe Leroy return (__force __wsum)csum; 9303bc8b0fSChristophe Leroy } 9403bc8b0fSChristophe Leroy EXPORT_SYMBOL(csum_and_copy_to_user); 95