Skip to content

BUG: close duplicated file descriptor if fdopen fails - #32386

Merged
ngoldbaum merged 2 commits into
numpy:mainfrom
sgoel2be24-cyber:codex/fix-32373-fd-leak
Aug 25, 2026
Merged

ngoldbaum merged 2 commits into
numpy:mainfrom
sgoel2be24-cyber:codex/fix-32373-fd-leak

Conversation

@sgoel2be24-cyber

Copy link
Copy Markdown

Fixes #32373

PR summary

The duplicated file descriptor `fd2` was leaked which was created through Python’s `os.dup`.

The fix was done by making the os module reference retained until the conversion is finished. If 'fdopen' fails, the code calls os.close(fd2) before returning the existing error. Pairing os.close with os.dup is also safer for Windows runtime ownership.

The regression test makes os.dup return an invalid descriptor (-2) so fdopen fails, records calls to os.close, confirms np.fromfile raises OSError, and verifies that the descriptor was passed to os.close.

First time committer introduction

I use NumPy in machine-learning projects for numerical computations and data preprocessing, and this is my first contribution to NumPy.

AI Disclosure

I used Codex 5.6 Sol High for assistance with issue selection ,preparing the C fix, regression test, and verification and understanding the code.

I have personally reviewed and understand the changes.

@sgoel2be24-cyber
sgoel2be24-cyber force-pushed the codex/fix-32373-fd-leak branch from b0aae83 to bea567d Compare August 21, 2026 21:14
@ngoldbaum ngoldbaum added the 09 - Backport-Candidate PRs tagged should be backported label Aug 24, 2026
monkeypatch.setattr(os, "dup", dup)
assert_raises(exc, np.fromfile, f)

