summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2023-01-31 17:37:59 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2023-03-11 16:43:56 +0100
commit5bc391944d9edab7af1cc9d717b81c1b9c2a2f9a (patch)
tree743dc1e5d5a6c3ad6d51da8844bee3218961173f /include/linux
parent0467681f09477384a8d9c166cb849e9d31cca41f (diff)
downloadlinux-5bc391944d9edab7af1cc9d717b81c1b9c2a2f9a.tar.gz
linux-5bc391944d9edab7af1cc9d717b81c1b9c2a2f9a.tar.bz2
linux-5bc391944d9edab7af1cc9d717b81c1b9c2a2f9a.zip
uaccess: Add minimum bounds check on kernel buffer size
[ Upstream commit 04ffde1319a715bd0550ded3580d4ea3bc003776 ] While there is logic about the difference between ksize and usize, copy_struct_from_user() didn't check the size of the destination buffer (when it was known) against ksize. Add this check so there is an upper bounds check on the possible memset() call, otherwise lower bounds checks made by callers will trigger bounds warnings under -Warray-bounds. Seen under GCC 13: In function 'copy_struct_from_user', inlined from 'iommufd_fops_ioctl' at ../drivers/iommu/iommufd/main.c:333:8: ../include/linux/fortify-string.h:59:33: warning: '__builtin_memset' offset [57, 4294967294] is out of the bounds [0, 56] of object 'buf' with type 'union ucmd_buffer' [-Warray-bounds=] 59 | #define __underlying_memset __builtin_memset | ^ ../include/linux/fortify-string.h:453:9: note: in expansion of macro '__underlying_memset' 453 | __underlying_memset(p, c, __fortify_size); \ | ^~~~~~~~~~~~~~~~~~~ ../include/linux/fortify-string.h:461:25: note: in expansion of macro '__fortify_memset_chk' 461 | #define memset(p, c, s) __fortify_memset_chk(p, c, s, \ | ^~~~~~~~~~~~~~~~~~~~ ../include/linux/uaccess.h:334:17: note: in expansion of macro 'memset' 334 | memset(dst + size, 0, rest); | ^~~~~~ ../drivers/iommu/iommufd/main.c: In function 'iommufd_fops_ioctl': ../drivers/iommu/iommufd/main.c:311:27: note: 'buf' declared here 311 | union ucmd_buffer buf; | ^~~ Cc: Christian Brauner <brauner@kernel.org> Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Dinh Nguyen <dinguyen@kernel.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Alexander Potapenko <glider@google.com> Acked-by: Aleksa Sarai <cyphar@cyphar.com> Signed-off-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/lkml/20230203193523.never.667-kees@kernel.org/ Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/uaccess.h4
1 files changed, 4 insertions, 0 deletions
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 38555435a64a..70941f49d66e 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -287,6 +287,10 @@ copy_struct_from_user(void *dst, size_t ksize, const void __user *src,
size_t size = min(ksize, usize);
size_t rest = max(ksize, usize) - size;
+ /* Double check if ksize is larger than a known object size. */
+ if (WARN_ON_ONCE(ksize > __builtin_object_size(dst, 1)))
+ return -E2BIG;
+
/* Deal with trailing bytes. */
if (usize < ksize) {
memset(dst + size, 0, rest);