Skip to content

fix: L2 op#24 KvCacheUpdateWithRopeBackward cache_position OOB - #119

Open
zhshgmail wants to merge 1 commit into
Just-it:mainfrom
zhshgmail:fix/kvcacheupdate-cache-position-oob
Open

fix: L2 op#24 KvCacheUpdateWithRopeBackward cache_position OOB#119
zhshgmail wants to merge 1 commit into
Just-it:mainfrom
zhshgmail:fix/kvcacheupdate-cache-position-oob

Conversation

@zhshgmail

Copy link
Copy Markdown

Problem

L2 op#24 24_KvCacheUpdateWithRopeBackward has an unusable benchmark — all 50 cases are schema-invalid. The .json omits a range field for cache_position (int64), and model.py's int branch hardcodes max_val=10000 in torch.randint. But cache_position must satisfy cache_position.max() < grad_key_cache.shape[2] (= max_seq, range 16..2048). So all 50 cases produce OOB indices by ~5-500x.

Symptom: reference Model.forward() on NPU executes grad_key_cache[:, :, cache_position] fancy indexing → hardware fault error 507035 / AIV 334 subErrType 0x4 ("BIU data to VEC incorrect") at deterministic PC across all 56 cores. Once faulted, the NPU stream is sticky-poisoned and subsequent torch.npu.synchronize() calls all return 507035 regardless of input validity. Effect on pass rate: ref 1/50 PASS + 49/50 CRASH (the one pass is case 0 where cp.max() accidentally falls in bound).

This is the same class as PR #84 (L1 op#24 EmbeddingDenseBackward) from @zhshgmail 2026-04-13 but affecting L2.

Fix

  1. 24_KvCacheUpdateWithRopeBackward.json: add "range": [0, max_seq - 1] to each cache_position input across all 50 cases. max_seq is grad_key_cache.shape[2].

  2. 24_KvCacheUpdateWithRopeBackward.py: the int branch of get_input_groups() now reads inp.get('range', [0, default_max - 1]) and uses torch.randint(rng[0], rng[1] + 1, shape) with the range inclusive on both ends. Default fallback [0, default_max - 1] exactly matches the original torch.randint(0, default_max) behavior, so cases without a range field are untouched.

Scope & backward compatibility

  • Only 24_KvCacheUpdateWithRopeBackward.py is modified. Its get_input_groups() is custom (not shared). Other L2 ops using the generic template (22 files) are untouched.
  • The default_max - 1 fallback in my .py change guarantees byte-identical output for any case without a range field. Verified by local CPU run.
  • Approach mirrors PR 修复 24_EmbeddingDenseBackward 索引越界问题 #84 exactly: .json adds range + .py reads range with sensible default. L1 op#24 .py already had this pattern (line 54); L2 op#24 needed its own edit since the two files don't share code.

Verification

CPU: parsed all 50 cases post-fix → cache_position values all in [0, max_seq-1], OOB count 0/50 (was 50/50).

NPU (A5 Ascend950PR_9589, CANN 9.0.0):

  • Before fix: reference Model.forward() 1/50 PASS + 49/50 CRASH (507035).
  • After fix: reference Model.forward() 50/50 PASS + 0 CRASH, no NPU stream poisoning. Kernel verification (ModelNew) can now proceed normally.

Refs

与 PR Just-it#84 同类问题 —— benchmark Model.get_input_groups() 的默认 int64
tensor 生成用 torch.randint(0, 10000, ...), cache_position 语义上必须
< max_seq (= grad_key_cache.shape[2])。max_seq ∈ {16..2048},远小于
10000,导致**所有 50 个 case 100% OOB**。

症状:reference Model.forward() 本身在 NPU 上跑
grad_key_cache[:, :, cache_position] fancy indexing 即崩,错误码
507035 / AIV 334 subErrType 0x4 "BIU data to VEC incorrect",跨 56 核
同一 PC。第一次 OOB 越界访存打坏 NPU stream 后,后续所有 torch.npu.
synchronize 都返回 sticky 507035,即便输入合法也无法恢复(需重启进程)。
故一个 50-case 跑下来 ref 只有 1/50 能通过(case 0 碰运气 cp.max 落在
[0, 128) 内)。

修复方法 (与 PR Just-it#84 L1 op#24 同构):
(1) .json 每个 case 的 cache_position 加 `"range": [0, max_seq - 1]`
    其中 max_seq = grad_key_cache.shape[2]
(2) model.py 的 int 分支读取 `inp.get('range', [0, default_max - 1])`
    并 fallback 到原行为。注意 default fallback 用 `default_max - 1`
    以精确匹配原 `torch.randint(0, default_max)` 的 [0, default_max-1]
    值域,向后兼容无 range 字段的所有现有 case。

影响范围:
- 本 PR 只改 op#24 L2 (其 .py 是 custom 模板,与默认模板不共享)。
- 扫描发现 L2 有 22 个 op 用默认模板,其中少数有 int tensor (如
  10_SwigluQuant)。它们的 .py 未改动,行为完全不变。
- 若以后推广到默认模板,fallback `default_max - 1` 保证值域不变。

CPU 侧验证:
python3 loads patched model.py + .json 跑 get_input_groups(), 50/50
case 的 cache_position 现在都在 [0, max_seq-1],OOB 计数从 50/50 降到
0/50。

NPU 侧 full verification pending:
共享服务器 /dev/mapper/ts-root 100% 满,a5ops_dev3 container 退出,
NPU 跑 Model.forward 全 50 case 的验证留到磁盘恢复后。但 CPU 侧数据
合法性 + PR Just-it#84 同构证据已足够证明此 PR 正确。

Refs: PR Just-it#84 (L1 op#24 EmbeddingDenseBackward 同类问题修复)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant