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
CPP: Add query for detecting invalid uses of temporary unique pointers. #15078
base: main
Are you sure you want to change the base?
Conversation
|
QHelp previews: cpp/ql/src/Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.qhelpUse of unique pointer after lifetime endsCalling RecommendationEnsure that the pointer returned by ExampleThe following example gets a #include <memory>
std::unique_ptr<T> getUniquePointer();
void work(const T*);
// BAD: the unique pointer is deallocated when `get` returns. So `work`
// is given a pointer to invalid memory.
void work_with_unique_ptr_bad() {
const T* combined_string = getUniquePointer().get();
work(combined_string);
}The following example fixes the above code by ensuring that the pointer returned by the call to #include <memory>
std::unique_ptr<T> getUniquePointer();
void work(const T*);
// GOOD: the unique pointer outlives the call to `work`. So the pointer
// obtainted from `get` is valid.
void work_with_unique_ptr_good() {
auto combined_string = getUniquePointer();
work(combined_string.get());
}References
|
24532cd
to
53729c6
Compare
53729c6
to
e9bc5a5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't help taking a peek at the query, and this looks super good already!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code (and docs) LGTM! We might as well do a DCA run for good measure while we wait for the docs team to review the documentation.
Oh, we do need a change note, though
cpp/ql/src/Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql
Outdated
Show resolved
Hide resolved
cpp/ql/src/change-notes/2023-12-12-use-of-unique-pointer-after-lifetime-ends.md
Outdated
Show resolved
Hide resolved
Fix spelling in query id Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
| * Holds if the value of `e` outlives the enclosing full expression. For | ||
| * example, because the value is stored in a local variable. | ||
| */ | ||
| predicate outlivesFullExpr(Expr e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to point out that this has changed as well as been moved so the changes need reviewing as well
Now that I think about it I should have probably moved in a separate commit to the actual changes. I don't mind doing that if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, you're right. Thanks for pointing that out. I can see that there are some subtle changes here 🤔 Let me just think about the implication of this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the inclusion of not e.getConversion*().hasLValueToRValueConversion() in all the cases is a bad idea. For instance, consider this example that we currently catch on main:
const char* s = "hello";
const char* s13 = b1 ? std::string("hello").c_str() : s;this is bad because of temporary std::string string is destroyed, but because there's an lvalue-to-rvalue conversion on the other branch of the ternary we won't flag this case any longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... We should actually add this test to the UseOfStringAfterLifetimeEnds testcases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I seems that the entire conditional is marked as having an hasLValueToRValueConversion as well as the branch s. But surely the conversion only needs to happen for s. The clang AST shows that. However our AST doesn't. Reading cppreference it suggest this is an rvalue so we don't need a a conversion here.
I can fix this by looking for this specific case but am I misunderstanding this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I think you're understanding this correctly. I think this is case 6.1 in https://en.cppreference.com/w/cpp/language/operator_other#Conditional_operator. I think we should just exclude the ternary case from the not e.getConversion*().hasLValueToRValueConversion() conjunct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only a single suggestion, but otherwise this LGTM! Let's see if we can get a docs review in before your holidays start 🤞. Otherwise, I'll be happy to pick this up on Monday
| @@ -190,6 +190,8 @@ const char* test1(bool b1, bool b2) { | |||
| char* s9; | |||
| s9 = std::string("hello").data(); // BAD | |||
|
|
|||
| const char* s13 = b1 ? std::string("hello").c_str() : s1; // BAD | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this test 🙇
Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
This adds a new query for finding cases like
T* x = functionReturningUniquePtr().get()where a reference to the internals of a temporary unique pointer lasts longer than the unique pointer. This is based of thec_strquery and shares some of the code from it.