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

C++: Assorted minor doc improvements #16939

Merged
merged 14 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
| test.cpp:151:9:151:24 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:152:15:152:18 | { ... } | This catch block |
| test.cpp:199:15:199:35 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:201:16:201:19 | { ... } | This catch block |
| test.cpp:212:14:212:34 | new | This allocation cannot throw. $@ is unnecessary. | test.cpp:213:34:213:36 | { ... } | This catch block |
| test.cpp:246:17:246:31 | new[] | This allocation cannot return null. $@ is unnecessary. | test.cpp:247:8:247:12 | ! ... | This check |
51 changes: 51 additions & 0 deletions cpp/ql/test/query-tests/Security/CWE/CWE-570/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,54 @@ void test_operator_new_without_exception_spec() {
int* p = new(42, std::nothrow) int; // GOOD
if(p == nullptr) {}
}

namespace std {
void *memset(void *s, int c, size_t n);
}

// from the qhelp:
namespace qhelp {
// BAD: the allocation will throw an unhandled exception
// instead of returning a null pointer.
void bad1(std::size_t length) noexcept {
int* dest = new int[length];
if(!dest) {
return;
}
std::memset(dest, 0, length);
// ...
}

// BAD: the allocation won't throw an exception, but
// instead return a null pointer. [NOT DETECTED]
void bad2(std::size_t length) noexcept {
try {
int* dest = new(std::nothrow) int[length];
std::memset(dest, 0, length);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this example isn't caught because the query assumes that any function with no body may throw:

As a follow-up to this PR we could add a whitelist of functions we know don't throw to fix this example.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I can tell there is no guarantee that std::memcpy doesn't throw in the language standard, but most implementations won't. The one I'm looking appears to get marked nothrow if it's compiled as C++, but not if it's compiled as C, and it appears to get C linkage and not nothrow in the actual database. It also has a body that just calls a builtin function.

So we could perhaps address this by assuming that a C linkage function doesn't throw ... but I don't think it's actually strictly true that a C linkage function can't throw. Or that builtin function don't throw, but again I'm not convinced that's true. Which leaves us with modelling ... we could model functions that don't throw, but again I don't know if it's strictly true that all versions of memset don't throw, and it seems a bit heavy handed to model it given that nothrow exists.

Copy link
Contributor

Choose a reason for hiding this comment

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

std::memcpy is from the C standard library, so it cannot throw. But you're right that it's not marked as noexcept in the standard.

Copy link
Contributor

@MathiasVP MathiasVP Jul 10, 2024

Choose a reason for hiding this comment

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

And this is probably the relevant part of the standard:

Screenshot 2024-07-10 at 14 07 22

So since memcpy cannot throw in C, and the contents of cstring and string.h is identical (other than the memchr stuff), my reading is that memcpy also isn't allowed to throw 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You make a good case for modelling. I'll create an issue for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Issue created. Thanks for helping me get to the bottom of this!

// ...
} catch(std::bad_alloc&) {
// ...
}
}

// GOOD: the allocation failure is handled appropriately.
void good1(std::size_t length) noexcept {
try {
int* dest = new int[length];
std::memset(dest, 0, length);
// ...
} catch(std::bad_alloc&) {
// ...
}
}

// GOOD: the allocation failure is handled appropriately.
void good2(std::size_t length) noexcept {
int* dest = new(std::nothrow) int[length];
if(!dest) {
return;
}
std::memset(dest, 0, length);
// ...
}
}