Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to using shared pointers for TagInfo #1145

Merged
merged 2 commits into from
Apr 16, 2023

Conversation

vladisslav2011
Copy link
Contributor

Avoid crash on Qt6 due to use after free.
Fixes #1143
Needs testing.

@yuvadm
Copy link

yuvadm commented Aug 24, 2022

Pulled and tested this fix, looks good AFAICT. There are some preexisting bugs with the tag lists, but no more seg faults.

@pinkavaj
Copy link
Contributor

pinkavaj commented Nov 8, 2022

I'm afraid this only hides the symptoms but does not solve the problem. The question is, how it is even possible the objects are destroyed, when eg. new tag is added. My guess is the QList stores the objects and relocates them once new object is added. So we cannot combine QList and reference its objects by pointers.

@vladisslav2011
Copy link
Contributor Author

vladisslav2011 commented Nov 8, 2022

So we cannot combine QList and reference its objects by pointers.

That's exactly the issue. Qt6 QList copies it's members during internal housekeeping and invalidates direct pointers. Dereferencing invalidated pointers results in bookmarks corruption, segfaults, etc... And using shared pointer solves it. We can copy/create/move shared pointers to an object, but the object remains the same and other shared pointer does not get invalidated. So QList stops breaking things and everything starts working as expected.

@pinkavaj
Copy link
Contributor

pinkavaj commented Nov 8, 2022

You are completely right, I have missed the important line, my claim above is invalid. Sorry for confusion.

Just thinking if it really makes sense to use relatively expansive shared_ptr. The objects should not be referenced after deletion from the list, if it is used it is likely a bug. Using shared_ptr will ensure the program will work, except it will use old data. I can imagine this might lead to wierd behaviouir (like using and displaying value from already deleted object).
With unique_ptr, the program will probably crash or it will be possible to identify such a think using valgrind

@pinkavaj
Copy link
Contributor

pinkavaj commented Nov 8, 2022

@argilo Are we expected to also add line to news?

@@ -158,21 +158,20 @@ bool Bookmarks::save()
stream << QString("# Tag name").leftJustified(20) + "; " +
QString(" color") << '\n';

QSet<TagInfo*> usedTags;
QMap<QString, TagInfo::sptr> usedTags;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shared pointers are relatively expansive compared to raw pointers. usedTags is referenced only within this function and tagInfo cannot be deleted during the lifetime of this function, so using shared pointer is unnecessary. Consider using raw pointers here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using raw pointers here makes Gqrx larger and makes code more complicated. You may try it yourself...

Copy link
Contributor

Choose a reason for hiding this comment

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

To make sure we understand each other, the proposal was to keep the old code (for now) except the necessary changes to make it work with shared pointer, eg. the only change necessary would be somethink like:

             BookmarkInfo& info = m_BookmarkList[iBookmark];
             for(int iTag = 0; iTag < info.tags.size(); ++iTag)
             {
-              TagInfo& tag = *info.tags[iTag];
-              usedTags.insert(&tag);
+              usedTags.insert(info.tags[iTag].get());
             }
         }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't guarantee, that QSet<TagInfo*> will work correctly here (collect tags with unique names). It may be better to change Bookmarks::m_TagList type form QList<TagInfo> to QMap<QString,TagInfo::sptr> and get rid of this loop.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, the non-uniqueness of tags should be solved too, but it seems to me this tries to solve more thinks in one MR. Mey-be separate commit if it is necessary to fix #1143 or separate MR if it can be done independently would help me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switching to a better container may be a different PR.
I do not know, how other people are using tags. Tag management should be improved if someone is using thousands of tags (adding a quick filter to help finding tags/bookmarks may help in this case).

@@ -315,10 +313,10 @@ const QColor BookmarkInfo::GetColor() const
{
for(int iTag=0; iTag<tags.size(); ++iTag)
{
TagInfo& tag = *tags[iTag];
if(tag.active)
TagInfo::sptr tag = tags[iTag];
Copy link
Contributor

Choose a reason for hiding this comment

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

This is completely valid and straightforward but a bit inefficient due to the copy-construction of the shared pointer. This assignment here is for convenience only, to avoid more complicated expressions bellow. Using a reference style (as before the change) is IMHO most convenient. Please consider dereferencing the pointer to reference.

Copy link
Contributor Author

@vladisslav2011 vladisslav2011 Nov 19, 2022

Choose a reason for hiding this comment

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

Thanks. Switching to using a const reference simplifies code a bit. Tests pending...
I forgot to rebase this on top of master. Done.
Qt5: works as before.
Qt6: works as before.

Copy link
Contributor

Choose a reason for hiding this comment

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

By dereferencing I ment somethink like TagInfo &tag = *tags[iTag]; :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. My latest commit is wrong. I'll remove it.
We should never create a reference to a smart pointer/a reference to a managed object as it defeats the purpose of using smart pointers to prevent use-after-free.

Copy link
Contributor

Choose a reason for hiding this comment

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

I does not see any way how use-after-free can happend within this function, is it?

Copy link
Contributor Author

@vladisslav2011 vladisslav2011 Nov 20, 2022

Choose a reason for hiding this comment

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

  1. I'm thinking about offloading plotter rendering to a background thread.
  2. https://schneide.blog/2012/01/30/dont-mix-c-smart-pointers-with-references/

Mixing smart pointers and references is a bad pattern, that should be avoided.

Copy link
Contributor

Choose a reason for hiding this comment

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

  1. Unfortunately I cannot see how this might affect this, because there is no code yet, so I'm afraid it's hard to discuss.
  2. Fine article, but it applies to situation where the smart pointer value is passed to another function/location. I belive bad usages like this can be caught during code-review, so using it strictly in local context is IMHO fine.

Nevertheless the situation still can be avoided just by replacing the tag by the whole tags[iTag], so this option can be considered too.

src/qtgui/bookmarks.h Outdated Show resolved Hide resolved
@vladisslav2011 vladisslav2011 force-pushed the fix_bookmarks_crash branch 2 times, most recently from 08c7969 to 8e5f251 Compare November 19, 2022 23:10
@argilo argilo added the cleanup label Mar 25, 2023
@argilo argilo force-pushed the fix_bookmarks_crash branch from 8e5f251 to 253411b Compare April 16, 2023 04:03
@argilo
Copy link
Member

argilo commented Apr 16, 2023

I rebased and resolved merge conflicts so I can test this out.

@argilo
Copy link
Member

argilo commented Apr 16, 2023

This seems like a sensible approach, and I verified that it resolves the invalid memory accesses.

@argilo argilo merged commit a5dd092 into gqrx-sdr:master Apr 16, 2023
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.

Segmentation fault when working with bookmarks
4 participants