OpenRCX: extMeasure::calcRes0 segfaults on a null getRes() result when a rules file has sparse per-layer tables

Open Beginner friendly
#12,052 2 comments 0 reactions 1 assignee View on GitHub

Maintainers usually reply within 1 day

@AcKoucher is already working on this.

Since Nov 2, 2026.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
cpp
Domain
backend, databases

Research direction

Start in src/rcx/src/extmeasure_res.cpp at extMeasure::calcRes0 (around lines 194–195), then compare its getRes() handling with extMeasure::calcRes and extMain::calcRes0 nearby. Add a null-result guard so an empty per-layer table contributes zero resistance, and check the OpenRCX extraction path with a rules file that leaves a routed layer uncalibrated; extraction should complete without a segfault.

Written by the indexing model from the issue text.

Description

bug rcx
Describe the bug

rcx::extMeasure::calcRes0 (src/rcx/src/extmeasure_res.cpp:194-195) dereferences the result of extDistWidthRCTable::getRes() without a null check. getRes() legitimately returns nullptr when a layer's width/distance RC table is empty (getWidthIndex() describes this as "the RC info for a particular pattern is empty", i.e. an expected case). So extract_parasitics -ext_model_file <rules> segfaults with Signal 11 in calcRes0 during coupling-cap extraction when the rules file has no data for a routed layer, e.g. one calibrated with fewer layers than the technology routes on. The neighbouring lookups in the same file, extMeasure::calcRes and extMain::calcRes0, already guard against nullptr.

Expected Behavior

Extraction should complete. A missing RC table entry should be skipped, contributing 0 resistance (optionally with a warning), the way extMeasure::calcRes and extMain::calcRes0 already handle a nullptr from getRes(). It should not segfault.

Environment
Not built from source, so ./etc/Env.sh doesn't apply: prebuilt openroad from the LibreLane container image ghcr.io/librelane/librelane:3.0.16, run through LibreLane's OpenROAD.RCX step.

openroad -version: dcf36133a369abc8f3c5e5738cd4d82e4903c0e0 (2026-02-17)
Host: Red Hat Enterprise Linux 8.10, kernel 4.18.0-553.144.1.el8_10.x86_64, rootless podman

The faulty code (extmeasure_res.cpp:194-195) is unchanged on master as of 7c837529a8 (2026-10-29).
To Reproduce
  1. Generate an OpenRCX rules file with the calibration flow in src/rcx/calibration (bench_wires -> golden SPEF -> write_rules), but calibrate fewer routing layers than the technology has. For example, use bench_wires -met_cnt 2 on a technology with 4 routing layers, so the RESOVER width/distance tables of the upper layers are empty.

  2. On a placed-and-routed design that routes on one of those uncalibrated layers, run:
    extract_parasitics -ext_model_file -lef_res

  3. OpenROAD segfaults during coupling-cap extraction:

    [INFO RCX-0439] Coupling Cap extraction ...
    [INFO RCX-0440] Coupling threshhold is 0.1000 fF, ...
    Signal 11 received
    Stack trace:
    2# rcx::extMeasure::calcRes0(double*, unsigned int, unsigned int, int, int)
    3# rcx::extMeasure::measureRC(std::array<int, 21ul>&)
    4# rcx::Track::findOverlap(...)
    5# rcx::Track::couplingCaps(...)
    6# rcx::Grid::couplingCaps(...)
    7# rcx::GridTable::couplingCaps(...)
    8# rcx::extMain::couplingFlow(...)
    9# rcx::extMain::makeBlockRCsegs(...)
    10# rcx::Ext::extract(ExtractOptions)

Calibrating every routing layer (-met_cnt equal to the number of routing layers) avoids the crash. The rules file and design come from a proprietary PDK, so I can't share them, but step 1 should reproduce the condition on any technology.

Relevant log output

Screenshots

No response

Additional Context

Root cause: in extMeasure::calcRes0 (src/rcx/src/extmeasure_res.cpp:194-195)

extDistRC* rc = rcModel->_resOver[tgtMet]->getRes(0, _width, dist1, dist2);
double R = len * rc->res_;   // rc is never null-checked

getRes() (extmeasure_res.cpp:347) returns nullptr when getWidthIndex() (extRCmodel.cpp:705) finds the table empty (_lastWidth == -1). The neighbouring lookups already handle this:

  • extMeasure::calcRes (extmeasure_res.cpp:172-177): if (rc != nullptr) { ... }
  • extMain::calcRes0 (extmeasure_res.cpp:215-218): if (rc == nullptr) { continue; }

Suggested fix: add the same guard to extMeasure::calcRes0:

if (rc == nullptr) {
  continue;
}

Both callers (extmeasure_couple.cpp:715, extmeasure.cpp:2596) zero-initialise the deltaRes array, so a skipped model contributes 0. The unbuilt #ifdef RES_V2 copy in extmeasure_res_v2.cpp:885-886 has the same pattern.

Line numbers are for master as of 7c837529a8, where this code is unchanged from earlier releases.

Dominant language
Verilog
Stars
3.2k
Forks
1.1k
Avg merge
2d 9h
Merged PRs (30d)
270

Getting set up

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.

More from The-OpenROAD-Project/OpenROAD

All issues in The-OpenROAD-Project/OpenROAD

Similar issues

More Backend & API Design issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.