You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
kmemcheck: rip it out
Fix up makefiles, remove references, and git rm kmemcheck. Link: http://lkml.kernel.org/r/20171007030159.22241-4-alexander.levin@verizon.com Signed-off-by: Sasha Levin <alexander.levin@verizon.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Vegard Nossum <vegardno@ifi.uio.no> Cc: Pekka Enberg <penberg@kernel.org> Cc: Michal Hocko <mhocko@kernel.org> Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Alexander Potapenko <glider@google.com> Cc: Tim Hansen <devtimhansen@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
committed by
Linus Torvalds
parent
d8be75663c
commit
4675ff05de
@@ -1864,13 +1864,6 @@
|
|||||||
Built with CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y,
|
Built with CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y,
|
||||||
the default is off.
|
the default is off.
|
||||||
|
|
||||||
kmemcheck= [X86] Boot-time kmemcheck enable/disable/one-shot mode
|
|
||||||
Valid arguments: 0, 1, 2
|
|
||||||
kmemcheck=0 (disabled)
|
|
||||||
kmemcheck=1 (enabled)
|
|
||||||
kmemcheck=2 (one-shot mode)
|
|
||||||
Default: 2 (one-shot mode)
|
|
||||||
|
|
||||||
kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs.
|
kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs.
|
||||||
Default is 0 (don't ignore, but inject #GP)
|
Default is 0 (don't ignore, but inject #GP)
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ whole; patches welcome!
|
|||||||
kasan
|
kasan
|
||||||
ubsan
|
ubsan
|
||||||
kmemleak
|
kmemleak
|
||||||
kmemcheck
|
|
||||||
gdb-kernel-debugging
|
gdb-kernel-debugging
|
||||||
kgdb
|
kgdb
|
||||||
kselftest
|
kselftest
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
-10
@@ -7688,16 +7688,6 @@ F: include/linux/kdb.h
|
|||||||
F: include/linux/kgdb.h
|
F: include/linux/kgdb.h
|
||||||
F: kernel/debug/
|
F: kernel/debug/
|
||||||
|
|
||||||
KMEMCHECK
|
|
||||||
M: Vegard Nossum <vegardno@ifi.uio.no>
|
|
||||||
M: Pekka Enberg <penberg@kernel.org>
|
|
||||||
S: Maintained
|
|
||||||
F: Documentation/dev-tools/kmemcheck.rst
|
|
||||||
F: arch/x86/include/asm/kmemcheck.h
|
|
||||||
F: arch/x86/mm/kmemcheck/
|
|
||||||
F: include/linux/kmemcheck.h
|
|
||||||
F: mm/kmemcheck.c
|
|
||||||
|
|
||||||
KMEMLEAK
|
KMEMLEAK
|
||||||
M: Catalin Marinas <catalin.marinas@arm.com>
|
M: Catalin Marinas <catalin.marinas@arm.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|||||||
+1
-2
@@ -112,7 +112,6 @@ config X86
|
|||||||
select HAVE_ARCH_JUMP_LABEL
|
select HAVE_ARCH_JUMP_LABEL
|
||||||
select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
|
select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
|
||||||
select HAVE_ARCH_KGDB
|
select HAVE_ARCH_KGDB
|
||||||
select HAVE_ARCH_KMEMCHECK
|
|
||||||
select HAVE_ARCH_MMAP_RND_BITS if MMU
|
select HAVE_ARCH_MMAP_RND_BITS if MMU
|
||||||
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
|
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT
|
||||||
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
|
select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT
|
||||||
@@ -1430,7 +1429,7 @@ config ARCH_DMA_ADDR_T_64BIT
|
|||||||
|
|
||||||
config X86_DIRECT_GBPAGES
|
config X86_DIRECT_GBPAGES
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on X86_64 && !DEBUG_PAGEALLOC && !KMEMCHECK
|
depends on X86_64 && !DEBUG_PAGEALLOC
|
||||||
---help---
|
---help---
|
||||||
Certain kernel features effectively disable kernel
|
Certain kernel features effectively disable kernel
|
||||||
linear 1 GB mappings (even if the CPU otherwise
|
linear 1 GB mappings (even if the CPU otherwise
|
||||||
|
|||||||
@@ -1,43 +1 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef ASM_X86_KMEMCHECK_H
|
|
||||||
#define ASM_X86_KMEMCHECK_H
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_KMEMCHECK
|
|
||||||
bool kmemcheck_active(struct pt_regs *regs);
|
|
||||||
|
|
||||||
void kmemcheck_show(struct pt_regs *regs);
|
|
||||||
void kmemcheck_hide(struct pt_regs *regs);
|
|
||||||
|
|
||||||
bool kmemcheck_fault(struct pt_regs *regs,
|
|
||||||
unsigned long address, unsigned long error_code);
|
|
||||||
bool kmemcheck_trap(struct pt_regs *regs);
|
|
||||||
#else
|
|
||||||
static inline bool kmemcheck_active(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void kmemcheck_show(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void kmemcheck_hide(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool kmemcheck_fault(struct pt_regs *regs,
|
|
||||||
unsigned long address, unsigned long error_code)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool kmemcheck_trap(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_KMEMCHECK */
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -179,8 +179,6 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
|
|||||||
* No 3D Now!
|
* No 3D Now!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CONFIG_KMEMCHECK
|
|
||||||
|
|
||||||
#if (__GNUC__ >= 4)
|
#if (__GNUC__ >= 4)
|
||||||
#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
|
#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
|
||||||
#else
|
#else
|
||||||
@@ -189,13 +187,6 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
|
|||||||
? __constant_memcpy((t), (f), (n)) \
|
? __constant_memcpy((t), (f), (n)) \
|
||||||
: __memcpy((t), (f), (n)))
|
: __memcpy((t), (f), (n)))
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* kmemcheck becomes very happy if we use the REP instructions unconditionally,
|
|
||||||
* because it means that we know both memory operands in advance.
|
|
||||||
*/
|
|
||||||
#define memcpy(t, f, n) __memcpy((t), (f), (n))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif /* !CONFIG_FORTIFY_SOURCE */
|
#endif /* !CONFIG_FORTIFY_SOURCE */
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ extern void *memcpy(void *to, const void *from, size_t len);
|
|||||||
extern void *__memcpy(void *to, const void *from, size_t len);
|
extern void *__memcpy(void *to, const void *from, size_t len);
|
||||||
|
|
||||||
#ifndef CONFIG_FORTIFY_SOURCE
|
#ifndef CONFIG_FORTIFY_SOURCE
|
||||||
#ifndef CONFIG_KMEMCHECK
|
|
||||||
#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
|
#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
|
||||||
#define memcpy(dst, src, len) \
|
#define memcpy(dst, src, len) \
|
||||||
({ \
|
({ \
|
||||||
@@ -46,13 +45,6 @@ extern void *__memcpy(void *to, const void *from, size_t len);
|
|||||||
__ret; \
|
__ret; \
|
||||||
})
|
})
|
||||||
#endif
|
#endif
|
||||||
#else
|
|
||||||
/*
|
|
||||||
* kmemcheck becomes very happy if we use the REP instructions unconditionally,
|
|
||||||
* because it means that we know both memory operands in advance.
|
|
||||||
*/
|
|
||||||
#define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len))
|
|
||||||
#endif
|
|
||||||
#endif /* !CONFIG_FORTIFY_SOURCE */
|
#endif /* !CONFIG_FORTIFY_SOURCE */
|
||||||
|
|
||||||
#define __HAVE_ARCH_MEMSET
|
#define __HAVE_ARCH_MEMSET
|
||||||
|
|||||||
@@ -187,21 +187,6 @@ static void early_init_intel(struct cpuinfo_x86 *c)
|
|||||||
if (c->x86 == 6 && c->x86_model < 15)
|
if (c->x86 == 6 && c->x86_model < 15)
|
||||||
clear_cpu_cap(c, X86_FEATURE_PAT);
|
clear_cpu_cap(c, X86_FEATURE_PAT);
|
||||||
|
|
||||||
#ifdef CONFIG_KMEMCHECK
|
|
||||||
/*
|
|
||||||
* P4s have a "fast strings" feature which causes single-
|
|
||||||
* stepping REP instructions to only generate a #DB on
|
|
||||||
* cache-line boundaries.
|
|
||||||
*
|
|
||||||
* Ingo Molnar reported a Pentium D (model 6) and a Xeon
|
|
||||||
* (model 2) with the same problem.
|
|
||||||
*/
|
|
||||||
if (c->x86 == 15)
|
|
||||||
if (msr_clear_bit(MSR_IA32_MISC_ENABLE,
|
|
||||||
MSR_IA32_MISC_ENABLE_FAST_STRING_BIT) > 0)
|
|
||||||
pr_info("kmemcheck: Disabling fast string operations\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If fast string is not enabled in IA32_MISC_ENABLE for any reason,
|
* If fast string is not enabled in IA32_MISC_ENABLE for any reason,
|
||||||
* clear the fast string and enhanced fast string CPU capabilities.
|
* clear the fast string and enhanced fast string CPU capabilities.
|
||||||
|
|||||||
@@ -29,8 +29,6 @@ obj-$(CONFIG_X86_PTDUMP) += debug_pagetables.o
|
|||||||
|
|
||||||
obj-$(CONFIG_HIGHMEM) += highmem_32.o
|
obj-$(CONFIG_HIGHMEM) += highmem_32.o
|
||||||
|
|
||||||
obj-$(CONFIG_KMEMCHECK) += kmemcheck/
|
|
||||||
|
|
||||||
KASAN_SANITIZE_kasan_init_$(BITS).o := n
|
KASAN_SANITIZE_kasan_init_$(BITS).o := n
|
||||||
obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o
|
obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -163,12 +163,11 @@ static int page_size_mask;
|
|||||||
static void __init probe_page_size_mask(void)
|
static void __init probe_page_size_mask(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* For CONFIG_KMEMCHECK or pagealloc debugging, identity mapping will
|
* For pagealloc debugging, identity mapping will use small pages.
|
||||||
* use small pages.
|
|
||||||
* This will simplify cpa(), which otherwise needs to support splitting
|
* This will simplify cpa(), which otherwise needs to support splitting
|
||||||
* large pages into small in interrupt context, etc.
|
* large pages into small in interrupt context, etc.
|
||||||
*/
|
*/
|
||||||
if (boot_cpu_has(X86_FEATURE_PSE) && !debug_pagealloc_enabled() && !IS_ENABLED(CONFIG_KMEMCHECK))
|
if (boot_cpu_has(X86_FEATURE_PSE) && !debug_pagealloc_enabled())
|
||||||
page_size_mask |= 1 << PG_LEVEL_2M;
|
page_size_mask |= 1 << PG_LEVEL_2M;
|
||||||
else
|
else
|
||||||
direct_gbpages = 0;
|
direct_gbpages = 0;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
obj-y := error.o kmemcheck.o opcode.o pte.o selftest.o shadow.o
|
|
||||||
@@ -1,228 +1 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/kdebug.h>
|
|
||||||
#include <linux/kmemcheck.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/ptrace.h>
|
|
||||||
#include <linux/stacktrace.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
|
|
||||||
#include "error.h"
|
|
||||||
#include "shadow.h"
|
|
||||||
|
|
||||||
enum kmemcheck_error_type {
|
|
||||||
KMEMCHECK_ERROR_INVALID_ACCESS,
|
|
||||||
KMEMCHECK_ERROR_BUG,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SHADOW_COPY_SIZE (1 << CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT)
|
|
||||||
|
|
||||||
struct kmemcheck_error {
|
|
||||||
enum kmemcheck_error_type type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
/* KMEMCHECK_ERROR_INVALID_ACCESS */
|
|
||||||
struct {
|
|
||||||
/* Kind of access that caused the error */
|
|
||||||
enum kmemcheck_shadow state;
|
|
||||||
/* Address and size of the erroneous read */
|
|
||||||
unsigned long address;
|
|
||||||
unsigned int size;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pt_regs regs;
|
|
||||||
struct stack_trace trace;
|
|
||||||
unsigned long trace_entries[32];
|
|
||||||
|
|
||||||
/* We compress it to a char. */
|
|
||||||
unsigned char shadow_copy[SHADOW_COPY_SIZE];
|
|
||||||
unsigned char memory_copy[SHADOW_COPY_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create a ring queue of errors to output. We can't call printk() directly
|
|
||||||
* from the kmemcheck traps, since this may call the console drivers and
|
|
||||||
* result in a recursive fault.
|
|
||||||
*/
|
|
||||||
static struct kmemcheck_error error_fifo[CONFIG_KMEMCHECK_QUEUE_SIZE];
|
|
||||||
static unsigned int error_count;
|
|
||||||
static unsigned int error_rd;
|
|
||||||
static unsigned int error_wr;
|
|
||||||
static unsigned int error_missed_count;
|
|
||||||
|
|
||||||
static struct kmemcheck_error *error_next_wr(void)
|
|
||||||
{
|
|
||||||
struct kmemcheck_error *e;
|
|
||||||
|
|
||||||
if (error_count == ARRAY_SIZE(error_fifo)) {
|
|
||||||
++error_missed_count;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
e = &error_fifo[error_wr];
|
|
||||||
if (++error_wr == ARRAY_SIZE(error_fifo))
|
|
||||||
error_wr = 0;
|
|
||||||
++error_count;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kmemcheck_error *error_next_rd(void)
|
|
||||||
{
|
|
||||||
struct kmemcheck_error *e;
|
|
||||||
|
|
||||||
if (error_count == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
e = &error_fifo[error_rd];
|
|
||||||
if (++error_rd == ARRAY_SIZE(error_fifo))
|
|
||||||
error_rd = 0;
|
|
||||||
--error_count;
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kmemcheck_error_recall(void)
|
|
||||||
{
|
|
||||||
static const char *desc[] = {
|
|
||||||
[KMEMCHECK_SHADOW_UNALLOCATED] = "unallocated",
|
|
||||||
[KMEMCHECK_SHADOW_UNINITIALIZED] = "uninitialized",
|
|
||||||
[KMEMCHECK_SHADOW_INITIALIZED] = "initialized",
|
|
||||||
[KMEMCHECK_SHADOW_FREED] = "freed",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char short_desc[] = {
|
|
||||||
[KMEMCHECK_SHADOW_UNALLOCATED] = 'a',
|
|
||||||
[KMEMCHECK_SHADOW_UNINITIALIZED] = 'u',
|
|
||||||
[KMEMCHECK_SHADOW_INITIALIZED] = 'i',
|
|
||||||
[KMEMCHECK_SHADOW_FREED] = 'f',
|
|
||||||
};
|
|
||||||
|
|
||||||
struct kmemcheck_error *e;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
e = error_next_rd();
|
|
||||||
if (!e)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (e->type) {
|
|
||||||
case KMEMCHECK_ERROR_INVALID_ACCESS:
|
|
||||||
printk(KERN_WARNING "WARNING: kmemcheck: Caught %d-bit read from %s memory (%p)\n",
|
|
||||||
8 * e->size, e->state < ARRAY_SIZE(desc) ?
|
|
||||||
desc[e->state] : "(invalid shadow state)",
|
|
||||||
(void *) e->address);
|
|
||||||
|
|
||||||
printk(KERN_WARNING);
|
|
||||||
for (i = 0; i < SHADOW_COPY_SIZE; ++i)
|
|
||||||
printk(KERN_CONT "%02x", e->memory_copy[i]);
|
|
||||||
printk(KERN_CONT "\n");
|
|
||||||
|
|
||||||
printk(KERN_WARNING);
|
|
||||||
for (i = 0; i < SHADOW_COPY_SIZE; ++i) {
|
|
||||||
if (e->shadow_copy[i] < ARRAY_SIZE(short_desc))
|
|
||||||
printk(KERN_CONT " %c", short_desc[e->shadow_copy[i]]);
|
|
||||||
else
|
|
||||||
printk(KERN_CONT " ?");
|
|
||||||
}
|
|
||||||
printk(KERN_CONT "\n");
|
|
||||||
printk(KERN_WARNING "%*c\n", 2 + 2
|
|
||||||
* (int) (e->address & (SHADOW_COPY_SIZE - 1)), '^');
|
|
||||||
break;
|
|
||||||
case KMEMCHECK_ERROR_BUG:
|
|
||||||
printk(KERN_EMERG "ERROR: kmemcheck: Fatal error\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
__show_regs(&e->regs, 1);
|
|
||||||
print_stack_trace(&e->trace, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_wakeup(unsigned long data)
|
|
||||||
{
|
|
||||||
while (error_count > 0)
|
|
||||||
kmemcheck_error_recall();
|
|
||||||
|
|
||||||
if (error_missed_count > 0) {
|
|
||||||
printk(KERN_WARNING "kmemcheck: Lost %d error reports because "
|
|
||||||
"the queue was too small\n", error_missed_count);
|
|
||||||
error_missed_count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static DECLARE_TASKLET(kmemcheck_tasklet, &do_wakeup, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save the context of an error report.
|
|
||||||
*/
|
|
||||||
void kmemcheck_error_save(enum kmemcheck_shadow state,
|
|
||||||
unsigned long address, unsigned int size, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
static unsigned long prev_ip;
|
|
||||||
|
|
||||||
struct kmemcheck_error *e;
|
|
||||||
void *shadow_copy;
|
|
||||||
void *memory_copy;
|
|
||||||
|
|
||||||
/* Don't report several adjacent errors from the same EIP. */
|
|
||||||
if (regs->ip == prev_ip)
|
|
||||||
return;
|
|
||||||
prev_ip = regs->ip;
|
|
||||||
|
|
||||||
e = error_next_wr();
|
|
||||||
if (!e)
|
|
||||||
return;
|
|
||||||
|
|
||||||
e->type = KMEMCHECK_ERROR_INVALID_ACCESS;
|
|
||||||
|
|
||||||
e->state = state;
|
|
||||||
e->address = address;
|
|
||||||
e->size = size;
|
|
||||||
|
|
||||||
/* Save regs */
|
|
||||||
memcpy(&e->regs, regs, sizeof(*regs));
|
|
||||||
|
|
||||||
/* Save stack trace */
|
|
||||||
e->trace.nr_entries = 0;
|
|
||||||
e->trace.entries = e->trace_entries;
|
|
||||||
e->trace.max_entries = ARRAY_SIZE(e->trace_entries);
|
|
||||||
e->trace.skip = 0;
|
|
||||||
save_stack_trace_regs(regs, &e->trace);
|
|
||||||
|
|
||||||
/* Round address down to nearest 16 bytes */
|
|
||||||
shadow_copy = kmemcheck_shadow_lookup(address
|
|
||||||
& ~(SHADOW_COPY_SIZE - 1));
|
|
||||||
BUG_ON(!shadow_copy);
|
|
||||||
|
|
||||||
memcpy(e->shadow_copy, shadow_copy, SHADOW_COPY_SIZE);
|
|
||||||
|
|
||||||
kmemcheck_show_addr(address);
|
|
||||||
memory_copy = (void *) (address & ~(SHADOW_COPY_SIZE - 1));
|
|
||||||
memcpy(e->memory_copy, memory_copy, SHADOW_COPY_SIZE);
|
|
||||||
kmemcheck_hide_addr(address);
|
|
||||||
|
|
||||||
tasklet_hi_schedule_first(&kmemcheck_tasklet);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Save the context of a kmemcheck bug.
|
|
||||||
*/
|
|
||||||
void kmemcheck_error_save_bug(struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct kmemcheck_error *e;
|
|
||||||
|
|
||||||
e = error_next_wr();
|
|
||||||
if (!e)
|
|
||||||
return;
|
|
||||||
|
|
||||||
e->type = KMEMCHECK_ERROR_BUG;
|
|
||||||
|
|
||||||
memcpy(&e->regs, regs, sizeof(*regs));
|
|
||||||
|
|
||||||
e->trace.nr_entries = 0;
|
|
||||||
e->trace.entries = e->trace_entries;
|
|
||||||
e->trace.max_entries = ARRAY_SIZE(e->trace_entries);
|
|
||||||
e->trace.skip = 1;
|
|
||||||
save_stack_trace(&e->trace);
|
|
||||||
|
|
||||||
tasklet_hi_schedule_first(&kmemcheck_tasklet);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef ARCH__X86__MM__KMEMCHECK__ERROR_H
|
|
||||||
#define ARCH__X86__MM__KMEMCHECK__ERROR_H
|
|
||||||
|
|
||||||
#include <linux/ptrace.h>
|
|
||||||
|
|
||||||
#include "shadow.h"
|
|
||||||
|
|
||||||
void kmemcheck_error_save(enum kmemcheck_shadow state,
|
|
||||||
unsigned long address, unsigned int size, struct pt_regs *regs);
|
|
||||||
|
|
||||||
void kmemcheck_error_save_bug(struct pt_regs *regs);
|
|
||||||
|
|
||||||
void kmemcheck_error_recall(void);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,107 +1 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
#include "opcode.h"
|
|
||||||
|
|
||||||
static bool opcode_is_prefix(uint8_t b)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
/* Group 1 */
|
|
||||||
b == 0xf0 || b == 0xf2 || b == 0xf3
|
|
||||||
/* Group 2 */
|
|
||||||
|| b == 0x2e || b == 0x36 || b == 0x3e || b == 0x26
|
|
||||||
|| b == 0x64 || b == 0x65
|
|
||||||
/* Group 3 */
|
|
||||||
|| b == 0x66
|
|
||||||
/* Group 4 */
|
|
||||||
|| b == 0x67;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
|
||||||
static bool opcode_is_rex_prefix(uint8_t b)
|
|
||||||
{
|
|
||||||
return (b & 0xf0) == 0x40;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static bool opcode_is_rex_prefix(uint8_t b)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define REX_W (1 << 3)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a VERY crude opcode decoder. We only need to find the size of the
|
|
||||||
* load/store that caused our #PF and this should work for all the opcodes
|
|
||||||
* that we care about. Moreover, the ones who invented this instruction set
|
|
||||||
* should be shot.
|
|
||||||
*/
|
|
||||||
void kmemcheck_opcode_decode(const uint8_t *op, unsigned int *size)
|
|
||||||
{
|
|
||||||
/* Default operand size */
|
|
||||||
int operand_size_override = 4;
|
|
||||||
|
|
||||||
/* prefixes */
|
|
||||||
for (; opcode_is_prefix(*op); ++op) {
|
|
||||||
if (*op == 0x66)
|
|
||||||
operand_size_override = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* REX prefix */
|
|
||||||
if (opcode_is_rex_prefix(*op)) {
|
|
||||||
uint8_t rex = *op;
|
|
||||||
|
|
||||||
++op;
|
|
||||||
if (rex & REX_W) {
|
|
||||||
switch (*op) {
|
|
||||||
case 0x63:
|
|
||||||
*size = 4;
|
|
||||||
return;
|
|
||||||
case 0x0f:
|
|
||||||
++op;
|
|
||||||
|
|
||||||
switch (*op) {
|
|
||||||
case 0xb6:
|
|
||||||
case 0xbe:
|
|
||||||
*size = 1;
|
|
||||||
return;
|
|
||||||
case 0xb7:
|
|
||||||
case 0xbf:
|
|
||||||
*size = 2;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = 8;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* escape opcode */
|
|
||||||
if (*op == 0x0f) {
|
|
||||||
++op;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is move with zero-extend and sign-extend, respectively;
|
|
||||||
* we don't have to think about 0xb6/0xbe, because this is
|
|
||||||
* already handled in the conditional below.
|
|
||||||
*/
|
|
||||||
if (*op == 0xb7 || *op == 0xbf)
|
|
||||||
operand_size_override = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
*size = (*op & 1) ? operand_size_override : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *kmemcheck_opcode_get_primary(const uint8_t *op)
|
|
||||||
{
|
|
||||||
/* skip prefixes */
|
|
||||||
while (opcode_is_prefix(*op))
|
|
||||||
++op;
|
|
||||||
if (opcode_is_rex_prefix(*op))
|
|
||||||
++op;
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef ARCH__X86__MM__KMEMCHECK__OPCODE_H
|
|
||||||
#define ARCH__X86__MM__KMEMCHECK__OPCODE_H
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
void kmemcheck_opcode_decode(const uint8_t *op, unsigned int *size);
|
|
||||||
const uint8_t *kmemcheck_opcode_get_primary(const uint8_t *op);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,23 +1 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/mm.h>
|
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
|
||||||
|
|
||||||
#include "pte.h"
|
|
||||||
|
|
||||||
pte_t *kmemcheck_pte_lookup(unsigned long address)
|
|
||||||
{
|
|
||||||
pte_t *pte;
|
|
||||||
unsigned int level;
|
|
||||||
|
|
||||||
pte = lookup_address(address, &level);
|
|
||||||
if (!pte)
|
|
||||||
return NULL;
|
|
||||||
if (level != PG_LEVEL_4K)
|
|
||||||
return NULL;
|
|
||||||
if (!pte_hidden(*pte))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return pte;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef ARCH__X86__MM__KMEMCHECK__PTE_H
|
|
||||||
#define ARCH__X86__MM__KMEMCHECK__PTE_H
|
|
||||||
|
|
||||||
#include <linux/mm.h>
|
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
|
||||||
|
|
||||||
pte_t *kmemcheck_pte_lookup(unsigned long address);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1,71 +1 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/bug.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
|
|
||||||
#include "opcode.h"
|
|
||||||
#include "selftest.h"
|
|
||||||
|
|
||||||
struct selftest_opcode {
|
|
||||||
unsigned int expected_size;
|
|
||||||
const uint8_t *insn;
|
|
||||||
const char *desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct selftest_opcode selftest_opcodes[] = {
|
|
||||||
/* REP MOVS */
|
|
||||||
{1, "\xf3\xa4", "rep movsb <mem8>, <mem8>"},
|
|
||||||
{4, "\xf3\xa5", "rep movsl <mem32>, <mem32>"},
|
|
||||||
|
|
||||||
/* MOVZX / MOVZXD */
|
|
||||||
{1, "\x66\x0f\xb6\x51\xf8", "movzwq <mem8>, <reg16>"},
|
|
||||||
{1, "\x0f\xb6\x51\xf8", "movzwq <mem8>, <reg32>"},
|
|
||||||
|
|
||||||
/* MOVSX / MOVSXD */
|
|
||||||
{1, "\x66\x0f\xbe\x51\xf8", "movswq <mem8>, <reg16>"},
|
|
||||||
{1, "\x0f\xbe\x51\xf8", "movswq <mem8>, <reg32>"},
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
|
||||||
/* MOVZX / MOVZXD */
|
|
||||||
{1, "\x49\x0f\xb6\x51\xf8", "movzbq <mem8>, <reg64>"},
|
|
||||||
{2, "\x49\x0f\xb7\x51\xf8", "movzbq <mem16>, <reg64>"},
|
|
||||||
|
|
||||||
/* MOVSX / MOVSXD */
|
|
||||||
{1, "\x49\x0f\xbe\x51\xf8", "movsbq <mem8>, <reg64>"},
|
|
||||||
{2, "\x49\x0f\xbf\x51\xf8", "movsbq <mem16>, <reg64>"},
|
|
||||||
{4, "\x49\x63\x51\xf8", "movslq <mem32>, <reg64>"},
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool selftest_opcode_one(const struct selftest_opcode *op)
|
|
||||||
{
|
|
||||||
unsigned size;
|
|
||||||
|
|
||||||
kmemcheck_opcode_decode(op->insn, &size);
|
|
||||||
|
|
||||||
if (size == op->expected_size)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
printk(KERN_WARNING "kmemcheck: opcode %s: expected size %d, got %d\n",
|
|
||||||
op->desc, op->expected_size, size);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool selftest_opcodes_all(void)
|
|
||||||
{
|
|
||||||
bool pass = true;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(selftest_opcodes); ++i)
|
|
||||||
pass = pass && selftest_opcode_one(&selftest_opcodes[i]);
|
|
||||||
|
|
||||||
return pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool kmemcheck_selftest(void)
|
|
||||||
{
|
|
||||||
bool pass = true;
|
|
||||||
|
|
||||||
pass = pass && selftest_opcodes_all();
|
|
||||||
|
|
||||||
return pass;
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user