Commit 7c201739 authored by Marco Elver's avatar Marco Elver Committed by Paul E. McKenney
Browse files

kcsan: Instrument memcpy/memset/memmove with newer Clang



With Clang version 16+, -fsanitize=thread will turn
memcpy/memset/memmove calls in instrumented functions into
__tsan_memcpy/__tsan_memset/__tsan_memmove calls respectively.

Add these functions to the core KCSAN runtime, so that we (a) catch data
races with mem* functions, and (b) won't run into linker errors with
such newer compilers.

Cc: stable@vger.kernel.org # v5.10+
Signed-off-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent 9abf2313
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -14,10 +14,12 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/uaccess.h>

#include "encoding.h"
@@ -1308,3 +1310,51 @@ noinline void __tsan_atomic_signal_fence(int memorder)
	}
}
EXPORT_SYMBOL(__tsan_atomic_signal_fence);

#ifdef __HAVE_ARCH_MEMSET
void *__tsan_memset(void *s, int c, size_t count);
noinline void *__tsan_memset(void *s, int c, size_t count)
{
	/*
	 * Instead of not setting up watchpoints where accessed size is greater
	 * than MAX_ENCODABLE_SIZE, truncate checked size to MAX_ENCODABLE_SIZE.
	 */
	size_t check_len = min_t(size_t, count, MAX_ENCODABLE_SIZE);

	check_access(s, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
	return memset(s, c, count);
}
#else
void *__tsan_memset(void *s, int c, size_t count) __alias(memset);
#endif
EXPORT_SYMBOL(__tsan_memset);

#ifdef __HAVE_ARCH_MEMMOVE
void *__tsan_memmove(void *dst, const void *src, size_t len);
noinline void *__tsan_memmove(void *dst, const void *src, size_t len)
{
	size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);

	check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
	check_access(src, check_len, 0, _RET_IP_);
	return memmove(dst, src, len);
}
#else
void *__tsan_memmove(void *dst, const void *src, size_t len) __alias(memmove);
#endif
EXPORT_SYMBOL(__tsan_memmove);

#ifdef __HAVE_ARCH_MEMCPY
void *__tsan_memcpy(void *dst, const void *src, size_t len);
noinline void *__tsan_memcpy(void *dst, const void *src, size_t len)
{
	size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);

	check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
	check_access(src, check_len, 0, _RET_IP_);
	return memcpy(dst, src, len);
}
#else
void *__tsan_memcpy(void *dst, const void *src, size_t len) __alias(memcpy);
#endif
EXPORT_SYMBOL(__tsan_memcpy);