Commit 53e5ecee authored by Zheng Yejian's avatar Zheng Yejian
Browse files

livepatch: Avoid patching conflicts with kprobes

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IA76NX


CVE: NA

--------------------------------

Kprobe and livepatch_wo may modify the first several instructions of
a function at the same time which causing a conflict. Since dynamic
ftrace reserve instructions at non-notrace functions, we can allow
kprobe works on the reserved instructions and livepatch_wo work on
other instructions so as to avoid the conflict. But note that we also
do not allow both modify the same instruction when a function is
marked as 'notrace' and without the reserved instructions.

Determining the order of locks to prevent deadlocks:
  kprobe_mutex -> klp_mutex -> cpus_read_lock -> text_mutex

Signed-off-by: default avatarZheng Yejian <zhengyejian1@huawei.com>
parent 79e1c0e4
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -288,3 +288,14 @@ void arch_klp_unpatch_func(struct klp_func *func)
		do_patch(pc, (unsigned long)next_func->new_func);
	}
}

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
unsigned long arch_klp_fentry_range_size(void)
{
	/*
	 * If first instruction is not 'bti', expect fentry exists
	 * at second instruction otherwise third instruction.
	 */
	return 3 * AARCH64_INSN_SIZE;
}
#endif /* CONFIG_LIVEPATCH_ISOLATE_KPROBE */
+18 −0
Original line number Diff line number Diff line
@@ -374,3 +374,21 @@ void arch_klp_unpatch_func(struct klp_func *func)
	/* replace the text with the new text */
	klp_patch_text((void *)ip, (const void *)new, JMP_E9_INSN_SIZE);
}

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
unsigned long arch_klp_fentry_range_size(void)
{
	unsigned long size = MCOUNT_INSN_SIZE;

	/*
	 * If CONFIG_X86_KERNEL_IBT enabled, there would be an 'endbr64'
	 * at function start, then it should be consider into the range
	 * size.
	 */
#ifdef CONFIG_X86_KERNEL_IBT
	size += ENDBR_INSN_SIZE;
#endif
	/* Expect fentry exists at first instruction. */
	return size;
}
#endif /* CONFIG_LIVEPATCH_ISOLATE_KPROBE */
+8 −0
Original line number Diff line number Diff line
@@ -575,6 +575,14 @@ unsigned long kretprobe_find_ret_addr(struct task_struct *tsk, void *fp,
}
#endif

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
void kprobes_lock(void);
void kprobes_unlock(void);
#else /* !CONFIG_LIVEPATCH_ISOLATE_KPROBE */
static inline void kprobes_lock(void) { }
static inline void kprobes_unlock(void) { }
#endif /* CONFIG_LIVEPATCH_ISOLATE_KPROBE */

/* Returns true if kprobes handled the fault */
static nokprobe_inline bool kprobe_page_fault(struct pt_regs *regs,
					      unsigned int trap)
+13 −0
Original line number Diff line number Diff line
@@ -369,4 +369,17 @@ int klp_apply_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,

#endif /* CONFIG_LIVEPATCH */

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
void klp_lock(void);
void klp_unlock(void);
int klp_check_patched(unsigned long addr);
#else /* !CONFIG_LIVEPATCH_ISOLATE_KPROBE */
static inline void klp_lock(void) { }
static inline void klp_unlock(void) { }
static inline int klp_check_patched(unsigned long addr)
{
	return 0;
}
#endif /* CONFIG_LIVEPATCH_ISOLATE_KPROBE */

#endif /* _LINUX_LIVEPATCH_H_ */
+24 −1
Original line number Diff line number Diff line
@@ -44,7 +44,9 @@
#include <asm/cacheflush.h>
#include <asm/errno.h>
#include <linux/uaccess.h>

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
#include <linux/livepatch.h>
#endif
#define KPROBE_HASH_BITS 6
#define KPROBE_TABLE_SIZE (1 << KPROBE_HASH_BITS)

@@ -1616,6 +1618,18 @@ static int check_kprobe_address_safe(struct kprobe *p,
	return ret;
}

#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
void kprobes_lock(void)
{
	mutex_lock(&kprobe_mutex);
}

void kprobes_unlock(void)
{
	mutex_unlock(&kprobe_mutex);
}
#endif

int register_kprobe(struct kprobe *p)
{
	int ret;
@@ -1644,6 +1658,12 @@ int register_kprobe(struct kprobe *p)
		return ret;

	mutex_lock(&kprobe_mutex);
#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
	klp_lock();
	ret = klp_check_patched((unsigned long)p->addr);
	if (ret)
		goto out;
#endif

	if (on_func_entry)
		p->flags |= KPROBE_FLAG_ON_FUNC_ENTRY;
@@ -1680,6 +1700,9 @@ int register_kprobe(struct kprobe *p)
	/* Try to optimize kprobe */
	try_to_optimize_kprobe(p);
out:
#ifdef CONFIG_LIVEPATCH_ISOLATE_KPROBE
	klp_unlock();
#endif
	mutex_unlock(&kprobe_mutex);

	if (probed_mod)
Loading