Recompiler: vcallmsr reads ctx->vi[27], out of bounds on uint16_t vi[16], instead of ctx->vu0_cmsar0

Đang mở
#262 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 5 ngày

@drakolordx7 đang làm issue này rồi.

Từ ngày 30/9/2026.

  • #268 của @drakolordx7 — đang mở

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
28/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Đình trệ
Công nghệ
cpp
Lĩnh vực
compilers, tooling

Hướng nghiên cứu

The mismatch is in ps2xRecomp/src/lib/vu_translation_helpers.cpp around the vcallmsr emitter (line 172 in the report) versus the ctc2 CMSAR0 path in vu_translator.cpp. Read those two sites and the vi[16] / vu0_cmsar0 layout in ps2xRuntime/include/ps2_runtime.h. Confirm other VU0_CR_* cases in the helpers do not index vi with a control-register number. PR #268 is already open against this issue, so do not start a parallel fix unless maintainers say otherwise.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

What happens

The two halves of the same instruction pair disagree about where the VU0 microprogram start address lives.

ctc2 $rt, $vi27 is translated correctly -- ps2xRecomp/src/lib/vu_translator.cpp:96:

case VU0_CR_CMSAR0:
    return fmt::format("ctx->vu0_cmsar0 = GPR_U32(ctx, {});", rt);

But vcallmsr reads the value back from the vi array -- ps2xRecomp/src/lib/vu_translation_helpers.cpp:172:

"    uint16_t instr_index = ctx->vi[{}] & 0x1FF; "

With vcallmsr $vi27 that formats to ctx->vi[27], and vi is declared uint16_t vi[16] (ps2xRuntime/include/ps2_runtime.h:75). Index 27 is 11 elements past the end; the read lands in the storage of the following fields (vu0_q / vu0_p in this layout). vu0_cmsar0 is declared separately at line 85, and is what the write filled.

clang flags it: array index 27 is past the end of the array.

Reproduction

Guest code at 0x002C4850 in SLUS_200.02:

andi  $a0, $a0, 0xFFFF
ctc2  $a0, $vi27          ; VI27 = CMSAR0
jr    $ra
vcallmsr                  ; delay slot: start the VU0 microprogram from CMSAR0

Generated output (Vu0StartMicroProgramReg_0x2c4850.cpp):

// 0x2c4854: 0x48c4d800  ctc2.ni  $a0, $vi27
ctx->vu0_cmsar0 = GPR_U32(ctx, 4);
...
{ uint16_t instr_index = ctx->vi[27] & 0x1FF;
  uint32_t target_byte_addr = (uint32_t)instr_index << 3;
  ctx->vu0_pc = target_byte_addr;
  runtime->vu0StartMicroProgram(rdram, ctx, target_byte_addr); }

The microprogram address never arrives, and the register-based VU0 microprogram launcher is broken.

Root cause

vcallmsr takes its address from CMSAR0 unconditionally -- that is what the instruction does. The $vi27 in the disassembly is how CMSAR0 is named in the VU0 control register space (VU0_CR_CMSAR0 = 27), which is a different namespace from the 16 VI integer registers. The emitter is using a control-register index to subscript the VI array.

Suggested fix

Emit the field the ctc2 side writes:

"    uint16_t instr_index = (uint16_t)(ctx->vu0_cmsar0 & 0x1FF); "

The other VU0_CR_* cases in vu_translation_helpers.cpp are worth auditing for the same namespace confusion.

Scope

One occurrence in our 2751 functions -- the only vcallmsr in the binary, called only from FUN_002C4740. The 124 functions using the immediate form vcallms are unaffected, since there the address is encoded in the instruction. But this is an out-of-bounds read, so it is undefined behaviour rather than merely a wrong value, and it does not stay dormant safely.


Found while statically recompiling Ridge Racer V (NTSC-U, SLUS_200.02) with PS2Recomp.
Tested on commit 75d729ce; main was c5a9d02 at the time of writing, one README-only commit ahead.
Host: Windows 11 x64, clang-cl 22.1.3 + Ninja, MSVC toolset 14.51.36231, Windows SDK 10.0.26100.0, CMake 4.2.3.
None of these are specific to this game.

Ngôn ngữ chính
C++
Star
3.3k
Fork
149
Merge trung bình
5 ngày 10 giờ
Pull request đã merge (30 ngày)
4

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của ran-j/PS2Recomp

Tất cả issue của ran-j/PS2Recomp

Issue tương tự

Thêm issue về C++

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.