aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-05-29 11:29:00 +0100
committerMichael Roth <mdroth@linux.vnet.ibm.com>2015-07-28 18:23:18 -0500
commita4bb522ee51087af61998f290d12ba2e14c7910e (patch)
treeda40cd297f84f0a2ae869ab7f1ce407f452c0413
parentcf6c213981de2a54154b13fecee58e6cdc5aa504 (diff)
downloadqemu-a4bb522ee51087af61998f290d12ba2e14c7910e.zip
qemu-a4bb522ee51087af61998f290d12ba2e14c7910e.tar.gz
qemu-a4bb522ee51087af61998f290d12ba2e14c7910e.tar.bz2
target-arm: Avoid buffer overrun on UNPREDICTABLE ldrd/strd
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 (cherry picked from commit 3960c336ad96c2183549c8bf32bbff93ecda7ea4) Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
-rw-r--r--target-arm/translate.c56
1 files changed, 32 insertions, 24 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 9116529..f8f72be 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -8423,34 +8423,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));
@@ -8459,7 +8455,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();
@@ -8469,15 +8464,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.