def test_fromfile_failed_fdopen_closes_dup(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasm and musl don't validate the file descriptor, so this test will fail there:

https://github.com/numpy/numpy/actions/runs/32527587113/job/97469436653?pr=32386#step:8:3325

The other CI failures are unrelated and will be fixed by #32407.

Suggested change
def test_fromfile_failed_fdopen_closes_dup(
@pytest.mark.skipif(IS_WASM or IS_MUSL,
reason="musl and emscripten libc fdopen do not validate the fd")
def test_fromfile_failed_fdopen_closes_dup(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, added the WASM/musl skip and rebased onto current main, which includes #32407. The full test module and fresh CircleCI run pass.

Comment on lines +124 to +126
ret = PyObject_CallMethod(os, "close", "i", fd2);
Py_XDECREF(ret);
Py_DECREF(os);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        ret = PyObject_CallMethod(os, "close", "i", fd2);
        Py_DECREF(os);
        if (ret == NULL) {
            PyErr_WriteUnraisable(NULL);
        }
        Py_XDECREF(ret);
        PyErr_SetString(PyExc_IOError,
                        "Getting a FILE* from a Python file object via "
                        "_fdopen failed. If you built NumPy, you probably "
                        "linked with the wrong debug/release runtime");
        return NULL;

The PyObject_CallMethod call could fail. Since you're trying to raise an exception anyway, I don't think there's a good way to handle that, so better to make that explicit instead of overwriting an active exception.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I now report a failed os.close call with PyErr_WriteUnraisable before setting the intended OSError. I also exercised this failure path locally.

@sgoel2be24-cyber

Copy link
Copy Markdown
Author

Both review suggestions are addressed in bb1d945, and the fresh CircleCI run is green. Could you please take another look when convenient?

@ngoldbaum

Copy link
Copy Markdown
Member

Thanks @sgoel2be24-cyber!

@ngoldbaum
ngoldbaum merged commit 444afc2 into numpy:main Aug 25, 2026
91 checks passed
@charris charris removed the 09 - Backport-Candidate PRs tagged should be backported label Aug 25, 2026
charris pushed a commit that referenced this pull request Aug 25, 2026
Co-authored-by: Shikhar Goel <223222024+sgoel2be24-cyber@users.noreply.github.com>
charris added a commit that referenced this pull request Aug 25, 2026
BUG: close duplicated file descriptor if fdopen fails (#32386)
736-c41-2c1-e464fc974 added a commit to Swiss-Armed-Forces/Loom that referenced this pull request Sep 15, 2026
This MR contains the following updates:

| Package | Type | Update | Change | OpenSSF |
|---|---|---|---|---|
| [numpy](https://github.com/numpy/numpy) ([changelog](https://numpy.org/doc/stable/release)) | dependencies | patch | `2.5.2` → `2.5.3` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/numpy/numpy/badge)](https://securityscorecards.dev/viewer/?uri=github.com/numpy/numpy) |
| [openai](https://github.com/openai/openai-python) | dependencies | patch | `2.52.0` → `2.52.1` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/openai/openai-python/badge)](https://securityscorecards.dev/viewer/?uri=github.com/openai/openai-python) |
| [pydantic](https://github.com/pydantic/pydantic) ([changelog](https://docs.pydantic.dev/latest/changelog/)) | dependencies | patch | `2.13.4` → `2.13.5` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/pydantic/pydantic/badge)](https://securityscorecards.dev/viewer/?uri=github.com/pydantic/pydantic) |
| [pydantic-ai](https://github.com/pydantic/pydantic-ai) ([changelog](https://github.com/pydantic/pydantic-ai/releases)) | dependencies | patch | `2.27.0` → `2.27.1` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/pydantic/pydantic-ai/badge)](https://securityscorecards.dev/viewer/?uri=github.com/pydantic/pydantic-ai) |
| [types-requests](https://github.com/python/typeshed) ([changelog](https://github.com/typeshed-internal/stub_uploader/blob/main/data/changelogs/requests.md)) | dependencies | patch | `2.33.0.20260712` → `2.33.0.20260906` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/python/typeshed/badge)](https://securityscorecards.dev/viewer/?uri=github.com/python/typeshed) |
| [uvicorn](https://github.com/Kludex/uvicorn) ([changelog](https://uvicorn.dev/release-notes)) | dependencies | patch | `0.52.3` → `0.52.4` | [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/Kludex/uvicorn/badge)](https://securityscorecards.dev/viewer/?uri=github.com/Kludex/uvicorn) |

---

### Release Notes

<details>
<summary>numpy/numpy (numpy)</summary>

### [`v2.5.3`](https://github.com/numpy/numpy/releases/tag/v2.5.3): (Sep 6, 2026)

[Compare Source](numpy/numpy@v2.5.2...v2.5.3)

### NumPy 2.5.3 Release Notes

The NumPy 2.5.3 is a patch release that fixes bugs discovered after the 2.5.2
release. Apart from the usual bug and maintenance work, there are a number of
StringDType related fixes for problems discovered during the ongoing string
work in the main branch.

This release supports Python versions 3.12-3.15

#### Changes

- Casting a fixed-width byte string array (`np.bytes_`) to `StringDType`
  now raises `TypeError` when the bytes are not valid UTF-8. Previously the
  invalid bytes were stored as-is and later caused undefined behavior in
  string operations.

  ([gh-32296](numpy/numpy#32296))

- `MaskedArray._fill_value` would become stale when ufuncs that change dtype
  left the result holding a fill\_value typed for the old dtype. The mismatch
  was silent until something later called `_check_fill_value`, such as
  `.view()`, and then a `TypeError` would be raised. Now, when the copied
  fill\_value is no longer valid for the new dtype, fall back to the
  default fill\_value for that dtype instead of propagating the stale value.
  This may raise a `ComplexWarning` if the fill\_value is complex and the
  new dtype is real.

  ([gh-32423](numpy/numpy#32423))

#### Contributors

A total of 9 people contributed to this release. People with a "+" by their
names contributed a patch for the first time.

- Charles Harris
- Iason Krommydas
- James Davies +
- Joren Hammudoglu
- Maanas Arora
- Matti Picus
- Nathan Goldbaum
- Shikhar Goel +
- Yeonho Kim +

#### Pull requests merged

A total of 27 pull requests were merged for this release.

- [#&#8203;32235](numpy/numpy#32235): MAINT: Prepare 2.5.x for further development
- [#&#8203;32289](numpy/numpy#32289): BUG: raise ValueError when reading into record array with references...
- [#&#8203;32290](numpy/numpy#32290): BUG: avoid uninitialized memory access / NULL-pointer deref in...
- [#&#8203;32291](numpy/numpy#32291): TYP: fix `np.random.{get,set}_bit_generator` implicit re-exports...
- [#&#8203;32292](numpy/numpy#32292): BUG: don't assume strides are a multiple of itemsize in stringdtype...
- [#&#8203;32293](numpy/numpy#32293): CI: fix ccache CC override, add CXX in mac Conda CI ([#&#8203;32285](numpy/numpy#32285))
- [#&#8203;32303](numpy/numpy#32303): BUG: Fix ref leak in \[convert\_from\_type]{#convert\_from\_type} for custom scalar types...
- [#&#8203;32304](numpy/numpy#32304): BUG: fix a number of issues around iterators and StringDType...
- [#&#8203;32326](numpy/numpy#32326): TST: avoid allocating huge tuple of arrays in concatenate test...
- [#&#8203;32338](numpy/numpy#32338): BUG: avoid possible UB in 'safe' multiplication helpers ([#&#8203;32294](numpy/numpy#32294))
- [#&#8203;32339](numpy/numpy#32339): MAINT: use `PyObject_` functions instead of raw `PyArray_ ones` ([#&#8203;32331](numpy/numpy#32331))
- [#&#8203;32378](numpy/numpy#32378): BUG: validate UTF-8 and harden StringDType bounds handling ([#&#8203;32296](numpy/numpy#32296))
- [#&#8203;32380](numpy/numpy#32380): BUG: fix two error handling mistakes in stringdtype replace loop...
- [#&#8203;32381](numpy/numpy#32381): BUG: fix visibility annotations for functions in StringDType...
- [#&#8203;32384](numpy/numpy#32384): MAINT: Update ml\_dtypes pin to 8/21/2026.
- [#&#8203;32385](numpy/numpy#32385): MAINT: Update numpy/\_core/src/umath/svml
- [#&#8203;32410](numpy/numpy#32410): MAINT: skip failing cython limited API tests on Cython 3.3.0...
- [#&#8203;32427](numpy/numpy#32427): BUG: close duplicated file descriptor if fdopen fails ([#&#8203;32386](numpy/numpy#32386))
- [#&#8203;32428](numpy/numpy#32428): MAINT: add missing space in warning and error messages ([#&#8203;32405](numpy/numpy#32405))
- [#&#8203;32430](numpy/numpy#32430): BUG: fix error handling in StringDType to fixed-width bytes case...
- [#&#8203;32441](numpy/numpy#32441): MAINT: Only run nightly BLAS tests on main.
- [#&#8203;32471](numpy/numpy#32471): BUG: fix memory leak in StringDType creation error path ([#&#8203;32470](numpy/numpy#32470))
- [#&#8203;32477](numpy/numpy#32477): BUG: fix stale fill\_value after ufuncs change MaskedArray dtype...
- [#&#8203;32478](numpy/numpy#32478): MAINT: exit deadlock tests quickly on slow hardware ([#&#8203;32466](numpy/numpy#32466))
- [#&#8203;32481](numpy/numpy#32481): BUG: Backport StringDType byteorder fixes
- [#&#8203;32506](numpy/numpy#32506): DOC: use static scipy doc site for intershpinx ([#&#8203;32503](numpy/numpy#32503))
- [#&#8203;32509](numpy/numpy#32509): BUG: fix crash in ufunc.resolve\_dtypes with a Python scalar type...

</details>

<details>
<summary>openai/openai-python (openai)</summary>

### [`v2.52.1`](https://github.com/openai/openai-python/blob/HEAD/CHANGELOG.md#2521-2026-07-31)

[Compare Source](openai/openai-python@v2.52.0...v2.52.1)

Full Changelog: [v2.52.0...v2.52.1](openai/openai-python@v2.52.0...v2.52.1)

##### Chores

- **ci:** pin setup-uv v5 to its underlying commit ([#&#8203;3560](openai/openai-python#3560)) ([cbdc98b](openai/openai-python@cbdc98b))

</details>

<details>
<summary>pydantic/pydantic (pydantic)</summary>

### [`v2.13.5`](https://github.com/pydantic/pydantic/releases/tag/v2.13.5)

[Compare Source](pydantic/pydantic@v2.13.4...v2.13.5)

#### v2.13.5 (2026-08-28)

##### What's Changed

##### Fixes

- Allow reuse of validators when plugins are set by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;13535](pydantic/pydantic#13535)
- Fix missing GC traversal on some `pydantic-core` struct fields by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;13624](pydantic/pydantic#13624)
- Fix missing GC traversal in `pydantic-core` for `GeneralFieldsSerializer` by [@&#8203;Viicos](https://github.com/Viicos) in [#&#8203;13629](pydantic/pydantic#13629)
- Count validated model fields once in smart unions by [@&#8203;tamird](https://github.com/tamird) in [#&#8203;13731](pydantic/pydantic#13731)

</details>

<details>
<summary>pydantic/pydantic-ai (pydantic-ai)</summary>

### [`v2.27.1`](https://github.com/pydantic/pydantic-ai/releases/tag/v2.27.1): (2026-08-10)

[Compare Source](pydantic/pydantic-ai@v2.27.0...v2.27.1)

##### 🛡️ Security

This release fixed an information-disclosure issue: retry-prompt content (validation feedback sent back to the model, which can quote invalid values from its output) was not redacted by `InstrumentationSettings(include_content=False)` when the retry was not tied to a tool call. Now disclosed as [GHSA-3gh4-cghq-f8v4](GHSA-3gh4-cghq-f8v4) (low). Fixed here in `2.27.1` ([#&#8203;7357](pydantic/pydantic-ai#7357)); v1 users should upgrade to `1.107.4` or later.

<!-- Release notes generated using configuration in .github/release.yml at main -->

#### What's Changed

##### 🐛 Bug Fixes

- Restore tool spans for failed argument validation by [@&#8203;adtyavrdhn](https://github.com/adtyavrdhn) in [#&#8203;6601](pydantic/pydantic-ai#6601)
- Fix `XaiStreamedResponse` finish\_reason mapping for streaming responses by [@&#8203;pydanty](https://github.com/pydanty)\[bot] in [#&#8203;6814](pydantic/pydantic-ai#6814)
- Point offline web UI hosting at the self-contained chat UI build by [@&#8203;dsfaccini](https://github.com/dsfaccini) in [#&#8203;7349](pydantic/pydantic-ai#7349)
- Allow adaptive thinking with Tool Output and forced tool choice on Anthropic by [@&#8203;pydanty](https://github.com/pydanty)\[bot] in [#&#8203;7200](pydantic/pydantic-ai#7200)
- Gate `RetryPromptPart` OpenTelemetry content on `include_content` by [@&#8203;sean-kim05](https://github.com/sean-kim05) in [#&#8203;7357](pydantic/pydantic-ai#7357)

**Full Changelog**: <pydantic/pydantic-ai@v2.27.0...v2.27.1>

</details>

<details>
<summary>Kludex/uvicorn (uvicorn)</summary>

### [`v0.52.4`](https://github.com/Kludex/uvicorn/releases/tag/0.52.4): Version 0.52.4

[Compare Source](Kludex/uvicorn@0.52.3...0.52.4)

##### Fixed

- Remove duplicate `Date` headers from accepted WebSocket handshakes with `websockets-sansio` ([#&#8203;3078](Kludex/uvicorn#3078))

**Full Changelog**: <Kludex/uvicorn@0.52.3...0.52.4>

</details>

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4zOS4yIiwidXBkYXRlZEluVmVyIjoiNDQuOTAuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwicmVub3ZhdGUiXX0=-->

See merge request swiss-armed-forces/cyber-command/cea/loom!769

Co-authored-by: Loom MR Pipeline Trigger <group_103951964_bot_9504bb8dead6d4e406ad817a607f24be@noreply.gitlab.com>
Co-authored-by: shrewd-laidback palace <shrewd-laidback-palace-736-c41-2c1-e464fc974@swiss-armed-forces-open-source.ch>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: File descriptor leak in npy_PyFile_Dup2 when fdopen fails

3 participants