pytorch - 💡(How to fix) Fix [JIT] Null pointer write in SourceRangeDeserializer::deserialize_source() via unchecked text_table indices

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

SourceRangeDeserializer::deserialize_source() in torch/csrc/jit/serialization/source_range_serialization.cpp validates fnameIndex against text_table_.size() but does NOT validate the entries of textIndex, which are also used as indices into text_table_. An out-of-range textIndex value accesses beyond the vector, returning a null/garbage shared_ptr<string>. Dereferencing it writes to the null page (SEGV). Crashes in both 2.7.0 and 2.11.0. Triggered by a crafted .debug_pkl inside a .pt file.

Root Cause

// source_range_serialization.cpp, ~line 188
for (int64_t i : textIndex) {
  pieces.emplace_back(*text_table_[i]);  // ← i not bounds-checked
  strs.emplace_back(text_table_[i]);     // ← OOB access
}

fnameIndex has a TORCH_CHECK guard (added previously), but the textIndex list entries in the loop are used directly as vector indices with no validation. Two crash variants are observed: a null write in deserialize_source (variant A) and a null read inside vector::_M_realloc_insert during string_view construction (variant B).

Code Example

// source_range_serialization.cpp, ~line 188
for (int64_t i : textIndex) {
  pieces.emplace_back(*text_table_[i]);  // ← i not bounds-checked
  strs.emplace_back(text_table_[i]);     // ← OOB access
}

---

torch::jit::load()
Unpickler::run()
Lexer::lexRaw()Lexer::expected()
SourceRange::highlight()
ConcreteSourceRangeUnpickler::unpickle()
SourceRangeDeserializer::deserialize_source()
            → pieces.emplace_back(*text_table_[i])  // SEGV write to null page

---

UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000039
The signal is caused by a WRITE memory access.
Hint: address points to the zero page.
  #0 torch::jit::SourceRangeDeserializer::deserialize_source()
  #1 torch::jit::SourceRangeDeserializer::deserialize()
  ...
  #37 torch::jit::load()

---

import torch
torch.jit.load("poc-048-sourcerange-write.pt")
# → Segmentation fault

---

for (int64_t i : textIndex) {
  TORCH_CHECK(
      i >= 0 && (uint64_t)i < text_table_.size(),
      "Text table index ", i, " out of range [0, ", text_table_.size(), ")");
  pieces.emplace_back(*text_table_[i]);
  strs.emplace_back(text_table_[i]);
}
RAW_BUFFERClick to expand / collapse

Summary

SourceRangeDeserializer::deserialize_source() in torch/csrc/jit/serialization/source_range_serialization.cpp validates fnameIndex against text_table_.size() but does NOT validate the entries of textIndex, which are also used as indices into text_table_. An out-of-range textIndex value accesses beyond the vector, returning a null/garbage shared_ptr<string>. Dereferencing it writes to the null page (SEGV). Crashes in both 2.7.0 and 2.11.0. Triggered by a crafted .debug_pkl inside a .pt file.

Root cause

// source_range_serialization.cpp, ~line 188
for (int64_t i : textIndex) {
  pieces.emplace_back(*text_table_[i]);  // ← i not bounds-checked
  strs.emplace_back(text_table_[i]);     // ← OOB access
}

fnameIndex has a TORCH_CHECK guard (added previously), but the textIndex list entries in the loop are used directly as vector indices with no validation. Two crash variants are observed: a null write in deserialize_source (variant A) and a null read inside vector::_M_realloc_insert during string_view construction (variant B).

Call chain

torch::jit::load()
  → Unpickler::run()
    → Lexer::lexRaw() → Lexer::expected()
      → SourceRange::highlight()
        → ConcreteSourceRangeUnpickler::unpickle()
          → SourceRangeDeserializer::deserialize_source()
            → pieces.emplace_back(*text_table_[i])  // SEGV write to null page

Crash

UndefinedBehaviorSanitizer: SEGV on unknown address 0x000000000039
The signal is caused by a WRITE memory access.
Hint: address points to the zero page.
  #0 torch::jit::SourceRangeDeserializer::deserialize_source()
  #1 torch::jit::SourceRangeDeserializer::deserialize()
  ...
  #37 torch::jit::load()

Reproduction

import torch
torch.jit.load("poc-048-sourcerange-write.pt")
# → Segmentation fault

PoC file available on request (3,297 bytes).

Suggested fix

Mirror the existing fnameIndex check for every value in textIndex:

for (int64_t i : textIndex) {
  TORCH_CHECK(
      i >= 0 && (uint64_t)i < text_table_.size(),
      "Text table index ", i, " out of range [0, ", text_table_.size(), ")");
  pieces.emplace_back(*text_table_[i]);
  strs.emplace_back(text_table_[i]);
}

Environment

  • PyTorch 2.11.0+cpu, 2.7.0+cpu (both affected)
  • Linux x86_64
  • Crash is deterministic

cc @EikanWang @jgong5 @wenzhe-nrv @sanchitintel

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING