The-OpenROAD-Project / The-OpenROAD-Project/OpenSTA

out-of-bounds std::vector<Delay> access in RequiredCmp::requiredsSave during timing-driven global placement

Open
#398 0 comments 0 reactions 1 assignee View on GitHub

@dsengupta0628 is already working on this.

Since Sep 9, 2026.

Dominant language
Verilog
Stars
619
Forks
270
Avg merge
5d 15h
Merged PRs (30d)
4

Description

Environment

  • OpenSTA as vendored in OpenROAD (built from ORFS commit: 26Q3)
  • PDK: IHP SG13G2/SG13CMOS5L; design: a RISC-V CPU block with SRAM macros
  • Reproduces deterministically and single-threaded (-threads 1) — not a data race.

Summary

A std::vectorsta::Delay is indexed out of bounds inside RequiredCmp::requiredsSave(), aborting the run. It fires from timing-driven global placement (gpl), which calls the resizer's slack estimation (rsz::Resizer::findResizeSlacks → Rebuffer::fullyRebuffer → Search::findRequireds).

Assertion + backtrace

  /usr/include/c++/13/bits/stl_vector.h:1128: constexpr std::vector<_Tp, _Alloc>::reference
  std::vector<_Tp, _Alloc>::operator[](size_type)
  [with _Tp = sta::Delay; _Alloc = std::allocator<sta::Delay>;
   reference = sta::Delay&; size_type = long unsigned int]:
  Assertion '__n < this->size()' failed.
  Signal 6 received
  Stack trace:
   0# 0x0000000000EACE66 in openroad
   1# 0x0000000000045330 in libc.so.6
   2# pthread_kill in libc.so.6
   3# gsignal in libc.so.6
   4# abort in libc.so.6
   5# 0x00000000000DF90D in libstdc++.so.6
   6# sta::RequiredCmp::requiredsSave(sta::Vertex*, sta::StaState const*)
   7# sta::RequiredVisitor::visit(sta::Vertex*)
   8# sta::BfsIterator::visitParallel(int, sta::VertexVisitor*)
   9# sta::Search::findRequireds(int)
  10# rsz::Rebuffer::fullyRebuffer(sta::Pin*)
  11# rsz::Resizer::findResizeSlacks(bool, bool, float)
  12# gpl::TimingBase::executeTimingDriven(bool, bool)
  13# gpl::NesterovPlace::runTimingDriven(...)
  14# gpl::NesterovPlace::doNesterovPlace(int)
  15# gpl::Replace::doNesterovPlace(int, gpl::PlaceOptions const&, int)
  16# replace_nesterov_place_cmd(...)
  17# 0x0000000000F7F4D4 in openroad
  18# TclNRRunCallbacks in libtcl8.6.so
     ... (Tcl eval / main frames 18–29) ...
  29# _start in openroad

Trigger

global_placement with -timing_driven enabled (OpenROAD replace_nesterov_place_cmd → gpl::…doNesterovPlace → runTimingDriven). During a timing-driven placement iteration, gpl asks the resizer for resize slacks; fullyRebuffer runs findRequireds, and the required-time visitor trips the OOB.

Root cause (analysis)

RequiredCmp::requireds_ is sized to the vertex's tag-group path count at init (requiredsInit). During the required-time traversal each path is indexed via path->pathIndex(sta), and that index comes back ≥ requireds_.size(), so requireds_[path_index] (the std::vectorsta::Delay) is accessed past the end. Two sites in search/Search.cc:

  • RequiredCmp::requiredSet() — requireds_[path_index]
  • RequiredCmp::requiredsSave() — requireds_[path_index] (the aborting frame)

The path count that sized requireds_ no longer matches the paths iterated — consistent with findRequireds being invoked mid-placement (from gpl's timing-driven loop) before arrivals/tag-groups have settled after the graph was perturbed.

Workaround

Defensive bounds guards at both sites; stops the abort and lets timing-driven placement complete (verified on the failing design):

  --- a/search/Search.cc
  +++ b/search/Search.cc
  @@ RequiredCmp::requiredSet(size_t path_index, ... )
  +  if (path_index >= requireds_.size())
  +    return;
     if (delayGreater(required, requireds_[path_index], min_max, sta)) {
  @@ RequiredCmp::requiredsSave(Vertex *vertex, ... )
       size_t path_index = path->pathIndex(sta);
  +    if (path_index >= requireds_.size())
  +      continue;
       const Required req = requireds_[path_index];

Claude came up with this workaround. I honestly have no idea if that the correct way to fix this issue or not. Let me know if you need more information from my side. Thanks!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.