-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
test: Check for some unlikely null dereferences in tests #5004
Changes from all commits
95ca921
7ae906c
540713d
a926a2d
d5dd37b
546cd42
16835cf
0ff9758
b52276e
501f1f8
fb8b17e
85beb42
a173ecc
38344ba
1c9d1bb
045e50f
d51702d
0c867c9
828c444
46ecfe2
5508609
9a8cb65
24aad0a
2c00a3e
ef96f58
54dfbb3
4098f94
d83ad93
2c57792
e2b3b11
edc0670
590d616
9a528a6
203dbfa
2e925a8
d2134af
85cda1d
4e38577
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,9 @@ void | |
flags::operator()(Env& env) const | ||
{ | ||
auto const sle = env.le(account_); | ||
if (sle->isFieldPresent(sfFlags)) | ||
if (!sle) | ||
env.test.fail(); | ||
else if (sle->isFieldPresent(sfFlags)) | ||
env.test.expect((sle->getFieldU32(sfFlags) & mask_) == mask_); | ||
else | ||
env.test.expect(mask_ == 0); | ||
|
@@ -51,7 +53,9 @@ void | |
nflags::operator()(Env& env) const | ||
{ | ||
auto const sle = env.le(account_); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The data members Hence, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A valid instance of an
That's a perfectly validly written test, but without this change, it'll dereference a null There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okay, thanks for the clarification. |
||
if (sle->isFieldPresent(sfFlags)) | ||
if (!sle) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before line 44 (above), for the sake of consistency, we need to introduce a similar check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Fixed. |
||
env.test.fail(); | ||
else if (sle->isFieldPresent(sfFlags)) | ||
env.test.expect((sle->getFieldU32(sfFlags) & mask_) == 0); | ||
else | ||
env.test.pass(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,7 @@ class AccountSet_test : public beast::unit_test::suite | |
env.fund(XRP(10000), noripple(alice)); | ||
// ask for the ledger entry - account root, to check its flags | ||
auto const jrr = env.le(alice); | ||
BEAST_EXPECT((*env.le(alice))[sfFlags] == 0u); | ||
BEAST_EXPECT(jrr && jrr->at(sfFlags) == 0u); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this file, there are 22 other dereferences of the But is it necessary to perform this type of null-pointer-check on all of them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It wouldn't hurt to check. Even though it's been a while, I think it was the double call to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realize that I did note "Maybe there are more of these?" in the "Future Tasks" section of the description. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
} | ||
|
||
void | ||
|
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.
Can we add a check inside the
flags(Account const& account, Args... args)
constructor, to check for null-pointer inaccount_
data member? It is not useful to set flags on a non-existent, unfunded account.However, I've only observed the
flags
object passed as an input intoenv.require()
. If that continues, there's probably no operational difference between theflags::constructor
and theflags::operator()
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. In the
flags
ctor,account_
is a value, not a pointer. It would be premature to check the ledger via anEnv
parameter because the ledger state may change in between the ctor and the call tooperator()
.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.
That may be true, but why would you ever want to construct a
flags
object with an unfunded account?We can move the validity check up from the
operator()
into the constructor.I can't think of situations where you'd need to create a
flags(unfunded_account)
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.
It's not that you would want to create a
flags(unfunded_account)
, it's that the way these helper classes are constructed, there's no way to check whether the account is funded from the constructor.Here's the definition of the ctor:
And here's the definition of the
operator()
:The difference is the
Env
parameter tooperator()
. There isn't one of those available to the constructor, and we don't want one, because it would defeat the whole purpose of these helper classes and theenv.require
helper wrapper.In other words, to do the check in the ctor, the callers would have to look something like
Which is ugly and redundant.
The last problem, IMHO, with trying to do a check like this in the ctor is what do you do if it fails? You could do some variation of a
BEAST_EXPECT
that would cause the test suite to fail, but then what? You'd either have an object with a dud account inside it that's going to getoperator()
called on it anyway, or you're going to have to throw an exception, which is what I was trying to avoid with this check in the first place.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.
okay, this is a helpful explanation.
Why do you want to avoid throwing an exception in the constructor? I vaguely remember reading about it, I think it has something to do with the cleanup of the dead object. If you know any resources/blogs, I'd like to read it.
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.
There is nothing intrinsically wrong with throwing an exception in the ctor. It's a good way to handle certain types of errors where you want to prevent an object from being instantiated at all.
The reason I don't want to throw an exception in this ctor is because exceptions need to be caught or they terminate the whole program. The good news is that in the test suite, the tests are all wrapped so that exceptions will be caught, and treated as a test suite error. The bad news is that code is a blunt instrument, which doesn't tell you much about where and why the exception occurred. It also causes the rest of that suite to be skipped, as opposed to a failing
BEAST_EXPECT
, which logs a failure including file and line number, and finishes the rest of the suite. That makes debugging a lot easier.The alternative is to wrap every single instantiation of
flags
in atry/catch
block, which just adds unnecessary complication and syntactic garbage that doesn't add anything to the test.So the short answer is that I don't want to throw in the ctor because it adds unnecessary complication when the check in
operator()
is perfectly sufficient.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.
okay 👍 makes sense