riscv: Invalid 32-bit instruction should not decrement pc #1991
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Note: I'm not sure if this is QEMU's bug or Unicorn's bug. I wasn't able to find a QEMU commit which had the line I removed here and so I suspect this was added in Unicorn, but I'm not sure which version of QEMU the current Unicorn was derived from and history shows this line being added as part of the Unicorn2 import mega-commit aaaea14.
If this is QEMU's bug then it it appears to have been fixed upstream already but the relevant part of this code has been changed significantly to support dynamic registration of ISA extension decoders. QEMU code from just before that redesign already lacks the line of code I removed, and I can't find any other earlier commit that contains it before I reach code that is clearly earlier than the snapshot Unicorn2 adopted.
I'm not sure what the policy is for patching the QEMU code directly instead of importing a new version from upstream; I'll understand if this patch is unwanted due to making future QEMU backports harder, but unfortunately this problem is blocking for me because my program relies on being able to detect invalid instruction exceptions but currently Unicorn is misreporting an instruction page fault at an unrelated address instead.
The line I've removed appears to be trying to undo the effect of adding 4 to
pc
above, but does so incorrectly and so ends up returning withnext_pc
earlier than it was prior to decoding.This causes the translator to malfunction because it does not expect
pc_next
to decrease during decoding: this is effectively reporting that the invalid instruction has a negative size, which is impossible. The decoder uses the increase innext_pc
to decide the translation block size, but converts it touint16_t
thereby causing a block containing only an invalid instruction to be treated as having size 65532 (reinterpreted -4) and therefore the translation loop tries to find the next translation block at 65532 bytes after the invalid instruction, which can cause a spurious instruction access/page fault if the page containing that address is not mapped as executable.In practice we don't need to readjust the
pc
at all here because it is correct to report that the invalid instruction is four bytes long. This allows the translation loop to correctly find the next instruction, and to avoid producing spurious TLB fills that might cause incorrect exceptions.The following is the assignment that calculates the translation block size from the difference between
pc_next
andpc_first
after the translator reports the end of a block:unicorn/qemu/accel/tcg/translator.c
Line 147 in d4b9248
The
size
field isuint16_t
so this causes unsigned integer overflow ifpc_next
is less thanpc_first
, which is the situation caused by the line I removed in this PR.The spurious exception occurs in the caller where it tries to find the physical address of the next instruction. The incorrect
size
value causes the incorrect conclusion that the next instruction is in a different page and causes a spurious TLB probe which an then raise a spurious instruction access or page fault:unicorn/qemu/accel/tcg/translate-all.c
Lines 1706 to 1711 in d4b9248
After my change
size
is 4 and so this logic then probes the correct address, which in my case belongs to the same page as the invalid instruction and so avoids callingget_page_addr_code
at all.If my 4-byte invalid instruction were straddling a page boundary then the end would be in a different page than the start, but in that case any access/page fault for the upper half would already have been detected when loading it in the riscv-specific instruction decoder, and so the
get_page_addr_code
call in the snippet above would still not be reachable:unicorn/qemu/target/riscv/translate.c
Lines 768 to 769 in d4b9248
(The minimum valid alignment for a RISC-V instruction is two bytes, so all other situations would cause "instruction address misaligned" to have occurred long before this code is running.)