[PATCH v3 5/6] LoongArch: Check atomic instructions in insns_not_supported()

From: Tiezhu Yang
Date: Wed Apr 19 2023 - 05:57:50 EST


Like llsc instructions, the atomic memory access instructions should
not be supported for probing, check them in insns_not_supported().

Here is a simple example with CONFIG_UPROBE_EVENTS=y:

# cat pthread.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

static pthread_spinlock_t lock;
static pthread_t t1, t2;
static int count = 0;

void *t1_start(void *arg)
{
for (int i = 0; i < 10000; i++)
{
pthread_spin_lock(&lock);
count++;
pthread_spin_unlock(&lock);
}
}

void *t2_start(void *arg)
{
for (int i = 0; i < 20000; i++)
{
pthread_spin_lock(&lock);
count++;
pthread_spin_unlock(&lock);
}
}

int main()
{
int ret;

ret = pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
if (ret)
return -1;

ret = pthread_create(&t1, NULL, t1_start, NULL);
if (ret)
exit(1);

ret = pthread_create(&t2, NULL, t2_start, NULL);
if (ret)
exit(1);

pthread_join(t1, NULL);
pthread_join(t2, NULL);
pthread_spin_destroy(&lock);

printf("%d\n", count);
return 0;
}
# gcc pthread.c -o /tmp/pthread
# objdump -d /lib64/libc.so.6 | grep -w "pthread_spin_lock" -A 5 | head -5
00000000000886a4 <pthread_spin_lock@@GLIBC_2.36>:
886a4: 0280040d addi.w $t1, $zero, 1(0x1)
886a8: 3869348c amswap_db.w $t0, $t1, $a0
886ac: 0040818c slli.w $t0, $t0, 0x0
886b0: 44003180 bnez $t0, 48(0x30) # 886e0 <pthread_spin_lock@@GLIBC_2.36+0x3c>
# cd /sys/kernel/debug/tracing
# echo > uprobe_events
# echo "p:myuprobe /lib64/libc.so.6:0x886a4" > uprobe_events

Without this patch:

# echo 1 > events/uprobes/enable
# echo 1 > tracing_on
# /tmp/pthread
Trace/breakpoint trap (core dumped)

With this patch:

# echo 1 > events/uprobes/enable
bash: echo: write error: Invalid argument

Reported-by: Hengqi Chen <hengqi.chen@xxxxxxxxx>
Link: https://lore.kernel.org/all/SY4P282MB351877A70A0333C790FE85A5C09C9@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
Signed-off-by: Tiezhu Yang <yangtiezhu@xxxxxxxxxxx>
---
arch/loongarch/include/asm/inst.h | 26 ++++++++++++++++++++++++++
arch/loongarch/kernel/inst.c | 6 ++++++
arch/loongarch/kernel/uprobes.c | 9 +++++----
3 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 061c0ea..09d5656 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -176,6 +176,32 @@ enum reg3_op {
amord_op = 0x70c7,
amxorw_op = 0x70c8,
amxord_op = 0x70c9,
+ ammaxw_op = 0x70ca,
+ ammaxd_op = 0x70cb,
+ amminw_op = 0x70cc,
+ ammind_op = 0x70cd,
+ ammaxwu_op = 0x70ce,
+ ammaxdu_op = 0x70cf,
+ amminwu_op = 0x70d0,
+ ammindu_op = 0x70d1,
+ amswapdbw_op = 0x70d2,
+ amswapdbd_op = 0x70d3,
+ amadddbw_op = 0x70d4,
+ amadddbd_op = 0x70d5,
+ amanddbw_op = 0x70d6,
+ amanddbd_op = 0x70d7,
+ amordbw_op = 0x70d8,
+ amordbd_op = 0x70d9,
+ amxordbw_op = 0x70da,
+ amxordbd_op = 0x70db,
+ ammaxdbw_op = 0x70dc,
+ ammaxdbd_op = 0x70dd,
+ ammindbw_op = 0x70de,
+ ammindbd_op = 0x70df,
+ ammaxdbwu_op = 0x70e0,
+ ammaxdbdu_op = 0x70e1,
+ ammindbwu_op = 0x70e2,
+ ammindbdu_op = 0x70e3,
};

enum reg3sa2_op {
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index 1d7d579..ce25a63 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -135,6 +135,12 @@ void simu_branch(struct pt_regs *regs, union loongarch_instruction insn)

bool insns_not_supported(union loongarch_instruction insn)
{
+ switch (insn.reg3_format.opcode) {
+ case amswapw_op ... ammindbdu_op:
+ pr_notice("atomic memory access instructions are not supported\n");
+ return true;
+ }
+
switch (insn.reg2i14_format.opcode) {
case llw_op:
case lld_op:
diff --git a/arch/loongarch/kernel/uprobes.c b/arch/loongarch/kernel/uprobes.c
index 628c39d..bc6ec74 100644
--- a/arch/loongarch/kernel/uprobes.c
+++ b/arch/loongarch/kernel/uprobes.c
@@ -15,10 +15,11 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe,
if (addr & 0x3)
return -EILSEQ;

- insn.word = auprobe->insn[0];
-
- if (insns_not_supported(insn))
- return -EINVAL;
+ for (int idx = ARRAY_SIZE(auprobe->insn) - 1; idx >= 0; idx--) {
+ insn.word = auprobe->insn[idx];
+ if (insns_not_supported(insn))
+ return -EINVAL;
+ }

if (insns_need_simulation(insn)) {
auprobe->ixol[0] = larch_insn_gen_nop();
--
2.1.0