Skip to content

Conversation

@NikEfth
Copy link
Collaborator

@NikEfth NikEfth commented Dec 23, 2025

This PR fixes a memory leak detected by AddressSanitizer in ScatterSimulation::set_template_proj_data_info.
The issue was caused by calling arg.clone() multiple times inside dynamic_cast expressions, leading to leaked heap allocations when the cast failed.

The fix:
• Calls clone() exactly once
• Transfers ownership safely using a smart pointer
• Preserves the existing runtime type checks for supported ProjDataInfo types

This change is purely a memory-safety fix and does not alter behaviour or interfaces.

Changes in this pull request

Testing performed

Related issues

Checklist before requesting a review

  • I have performed a self-review of my code
  • [] I have added docstrings/doxygen in line with the guidance in the developer guide
  • [] I have implemented unit tests that cover any new or modified functionality (if applicable)
  • The code builds and runs on my machine
  • [] documentation/release_XXX.md has been updated with any functionality change (if applicable)

Contribution Notes

Please tick the following:

  • The content of this Pull Request (the Contribution) is intentionally submitted for inclusion in STIR (the Work) under the terms and conditions of the Apache-2.0 License.
  • I (or my institution) have signed the STIR Contribution License Agreement (not required for small changes).

@NikEfth
Copy link
Collaborator Author

NikEfth commented Dec 23, 2025

Based on CODACY suggestion I simplified a bit the set_template_proj_data_info() to use the shared_ptr

Copy link
Collaborator

@KrisThielemans KrisThielemans left a comment

Choose a reason for hiding this comment

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

I have flagged some minor style issues. In addition, it'd be best to add an overload for backwards compatibility.

void set_template_proj_data_info(const ProjDataInfo&);

which would then have to do the clone and call the other one, e.g.

set_template_proj_data_info(arg.make_shared_clone());

It's possible that you'd have to %ignore the new one in SWIG though.

write_to_file("image_for", *image1_sptr);

image = *image.get_empty_copy();
image.fill(0); //= *image.get_empty_copy();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm happy with this, but let's remove the comment.

Suggested change
image.fill(0); //= *image.get_empty_copy();
image.fill(0);

// rotate by 30 degrees
phi2 = 30 * _PI / 180;
VoxelsOnCartesianGrid<float> image2 = *image.get_empty_copy();
VoxelsOnCartesianGrid<float> image2(image); // = *image.get_empty_copy();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I see no good reason to do this, although I don't mind. (Should be equivalent, no?). If you prefer it this way, please delete the comment.

write_to_file("image_to_fwp", *image1_sptr);

image = *image.get_empty_copy();
image.fill(0); //= *image.get_empty_copy();
Copy link
Collaborator

Choose a reason for hiding this comment

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

delete comment

write_to_file("image_with_voxel_at_30_0", *image1_sptr);

image = *image.get_empty_copy();
image.fill(0); //= *image.get_empty_copy();
Copy link
Collaborator

Choose a reason for hiding this comment

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

delete comment

if (dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get()))
{
auto ptr = dynamic_cast<ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
const auto* ptr = dynamic_cast<const ProjDataInfoCylindricalNoArcCorr*>(proj_data_info_sptr.get());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm a bit confused that this works. const auto is a bit better than auto, but why make things more complicated by adding the *? It works without it (and means the same thing, I believe).

Same comment below of course.


void
ScatterSimulation::set_template_proj_data_info(const ProjDataInfo& arg)
ScatterSimulation::set_template_proj_data_info(std::shared_ptr<const ProjDataInfo> arg)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
ScatterSimulation::set_template_proj_data_info(std::shared_ptr<const ProjDataInfo> arg)
ScatterSimulation::set_template_proj_data_info(shared_ptr<const ProjDataInfo> arg)

Of course, stir::shared_ptr is now std::shared_ptr, but we use this idiom everywhere...

#endif
CartesianCoordinate3D<float> detector_coord_A, detector_coord_B;
auto ptr = dynamic_cast<ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
const auto* ptr = dynamic_cast<const ProjDataInfoBlocksOnCylindricalNoArcCorr*>(proj_data_info_sptr.get());
Copy link
Collaborator

Choose a reason for hiding this comment

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

same comment on const auto*. I'd just use const auto

void set_template_proj_data_info(const std::string&);

void set_template_proj_data_info(const ProjDataInfo&);
void set_template_proj_data_info(std::shared_ptr<const ProjDataInfo> arg);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
void set_template_proj_data_info(std::shared_ptr<const ProjDataInfo> arg);
void set_template_proj_data_info(shared_ptr<const ProjDataInfo> arg);

to be consistent with STIR styling

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.

3 participants