Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

instrumented.h: add KMSAN support

To avoid false positives, KMSAN needs to unpoison the data copied from the
userspace. To detect infoleaks - check the memory buffer passed to
copy_to_user().

Link: https://lkml.kernel.org/r/20220915150417.722975-19-glider@google.com
Signed-off-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Marco Elver <elver@google.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Christoph Lameter <cl@linux.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Eric Biggers <ebiggers@google.com>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Stephen Rothwell <sfr@canb.auug.org.au>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Vegard Nossum <vegard.nossum@oracle.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Alexander Potapenko and committed by
Andrew Morton
75cf0290 3c206509

+70 -5
+13 -5
include/linux/instrumented.h
··· 2 2 3 3 /* 4 4 * This header provides generic wrappers for memory access instrumentation that 5 - * the compiler cannot emit for: KASAN, KCSAN. 5 + * the compiler cannot emit for: KASAN, KCSAN, KMSAN. 6 6 */ 7 7 #ifndef _LINUX_INSTRUMENTED_H 8 8 #define _LINUX_INSTRUMENTED_H ··· 10 10 #include <linux/compiler.h> 11 11 #include <linux/kasan-checks.h> 12 12 #include <linux/kcsan-checks.h> 13 + #include <linux/kmsan-checks.h> 13 14 #include <linux/types.h> 14 15 15 16 /** ··· 118 117 { 119 118 kasan_check_read(from, n); 120 119 kcsan_check_read(from, n); 120 + kmsan_copy_to_user(to, from, n, 0); 121 121 } 122 122 123 123 /** ··· 153 151 instrument_copy_from_user_after(const void *to, const void __user *from, 154 152 unsigned long n, unsigned long left) 155 153 { 154 + kmsan_unpoison_memory(to, n - left); 156 155 } 157 156 158 157 /** ··· 165 162 * 166 163 * @to destination variable, may not be address-taken 167 164 */ 168 - #define instrument_get_user(to) \ 169 - ({ \ 165 + #define instrument_get_user(to) \ 166 + ({ \ 167 + u64 __tmp = (u64)(to); \ 168 + kmsan_unpoison_memory(&__tmp, sizeof(__tmp)); \ 169 + to = __tmp; \ 170 170 }) 171 + 171 172 172 173 /** 173 174 * instrument_put_user() - add instrumentation to put_user()-like macros ··· 184 177 * @ptr userspace pointer to copy to 185 178 * @size number of bytes to copy 186 179 */ 187 - #define instrument_put_user(from, ptr, size) \ 188 - ({ \ 180 + #define instrument_put_user(from, ptr, size) \ 181 + ({ \ 182 + kmsan_copy_to_user(ptr, &from, sizeof(from), 0); \ 189 183 }) 190 184 191 185 #endif /* _LINUX_INSTRUMENTED_H */
+19
include/linux/kmsan-checks.h
··· 46 46 */ 47 47 void kmsan_check_memory(const void *address, size_t size); 48 48 49 + /** 50 + * kmsan_copy_to_user() - Notify KMSAN about a data transfer to userspace. 51 + * @to: destination address in the userspace. 52 + * @from: source address in the kernel. 53 + * @to_copy: number of bytes to copy. 54 + * @left: number of bytes not copied. 55 + * 56 + * If this is a real userspace data transfer, KMSAN checks the bytes that were 57 + * actually copied to ensure there was no information leak. If @to belongs to 58 + * the kernel space (which is possible for compat syscalls), KMSAN just copies 59 + * the metadata. 60 + */ 61 + void kmsan_copy_to_user(void __user *to, const void *from, size_t to_copy, 62 + size_t left); 63 + 49 64 #else 50 65 51 66 static inline void kmsan_poison_memory(const void *address, size_t size, ··· 71 56 { 72 57 } 73 58 static inline void kmsan_check_memory(const void *address, size_t size) 59 + { 60 + } 61 + static inline void kmsan_copy_to_user(void __user *to, const void *from, 62 + size_t to_copy, size_t left) 74 63 { 75 64 } 76 65
+38
mm/kmsan/hooks.c
··· 205 205 kmsan_leave_runtime(); 206 206 } 207 207 208 + void kmsan_copy_to_user(void __user *to, const void *from, size_t to_copy, 209 + size_t left) 210 + { 211 + unsigned long ua_flags; 212 + 213 + if (!kmsan_enabled || kmsan_in_runtime()) 214 + return; 215 + /* 216 + * At this point we've copied the memory already. It's hard to check it 217 + * before copying, as the size of actually copied buffer is unknown. 218 + */ 219 + 220 + /* copy_to_user() may copy zero bytes. No need to check. */ 221 + if (!to_copy) 222 + return; 223 + /* Or maybe copy_to_user() failed to copy anything. */ 224 + if (to_copy <= left) 225 + return; 226 + 227 + ua_flags = user_access_save(); 228 + if ((u64)to < TASK_SIZE) { 229 + /* This is a user memory access, check it. */ 230 + kmsan_internal_check_memory((void *)from, to_copy - left, to, 231 + REASON_COPY_TO_USER); 232 + } else { 233 + /* Otherwise this is a kernel memory access. This happens when a 234 + * compat syscall passes an argument allocated on the kernel 235 + * stack to a real syscall. 236 + * Don't check anything, just copy the shadow of the copied 237 + * bytes. 238 + */ 239 + kmsan_internal_memmove_metadata((void *)to, (void *)from, 240 + to_copy - left); 241 + } 242 + user_access_restore(ua_flags); 243 + } 244 + EXPORT_SYMBOL(kmsan_copy_to_user); 245 + 208 246 /* Functions from kmsan-checks.h follow. */ 209 247 void kmsan_poison_memory(const void *address, size_t size, gfp_t flags) 210 248 {