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

test: Check for some unlikely null dereferences in tests #5004

Open
wants to merge 36 commits into
base: develop
Choose a base branch
from

Conversation

ximinez
Copy link
Collaborator

@ximinez ximinez commented Apr 26, 2024

High Level Overview of Change

I stumbled upon these couple of places where test code could, but is very unlikely to, dereference a null pointer. I added a simple check to each of them.

Context of Change

This was a random find.

Even though these changes are tiny, I don't think they are insignificant enough to be considered "trivial", so I'm requesting two reviews.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)

Before / After

There should be no observable change, as tests aren't currently likely / able to get a null value.

Future Tasks

Maybe there are more of these?

@codecov-commenter
Copy link

codecov-commenter commented Apr 26, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 77.9%. Comparing base (838978b) to head (d2134af).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #5004     +/-   ##
=========================================
- Coverage     77.9%   77.9%   -0.0%     
=========================================
  Files          782     782             
  Lines        66621   66620      -1     
  Branches      8161    8139     -22     
=========================================
- Hits         51902   51890     -12     
- Misses       14719   14730     +11     

see 7 files with indirect coverage changes

Impacted file tree graph

@@ -51,7 +51,9 @@ void
nflags::operator()(Env& env) const
{
auto const sle = env.le(account_);
if (sle->isFieldPresent(sfFlags))
if (!sle)
Copy link
Collaborator

Choose a reason for hiding this comment

The 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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. Fixed.

@@ -51,7 +51,9 @@ void
nflags::operator()(Env& env) const
{
auto const sle = env.le(account_);
Copy link
Collaborator

Choose a reason for hiding this comment

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

The data members flags::account_ and nflags::account_ always hold a valid instance of the Account class. These data members are not pointers or std::optional values.

Hence, env.le(account_) must always exist, isn't it? Are you envisioning a future modification to these flags and nflags classes, due to which a null pointer might be returned here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A valid instance of an Account does not guarantee that that account has been created / funded on the ledger. So even though the Account is valid, a null sle could easily be returned. Consider this test:

Env env{*this};
Account alice("alice");

env.require(flags(alice, asfRequireDest));
env.require(nflags(alice, asfRequireAuth));

That's a perfectly validly written test, but without this change, it'll dereference a null SLE and crash. With this change, the test will fail in the changed functions, but it won't crash.

Copy link
Collaborator

Choose a reason for hiding this comment

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

okay, thanks for the clarification.

* upstream/develop:
  Set version to 2.2.0-rc1
* upstream/develop:
  Remove flow assert: (5009)
  Update list of maintainers: (4984)
* upstream/develop:
  Add external directory to Conan recipe's exports (5006)
  Add missing includes (5011)
ximinez and others added 15 commits July 2, 2024 17:03
* commit 'c706926': (23 commits)
  Change order of checks in amm_info: (4924)
  Add the fixEnforceNFTokenTrustline amendment: (4946)
  Replaces the usage of boost::string_view with std::string_view (4509)
  docs: explain how to find a clang-format patch generated by CI (4521)
  XLS-52d: NFTokenMintOffer (4845)
  chore: remove repeat words (5041)
  Expose all amendments known by libxrpl (5026)
  fixReducedOffersV2: prevent offers from blocking order books: (5032)
  Additional unit tests for testing deletion of trust lines (4886)
  Fix conan typo: (5044)
  Add new command line option to make replaying transactions easier: (5027)
  Fix compatibility with Conan 2.x: (5001)
  Set version to 2.2.0
  Set version to 2.2.0-rc3
  Add xrpl.libpp as an exported lib in conan (5022)
  Fix Oracle's token pair deterministic order: (5021)
  Set version to 2.2.0-rc2
  Fix last Liquidity Provider withdrawal:
  Fix offer crossing via single path AMM with transfer fee:
  Fix adjustAmountsByLPTokens():
  ...
* commit 'f6879da':
  Add bin/physical.sh (4997)
  Prepare to rearrange sources: (4997)
* upstream/develop:
  fixInnerObjTemplate2 amendment (5047)
  Set version to 2.3.0-b1
  Ignore restructuring commits (4997)
  Recompute loops (4997)
  Rewrite includes (4997)
  Rearrange sources (4997)
  Move CMake directory (4997)
* upstream/develop:
  fix CTID in tx command returns invalidParams on lowercase hex (5049)
  Invariant: prevent a deleted account from leaving (most) artifacts on the ledger. (4663)
  Bump codecov plugin version to version 4.5.0 (5055)
  fix "account_nfts" with unassociated marker returning issue (5045)
* upstream/develop:
  chore: remove repeat words (5053)
* upstream/develop:
  Add xrpld build option and Conan package test (5052)
* upstream/develop:
  Update BUILD.md after PR 5052 (5067)
* Add the same null check in the `flags` class as `nflags`.
* upstream/develop:
  chore: Add comments to SignerEntries.h (5059)
  chore: Rename two files from Directory* to Dir*: (5058)
* upstream/develop:
  Ensure levelization sorting is ASCII-order across platforms (5072)
  fix: Fix NuDB build error via Conan patch (5061)
  Disallow filtering account_objects by unsupported types (5056)
@ximinez ximinez requested a review from ckeshava August 1, 2024 20:41
* upstream/develop:
  Update gcovr EXCLUDE (5084)
  Fix crash inside `OverlayImpl` loops over `ids_` (5071)
  Set version to 2.3.0-b2
  docs: Document the process for merging pull requests (5010)
  Remove unused constants from resource/Fees.h (4856)
  fix: change error for invalid `feature` param in `feature` RPC (5063)
  Set version to 2.2.1
  Use error codes throughout fast Base58 implementation
  Improve error handling in some RPC commands
* upstream/develop:
  Factor out Transactor::trapTransaction (5087)
  Remove shards (5066)
* upstream/develop:
  Address rare corruption of NFTokenPage linked list (4945)
* upstream/develop:
  chore: Fix documentation generation job: (5091)
  chore: libxrpl verification on CI (5028)
* upstream/develop:
  docs: Update options documentation (5083)
  refactor: Remove dead headers (5081)
  refactor: Remove reporting mode (5092)
* upstream/develop:
  Update Release Notes for 2.2.1 and 2.2.2
  Set version to 2.2.2
  Allow only 1 job queue slot for each validation ledger check
  Allow only 1 job queue slot for acquiring inbound ledger.
  Track latencies of certain code blocks, and log if they take too long
* upstream/develop:
  test: Retry RPC commands to try to fix MacOS CI jobs (5120)
* upstream/develop:
  Set version to 2.3.0-b4
  feat(SQLite): allow configurable database pragma values (5135)
  refactor: re-order PRAGMA statements (5140)
  fix(book_changes): add "validated" field and reduce RPC latency (5096)
  chore: fix typos in comments (5094)
  Set version to 2.2.3
  Update SQLite3 max_page_count to match current defaults (5114)
* upstream/develop:
  Expand Error Message for rpcInternal (4959)
  docs: clean up API-CHANGELOG.md (5064)
* upstream/develop:
  Consolidate definitions of fields, objects, transactions, and features (5122)
  Ignore reformat when blaming
  Reformat code with clang-format-18
  Update pre-commit hook
  Update clang-format settings
  Update clang-format workflow
@vlntb vlntb self-requested a review October 30, 2024 18:05
Copy link
Collaborator

@vlntb vlntb left a comment

Choose a reason for hiding this comment

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

LGTM

* upstream/develop:
  docs: Add protobuf dependencies to linux setup instructions (5156)
  fix: reject invalid markers in account_objects RPC calls (5046)
  Update RELEASENOTES.md (5154)
  Introduce MPT support (XLS-33d): (5143)
* upstream/develop:
  Add hubs.xrpkuwait.com to bootstrap (5169)
* upstream/develop:
  Add AMMClawback Transaction (XLS-0073d) (5142)
@@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

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

In this file, there are 22 other dereferences of the *env.le() function. Visually, I'm convinced that none of them are null-pointers, because all the Account objects have been funded with some XRP.

But is it necessary to perform this type of null-pointer-check on all of them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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 env.le() that caught my attention, and then I just checked for null since I was changing it anyway.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

Choose a reason for hiding this comment

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

ok

@@ -41,7 +41,9 @@ void
flags::operator()(Env& env) const
{
auto const sle = env.le(account_);
if (sle->isFieldPresent(sfFlags))
if (!sle)
Copy link
Collaborator

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 in account_ 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 into env.require(). If that continues, there's probably no operational difference between the flags::constructor and the flags::operator()

Copy link
Collaborator Author

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 an Env parameter because the ledger state may change in between the ctor and the call to operator().

Copy link
Collaborator

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)

Copy link
Collaborator Author

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:

    template <class... Args>
    flags(Account const& account, Args... args)
        : flags_helper(args...), account_(account)
    {
    }

And here's the definition of the operator():

void
flags::operator()(Env& env) const
{
    auto const sle = env.le(account_);
    if (!sle)
        env.test.fail();
    else if (sle->isFieldPresent(sfFlags))
        env.test.expect((sle->getFieldU32(sfFlags) & mask_) == mask_);
    else
        env.test.expect(mask_ == 0);
}

The difference is the Env parameter to operator(). 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 the env.require helper wrapper.

In other words, to do the check in the ctor, the callers would have to look something like

env.require(flags(env, alice, flag));

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 get operator() 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.

* upstream/develop:
  Set version to 2.3.0-rc1
  Replace Uint192 with Hash192 in server_definitions response (5177)
  Fix potential deadlock (5124)
  Introduce Credentials support (XLS-70d): (5103)
  Fix token comparison in Payment (5172)
  Add fixAMMv1_2 amendment (5176)
* upstream/develop:
  fix: include `index` in `server_definitions` RPC (5190)
  Fix ledger_entry crash on invalid credentials request (5189)
* upstream/develop:
  Set version to 2.3.0-rc2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants