diff mbox

[PULL,39/39] target-arm: Avoid buffer overrun on UNPREDICTABLE ldrd/strd

Message ID 1432905045-22138-40-git-send-email-peter.maydell@linaro.org
State Accepted
Commit 3960c336ad96c2183549c8bf32bbff93ecda7ea4
Headers show

Commit Message

Peter Maydell May 29, 2015, 1:10 p.m. UTC
A LDRD or STRD where rd is not an even number is UNPREDICTABLE.
We were letting this fall through, which is OK unless rd is 15,
in which case we would attempt to do a load_reg or store_reg
to a nonexistent r16 for the second half of the double-word.
Catch the odd-numbered-rd cases and UNDEF them instead.

To do this we rearrange the structure of the code a little
so we can put the UNDEF catches at the top before we've
allocated TCG temporaries.

Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 1431348973-21315-1-git-send-email-peter.maydell@linaro.org
---
 target-arm/translate.c | 56 ++++++++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 24 deletions(-)
diff mbox

Patch

diff --git a/target-arm/translate.c b/target-arm/translate.c
index 6493b9a..39692d7 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -8430,34 +8430,30 @@  static void disas_arm_insn(DisasContext *s, unsigned int insn)
                 }
             } else {
                 int address_offset;
-                int load;
+                bool load = insn & (1 << 20);
+                bool doubleword = false;
                 /* Misc load/store */
                 rn = (insn >> 16) & 0xf;
                 rd = (insn >> 12) & 0xf;
+
+                if (!load && (sh & 2)) {
+                    /* doubleword */
+                    ARCH(5TE);
+                    if (rd & 1) {
+                        /* UNPREDICTABLE; we choose to UNDEF */
+                        goto illegal_op;
+                    }
+                    load = (sh & 1) == 0;
+                    doubleword = true;
+                }
+
                 addr = load_reg(s, rn);
                 if (insn & (1 << 24))
                     gen_add_datah_offset(s, insn, 0, addr);
                 address_offset = 0;
-                if (insn & (1 << 20)) {
-                    /* load */
-                    tmp = tcg_temp_new_i32();
-                    switch(sh) {
-                    case 1:
-                        gen_aa32_ld16u(tmp, addr, get_mem_index(s));
-                        break;
-                    case 2:
-                        gen_aa32_ld8s(tmp, addr, get_mem_index(s));
-                        break;
-                    default:
-                    case 3:
-                        gen_aa32_ld16s(tmp, addr, get_mem_index(s));
-                        break;
-                    }
-                    load = 1;
-                } else if (sh & 2) {
-                    ARCH(5TE);
-                    /* doubleword */
-                    if (sh & 1) {
+
+                if (doubleword) {
+                    if (!load) {
                         /* store */
                         tmp = load_reg(s, rd);
                         gen_aa32_st32(tmp, addr, get_mem_index(s));
@@ -8466,7 +8462,6 @@  static void disas_arm_insn(DisasContext *s, unsigned int insn)
                         tmp = load_reg(s, rd + 1);
                         gen_aa32_st32(tmp, addr, get_mem_index(s));
                         tcg_temp_free_i32(tmp);
-                        load = 0;
                     } else {
                         /* load */
                         tmp = tcg_temp_new_i32();
@@ -8476,15 +8471,28 @@  static void disas_arm_insn(DisasContext *s, unsigned int insn)
                         tmp = tcg_temp_new_i32();
                         gen_aa32_ld32u(tmp, addr, get_mem_index(s));
                         rd++;
-                        load = 1;
                     }
                     address_offset = -4;
+                } else if (load) {
+                    /* load */
+                    tmp = tcg_temp_new_i32();
+                    switch (sh) {
+                    case 1:
+                        gen_aa32_ld16u(tmp, addr, get_mem_index(s));
+                        break;
+                    case 2:
+                        gen_aa32_ld8s(tmp, addr, get_mem_index(s));
+                        break;
+                    default:
+                    case 3:
+                        gen_aa32_ld16s(tmp, addr, get_mem_index(s));
+                        break;
+                    }
                 } else {
                     /* store */
                     tmp = load_reg(s, rd);
                     gen_aa32_st16(tmp, addr, get_mem_index(s));
                     tcg_temp_free_i32(tmp);
-                    load = 0;
                 }
                 /* Perform base writeback before the loaded value to
                    ensure correct behavior with overlapping index registers.