Skip to content

Fix memory safety in TUV-x Python bindings for array returns#759

Merged
K20shores merged 5 commits intodevelop-603-tuvx-examplesfrom
copilot/sub-pr-758
Jan 30, 2026
Merged

Fix memory safety in TUV-x Python bindings for array returns#759
K20shores merged 5 commits intodevelop-603-tuvx-examplesfrom
copilot/sub-pr-758

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jan 29, 2026

The _run_tuvx function created NumPy arrays from pointers to local std::vector buffers. When the lambda returned, these vectors were destroyed, leaving dangling pointers in the NumPy arrays.

Changes

  • Heap allocation with RAII: Use std::unique_ptr<std::vector<double>> instead of stack-allocated vectors for automatic exception-safe cleanup
  • Capsule-based ownership transfer: Create py::capsule with custom deleter to manage vector lifetime, then atomically transfer ownership to NumPy arrays
  • Exception safety: Create each NumPy array immediately after its capsule to prevent use-after-free if array construction throws

Implementation

// Before: stack allocation causes dangling pointers
std::vector<double> photolysis_rates(n_photolysis * (n_layers + 1));
py::array_t<double> py_photolysis = py::array_t<double>({n_photolysis, n_layers + 1}, photolysis_rates.data());
return py_photolysis;  // dangling pointer when vector destroyed

// After: heap allocation with capsule ownership
auto photolysis_rates = std::make_unique<std::vector<double>>(n_photolysis * (n_layers + 1));
double* photolysis_data = photolysis_rates->data();
auto photolysis_capsule = py::capsule(photolysis_rates.get(), [](void* v) { delete reinterpret_cast<std::vector<double>*>(v); });
photolysis_rates.release();
py::array_t<double> py_photolysis = py::array_t<double>({n_photolysis, n_layers + 1}, photolysis_data, photolysis_capsule);
return py_photolysis;  // capsule ensures cleanup when array destroyed

Applied to all five output arrays: photolysis_rates, heating_rates, dose_rates, actinic_flux, spectral_irradiance.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits January 29, 2026 21:51
Allocate output arrays on heap and use py::capsule to manage lifetime.
This prevents dangling pointers when local vectors are destroyed.

Co-authored-by: K20shores <[email protected]>
Use RAII-based memory management with std::unique_ptr instead of raw
pointers to ensure automatic cleanup if exceptions occur during
allocation or array construction.

Co-authored-by: K20shores <[email protected]>
Store data pointers before releasing unique_ptrs to prevent double-delete
if exceptions occur during numpy array construction.

Co-authored-by: K20shores <[email protected]>
Create each numpy array immediately after its capsule to prevent
use-after-free if exceptions occur during array construction.

Co-authored-by: K20shores <[email protected]>
Copilot AI changed the title [WIP] WIP address feedback from PR #758 for TUV-x examples and tutorial Fix memory safety in TUV-x Python bindings for array returns Jan 29, 2026
Copilot AI requested a review from K20shores January 29, 2026 21:59
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jan 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.49%. Comparing base (73f6b65) to head (4cd673c).

Additional details and impacted files
@@                    Coverage Diff                     @@
##           develop-603-tuvx-examples     #759   +/-   ##
==========================================================
  Coverage                      71.49%   71.49%           
==========================================================
  Files                            114      114           
  Lines                           8726     8726           
==========================================================
  Hits                            6239     6239           
  Misses                          2487     2487           
Flag Coverage Δ
cpp_fortran 65.87% <ø> (ø)
javascript 92.56% <ø> (ø)
python 73.26% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@K20shores K20shores marked this pull request as ready for review January 30, 2026 00:03
@K20shores K20shores merged commit 4936536 into develop-603-tuvx-examples Jan 30, 2026
45 of 50 checks passed
@K20shores K20shores deleted the copilot/sub-pr-758 branch January 30, 2026 00:03
K20shores added a commit that referenced this pull request Jan 30, 2026
* add v5.4 wavelength grid

* add v5.4 height grid

* add profile read function

* add profile data for v5.4

* add remaining profiles

* add v5.4 aerosol radiator

* add draft tuv-x 5.4 example

* add test of tuv 5.4 against standalone tuv-x

* added ts1 config test and example

* add TUV-x yaml configs

* tighten tolerances on tuv-x standalone tests

* allow in-place profile updates

* allow in-place grid element updates

* allow in-place radiator array updates

* start tuv-x jupyter notebook

* add second TUV-x tutorial

* typos

* add tuv-x dockerfile action

* address copilot comments and action failures

* try to address fortran and python action failures

* attempting to pull the fork head instead of main

* Update tutorials/8.tuv-x_standard_configurations.ipynb

Co-authored-by: Kyle Shores <[email protected]>

* merge main into develop-603-tuvx-examples

* packaging configs

* ignoring duplicate named nc files

* Fix memory safety in TUV-x Python bindings for array returns (#759)

* Initial plan

* Fix memory safety issue in TUV-x Python bindings

Allocate output arrays on heap and use py::capsule to manage lifetime.
This prevents dangling pointers when local vectors are destroyed.

Co-authored-by: K20shores <[email protected]>

* Improve exception safety using std::unique_ptr

Use RAII-based memory management with std::unique_ptr instead of raw
pointers to ensure automatic cleanup if exceptions occur during
allocation or array construction.

Co-authored-by: K20shores <[email protected]>

* Fix double-delete issue in ownership transfer

Store data pointers before releasing unique_ptrs to prevent double-delete
if exceptions occur during numpy array construction.

Co-authored-by: K20shores <[email protected]>

* Ensure atomic ownership transfer from capsule to array

Create each numpy array immediately after its capsule to prevent
use-after-free if exceptions occur during array construction.

Co-authored-by: K20shores <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: K20shores <[email protected]>

* [WIP] Update TUV-x examples and tutorial based on feedback (#760)

* Initial plan

* Align Run() docstring array dimensions with C interface and Python bindings

Co-authored-by: K20shores <[email protected]>

* Use consistent dimension labels with underscores in Run() docstring

Co-authored-by: K20shores <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: K20shores <[email protected]>

* responding to pr comments

* adding ffmpeg

---------

Co-authored-by: Kyle Shores <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: K20shores <[email protected]>
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.

3 participants