This is an archive of the discontinued LLVM Phabricator instance.

[NFC] Fix potential for use-after-free in DumpModuleInfoAction
ClosedPublic

Authored by Fznamznon on Mar 20 2023, 4:09 AM.

Details

Summary

Since each DumpModuleInfoAction can now contain a pointer to a
raw_ostream, saving there a poiter that owned by a local unique_ptr
may cause use-after-free. Clarify ownership and save a shared_ptr
inside of DumpModuleInfoAction instead.
Found by static analyzer.

Diff Detail

Event Timeline

Fznamznon created this revision.Mar 20 2023, 4:09 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 20 2023, 4:09 AM
Fznamznon requested review of this revision.Mar 20 2023, 4:09 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 20 2023, 4:09 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

I understand the potential for a use after free, since OutputStream is a raw pointer, but I don't understand the fix.

Fznamznon added a comment.EditedMar 27 2023, 1:57 AM

I understand the potential for a use after free, since OutputStream is a raw pointer, but I don't understand the fix.

Okay, probably commit message is a little confusing. The potential for a use after free is that OutputStream is a raw pointer as you already noted, but it is also accessible outside of DumpModuleInfoAction::ExecuteAction. Before the patch there was the case where result of local unique_ptr::get was saved there. When the function is exited, unique_ptr frees the memory it holds, making OutputStream poiniting to freed memory. Since OutputStream can be easily accessed outside of DumpModuleInfoAction::ExecuteAction, hence the potential for use-after-free. I wasn't sure if assigning nullptr to OutputStream at the end of DumpModuleInfoAction::ExecuteAction was a good idea, so I just added code avoiding saving result of unique_ptr::get to OutputStream .

I understand the potential for a use after free, since OutputStream is a raw pointer, but I don't understand the fix.

Okay, probably commit message is a little confusing. The potential for a use after free is that OutputStream is a raw pointer as you already noted, but it is also accessible outside of DumpModuleInfoAction::ExecuteAction. Before the patch there was the case where result of local unique_ptr::get was saved there. When the function is exited, unique_ptr frees the memory it holds, making OutputStream poiniting to freed memory. Since OutputStream can be easily accessed outside of DumpModuleInfoAction::ExecuteAction, hence the potential for use-after-free. I wasn't sure if assigning nullptr to OutputStream at the end of DumpModuleInfoAction::ExecuteAction was a good idea, so I just added code avoiding saving result of unique_ptr::get to OutputStream .

Hmmm, but this looks to be the only place DumpModuleInfoAction will initialize OutputStream to a nonnull value, so that seems to be a pretty significant change in behavior.

Should OutputStream be made into a shared_ptr so we can clarify the memory ownership instead of leaving it as a raw pointer? That should also address the use-after-free.

Fznamznon added a comment.EditedMar 28 2023, 8:35 AM

Should OutputStream be made into a shared_ptr so we can clarify the memory ownership instead of leaving it as a raw pointer? That should also address the use-after-free.

Well, I was thinking about that and wasn't sure about it either. https://reviews.llvm.org/D129456 added initialization of OutputStream from the outside. When last shared_ptr is destroyed, it destroys undelying object as well. In case underlying object came from the outside in a form of raw pointer, we may end up accidentally deleting it when DumpModuleInfoAction is destroyed.

Should OutputStream be made into a shared_ptr so we can clarify the memory ownership instead of leaving it as a raw pointer? That should also address the use-after-free.

Well, I was thinking about that and wasn't sure about it either. https://reviews.llvm.org/D129456 added initialization of OutputStream from the outside. When last shared_ptr is destroyed, it destroys undelying object as well. In case underlying object came from the outside in a form of raw pointer, we may end up accidentally deleting it when DumpModuleInfoAction is destroyed.

Yeah, exposing that data member was not the cleanest way to implement this functionality. This does fix the issue, but it keeps the unclean design -- I wonder if we could make that member private and use a constructor to specify it, then we can enforce the shared ownership property. (If this suggestion turns out to be a slog, then I'm okay with the current approach as well.)

Fznamznon updated this revision to Diff 509285.Mar 29 2023, 3:23 AM

Use private shared_ptr in DumpModuleInfoAction

Herald added a project: Restricted Project. · View Herald TranscriptMar 29 2023, 3:23 AM
Fznamznon retitled this revision from [NFC] Fix potential use-after-free in DumpModuleInfoAction::ExecuteAction() to [NFC] Fix potential for use-after-free in DumpModuleInfoAction.Mar 29 2023, 3:24 AM
Fznamznon edited the summary of this revision. (Show Details)
aaron.ballman accepted this revision.Mar 29 2023, 5:52 AM

This looks much better to me, thank you! LGTM with one small nit for safety.

clang/include/clang/Frontend/FrontendActions.h
191

A bit of extra safety around implicit conversions.

This revision is now accepted and ready to land.Mar 29 2023, 5:52 AM
Fznamznon updated this revision to Diff 509372.Mar 29 2023, 8:07 AM

Add a little bit of safety.

tahonermann added inline comments.Mar 29 2023, 9:08 AM
lldb/source/Commands/CommandObjectTarget.cpp
2182–2187

Use of std::shared_ptr with a deleter that doesn't do anything is unusual; I think this deserves a comment.

Add comment explaining custom deleter.

lldb/source/Commands/CommandObjectTarget.cpp
2182–2187

Sure, done.

tahonermann accepted this revision.Mar 29 2023, 11:02 AM

Thank you, Mariya! Looks good to me!

The test fail is unrelated, seem to be broken by https://reviews.llvm.org/D146811 .

This revision was landed with ongoing or failed builds.Mar 30 2023, 3:45 AM
This revision was automatically updated to reflect the committed changes.