-
Notifications
You must be signed in to change notification settings - Fork 1.7k
fix: Reject credentials on pseudo-accounts and unpin Vault/LoanBroker (FN-36) #7877
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
base: develop
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -5,8 +5,10 @@ | |
| #include <xrpl/basics/base_uint.h> | ||
| #include <xrpl/basics/chrono.h> | ||
| #include <xrpl/beast/utility/Journal.h> | ||
| #include <xrpl/beast/utility/instrumentation.h> | ||
| #include <xrpl/ledger/ApplyView.h> | ||
| #include <xrpl/ledger/ReadView.h> | ||
| #include <xrpl/ledger/View.h> | ||
| #include <xrpl/ledger/helpers/AccountRootHelpers.h> | ||
| #include <xrpl/protocol/AccountID.h> | ||
| #include <xrpl/protocol/Feature.h> | ||
|
|
@@ -123,6 +125,36 @@ deleteSLE(ApplyView& view, SLE::ref sleCredential, beast::Journal j) | |
| return tesSUCCESS; | ||
| } | ||
|
|
||
| TER | ||
| deletePseudoAccountCredentials( | ||
| ApplyView& view, | ||
| AccountID const& pseudoAcct, | ||
| std::uint16_t maxNodesToDelete, | ||
| beast::Journal j) | ||
| { | ||
| XRPL_ASSERT( | ||
| isPseudoAccount(view.read(keylet::account(pseudoAcct))), | ||
| "xrpl::credentials::deletePseudoAccountCredentials : is a pseudo-account"); | ||
|
|
||
| // Delete the credentials linked into the pseudo-account's owner directory, | ||
| // visiting at most maxNodesToDelete entries. Any other object is left in | ||
| // place; the caller's own checks decide whether the remaining directory | ||
| // blocks deletion. If the bound is reached, cleanupOnAccountDelete returns | ||
| // tecINCOMPLETE and the caller propagates it so a later transaction resumes. | ||
| return cleanupOnAccountDelete( | ||
|
Contributor
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. DoS risk: unbounded deletion with no maxNodesToDelete limit. Unlike deleteAMMTrustLines (512 cap) and AccountDelete (1000 cap), an attacker can pin credentials to a pseudo-account and force one transaction to walk and delete an arbitrarily large directory. Add maxNodesToDelete parameter (e.g., 512), propagate tecINCOMPLETE to callers, and allow retryable cleanup instead of one-pass enumeration. Example pattern to follow:
Contributor
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, clanker! @tyalymov once maxNodesToDelete is added, let's add a test analogous to the existing kMaxDeletableAmmTrustLines overflow tests (AMM_test.cpp:5214, :7192) that pins more than the cap's worth of credentials to a pseudo-account pre-amendment and verifies tecINCOMPLETE + eventual multi-transaction cleanup.
Author
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. Thanks for catching this. I started implementing the bound and hit a snag I'd like to consult you on. The plan was to delete up to N credentials per transaction and return Two directions I came up with:
LoanBroker and AMM I can bound incrementally regardless; it's the Vault path I'm unsure about. Any thoughts on that?
Contributor
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 first direction sounds right, in the transactor we first attempt to delete the credentials. If we fail to do so because there are too many of them, then the user must submit the transaction again to delete the rest of the credentials and the account.
Author
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. Fixed |
||
| view, | ||
| keylet::ownerDir(pseudoAcct), | ||
| [&view, &j](LedgerEntryType nodeType, uint256 const&, SLE::pointer& sleItem) | ||
| -> std::pair<TER, SkipEntry> { | ||
| if (nodeType == ltCREDENTIAL) | ||
| return {deleteSLE(view, sleItem, j), SkipEntry::No}; | ||
|
|
||
| return {tesSUCCESS, SkipEntry::Yes}; | ||
| }, | ||
| j, | ||
| maxNodesToDelete); | ||
| } | ||
|
|
||
| NotTEC | ||
| checkFields(STTx const& tx, beast::Journal j) | ||
| { | ||
|
|
||
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.
Unbounded credential cleanup allows DoS: attacker can pre-load credentials before amendment activates, forcing unbounded work in one transaction. Add maxNodesToDelete bound and handle tecINCOMPLETE result like deleteAMMTrustLines:
Uh oh!
There was an error while loading. Please reload this page.
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.
Fixed