mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08: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,
|
||||
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.
|
||||
Default is 0 (don't ignore, but inject #GP)
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ whole; patches welcome!
|
||||
kasan
|
||||
ubsan
|
||||
kmemleak
|
||||
kmemcheck
|
||||
gdb-kernel-debugging
|
||||
kgdb
|
||||
kselftest
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
10
MAINTAINERS
10
MAINTAINERS
@@ -7688,16 +7688,6 @@ F: include/linux/kdb.h
|
||||
F: include/linux/kgdb.h
|
||||
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
|
||||
M: Catalin Marinas <catalin.marinas@arm.com>
|
||||
S: Maintained
|
||||
|
||||
@@ -112,7 +112,6 @@ config X86
|
||||
select HAVE_ARCH_JUMP_LABEL
|
||||
select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP
|
||||
select HAVE_ARCH_KGDB
|
||||
select HAVE_ARCH_KMEMCHECK
|
||||
select HAVE_ARCH_MMAP_RND_BITS if MMU
|
||||
select HAVE_ARCH_MMAP_RND_COMPAT_BITS 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
|
||||
def_bool y
|
||||
depends on X86_64 && !DEBUG_PAGEALLOC && !KMEMCHECK
|
||||
depends on X86_64 && !DEBUG_PAGEALLOC
|
||||
---help---
|
||||
Certain kernel features effectively disable kernel
|
||||
linear 1 GB mappings (even if the CPU otherwise
|
||||
|
||||
@@ -1,43 +1 @@
|
||||
/* 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!
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_KMEMCHECK
|
||||
|
||||
#if (__GNUC__ >= 4)
|
||||
#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
|
||||
#else
|
||||
@@ -189,13 +187,6 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len)
|
||||
? __constant_memcpy((t), (f), (n)) \
|
||||
: __memcpy((t), (f), (n)))
|
||||
#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 /* !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);
|
||||
|
||||
#ifndef CONFIG_FORTIFY_SOURCE
|
||||
#ifndef CONFIG_KMEMCHECK
|
||||
#if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4
|
||||
#define memcpy(dst, src, len) \
|
||||
({ \
|
||||
@@ -46,13 +45,6 @@ extern void *__memcpy(void *to, const void *from, size_t len);
|
||||
__ret; \
|
||||
})
|
||||
#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 */
|
||||
|
||||
#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)
|
||||
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,
|
||||
* 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_KMEMCHECK) += kmemcheck/
|
||||
|
||||
KASAN_SANITIZE_kasan_init_$(BITS).o := n
|
||||
obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o
|
||||
|
||||
|
||||
@@ -163,12 +163,11 @@ static int page_size_mask;
|
||||
static void __init probe_page_size_mask(void)
|
||||
{
|
||||
/*
|
||||
* For CONFIG_KMEMCHECK or pagealloc debugging, identity mapping will
|
||||
* use small pages.
|
||||
* For pagealloc debugging, identity mapping will use small pages.
|
||||
* This will simplify cpa(), which otherwise needs to support splitting
|
||||
* 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;
|
||||
else
|
||||
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
|
||||
#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 */
|
||||
#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
|
||||
#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 */
|
||||
#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
|
||||
#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 */
|
||||
#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
|
||||
#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