[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
ldrd implementation issue?
From: |
Stu Grossman |
Subject: |
ldrd implementation issue? |
Date: |
Mon, 10 Feb 2025 08:54:06 -0800 |
User-agent: |
Mozilla Thunderbird |
I've been getting SIGBUS cores with a bunch of user apps running under
linux 5.15 and qemu-system-aarch64. These happen to be 32 bit (T32?)
programs.
All of the cores point at the following instruction:
ldrd r2,r3,[r2]
where r2 points at four bytes prior to the end of a page. Eg: 0x10ffc.
The bug appears to be that we get a page fault between the accesses for
each word. Prior to the fault, the first register is updated. Later,
after the fault is serviced, the instruction is restarted with the index
register containing the word loaded prior to the fault, which is
probably not the desired address, resulting in a coredump.
So the conditions for the problem are:
- the index register must be one of the registers being loaded.
- the index register must point at a uint64_t that spans a page
boundary.
I understand that the uint64_t spanning a page boundary may not be
allowed by the arm 32 bit API. However, the ldrd instruction definition
allows for four byte alignment.
Note that ldm may have similar issues if the index is one of the
registers being loaded and the words cross a page boundary.
The fix is to defer the register stores till after both words have been
read from memory.
Here is my fix:
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 9ee761fc64..78ad0ed186 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -5006,7 +5006,7 @@ static bool op_store_rr(DisasContext *s,
arg_ldst_rr *a,
static bool trans_LDRD_rr(DisasContext *s, arg_ldst_rr *a)
{
int mem_idx = get_mem_index(s);
- TCGv_i32 addr, tmp;
+ TCGv_i32 addr, tmp1, tmp2;
if (!ENABLE_ARCH_5TE) {
return false;
@@ -5017,15 +5017,16 @@ static bool trans_LDRD_rr(DisasContext *s,
arg_ldst_rr *a)
}
addr = op_addr_rr_pre(s, a);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
- store_reg(s, a->rt, tmp);
+ tmp1 = tcg_temp_new_i32();
+ gen_aa32_ld_i32(s, tmp1, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_gen_addi_i32(addr, addr, 4);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
- store_reg(s, a->rt + 1, tmp);
+ tmp2 = tcg_temp_new_i32();
+ gen_aa32_ld_i32(s, tmp2, addr, mem_idx, MO_UL | MO_ALIGN);
+
+ store_reg(s, a->rt, tmp1);
+ store_reg(s, a->rt + 1, tmp2);
/* LDRD w/ base writeback is undefined if the registers overlap. */
op_addr_rr_post(s, a, addr, -4);
@@ -5153,19 +5154,20 @@ static bool op_store_ri(DisasContext *s,
arg_ldst_ri *a,
static bool op_ldrd_ri(DisasContext *s, arg_ldst_ri *a, int rt2)
{
int mem_idx = get_mem_index(s);
- TCGv_i32 addr, tmp;
+ TCGv_i32 addr, tmp1, tmp2;
addr = op_addr_ri_pre(s, a);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
- store_reg(s, a->rt, tmp);
+ tmp1 = tcg_temp_new_i32();
+ gen_aa32_ld_i32(s, tmp1, addr, mem_idx, MO_UL | MO_ALIGN);
tcg_gen_addi_i32(addr, addr, 4);
- tmp = tcg_temp_new_i32();
- gen_aa32_ld_i32(s, tmp, addr, mem_idx, MO_UL | MO_ALIGN);
- store_reg(s, rt2, tmp);
+ tmp2 = tcg_temp_new_i32();
+ gen_aa32_ld_i32(s, tmp2, addr, mem_idx, MO_UL | MO_ALIGN);
+
+ store_reg(s, a->rt, tmp1);
+ store_reg(s, rt2, tmp2);
/* LDRD w/ base writeback is undefined if the registers overlap. */
op_addr_ri_post(s, a, addr, -4);
Stu
- ldrd implementation issue?,
Stu Grossman <=