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

fix(evm): issue with infinite recursion in erc20 funtoken contracts #2129

Merged
merged 4 commits into from
Dec 28, 2024

Conversation

onikonychev
Copy link
Contributor

@onikonychev onikonychev commented Dec 24, 2024

Summary by CodeRabbit

  • New Features

    • Added a new entry in the changelog detailing a bug fix for infinite recursion in ERC20 FunToken contracts.
    • Introduced a new smart contract, TestInfiniteRecursionERC20, which simulates ERC20 behavior with recursive functions.
    • Added a method to dynamically calculate gas limits for ERC20 contract calls.
    • Included a new test for handling infinite recursion scenarios in FunToken creation.
  • Bug Fixes

    • Resolved an infinite recursion issue in ERC20 FunToken contracts.
  • Tests

    • Expanded test coverage with a new test for the TestFunTokenInfiniteRecursionERC20 scenario.

@onikonychev onikonychev requested a review from a team as a code owner December 24, 2024 19:37
Copy link
Contributor

coderabbitai bot commented Dec 24, 2024

Walkthrough

This pull request introduces a new entry in the changelog for the Nibiru EVM project, highlighting a bug fix related to infinite recursion in ERC20 FunToken contracts. A new smart contract, TestInfiniteRecursionERC20, is added to simulate recursive behavior in token operations. Additionally, a function getCallGasWithLimit is introduced to dynamically manage gas limits, enhancing the robustness of contract interactions. The changes also include updates to the embedding of the contract artifact and the addition of a test method to verify the system's resilience against recursive calls.

Changes

File Change Summary
CHANGELOG.md Added entry for bug fix related to infinite recursion in ERC20 FunToken contracts
x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol New contract with recursive balanceOf() and transfer() methods
x/evm/embeds/artifacts/...TestInfiniteRecursionERC20.json Artifact file for the new test contract
x/evm/embeds/embeds.go Added embedding for new contract artifact
x/evm/keeper/erc20.go Introduced getCallGasWithLimit() function for dynamic gas limit management
x/evm/keeper/funtoken_from_erc20_test.go Added test method TestFunTokenInfiniteRecursionERC20

Possibly related PRs

Suggested reviewers

  • Unique-Divine

Poem

🐰 A Recursive Tale of Token Might
In ERC20's dance of byte and light,
Recursion threatened to take flight,
But CodeRabbit's fix, both swift and bright,
Tamed the infinite loop's wild height,
And brought the contracts back to right! 🔧

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (7)
x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol (2)

7-11: Consider clarifying the mint amount or introducing a parameter.
Currently, the contract mints a fixed supply of 1,000,000 tokens (multiplied by 10^18) to the deployer. If the test scenario needs different initial supplies, consider making this parameter configurable.


34-40: Attacks rely on zero address.
Both attackBalance() and attackTransfer() use the zero address. This is likely designed to test malicious scenarios. Consider adding comments or requiring a configurable target address for broader test coverage.

x/evm/keeper/erc20.go (1)

29-35: Ensure the 63/64 gas rule is well-documented.
The getCallGasWithLimit function logically applies EIP-150, returning min((availableGas * 63) / 64, gasLimit). Consider expanding the comment to mention the rationale for this approach and possible side effects if future gas-related upgrades occur.

x/evm/embeds/embeds.go (2)

123-128: Document malicious recursion intent.
The contract name and comments indicate an infinite recursion test, but consider clarifying it further to avoid confusion for new maintainers.


143-143: Loading new contracts on init.
Automatically loading SmartContract_TestInfiniteRecursionERC20 on init is convenient for testing, but for a production environment, evaluate on-demand or carefully manage which contracts are loaded globally.

x/evm/keeper/funtoken_from_erc20_test.go (2)

391-407: Check for revert or fallback.
When deploying a contract with known malicious recursion, consider verifying if the constructor itself can trigger any recursion. Currently it’s safe, but for more rigorous testing, you might guard or log constructor calls.


428-441: Validate attackBalance outcome.
This test confirms no error is raised. If desired, a deeper check (like verifying state changes or confirming logs) might clarify that no partial recursion occurred.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3954394 and 2665603.

📒 Files selected for processing (7)
  • CHANGELOG.md (1 hunks)
  • x/evm/embeds/artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json (1 hunks)
  • x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol (1 hunks)
  • x/evm/embeds/embeds.go (3 hunks)
  • x/evm/embeds/embeds_test.go (1 hunks)
  • x/evm/keeper/erc20.go (7 hunks)
  • x/evm/keeper/funtoken_from_erc20_test.go (1 hunks)
👮 Files not reviewed due to content moderation or server errors (2)
  • x/evm/embeds/artifacts/contracts/TestInfiniteRecursionERC20.sol/TestInfiniteRecursionERC20.json
  • CHANGELOG.md
🔇 Additional comments (12)
x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol (1)

13-22: Use caution with unhandled staticcall return values.
This staticcall to FUNTOKEN_PRECOMPILE_ADDRESS discards returned data. In real scenarios, ignoring unsuccessful calls might introduce silent failures or mislead logic. Consider verifying the success and returned data for production contracts.

x/evm/keeper/erc20.go (6)

71-71: Appropriate usage of getCallGasWithLimit.
Replacing hardcoded gas logic with dynamic calculation mitigates potential recursion vulnerabilities.


93-93: Consistent approach for transfer.
Applying the same dynamic gas limit here ensures uniform handling across contract calls.


152-152: Maintain consistency for burn.
Using getCallGasWithLimit for burn keeps consistency in gas usage patterns.


185-185: Parameter usage for read-only calls.
Erc20GasLimitQuery is applied with getCallGasWithLimit for retrieving the ERC20 string. Ensure minimal overhead for read-only calls.


213-213: Same dynamic query gas limit for decimals.
Using the 63/64 rule prevents malicious decimals retrieval from causing unforeseen loops or gas exhaustion.


243-243: Apply safe approach for BalanceOf.
Adopting the dynamic limit for balance queries confirms no malicious recursion can bypass gas constraints.

x/evm/embeds/embeds_test.go (1)

22-22: Expanded test coverage.
Including SmartContract_TestInfiniteRecursionERC20.MustLoad() ensures the new contract is also verified during load tests, catching any early compile or embed issues.

x/evm/embeds/embeds.go (1)

40-41: Keep artifact paths in sync.
Ensure that the newly embedded file path matches any directory structure changes. Inconsistent naming or file moves can break the embed.

x/evm/keeper/funtoken_from_erc20_test.go (3)

388-390: Clear test documentation.
The docstring accurately describes the malicious recursion checks. Good clarity for future reference.


409-427: Verify creation fees are fully tested.
We see the fun token creation after soldiering funds. Ensure coverage for corner cases such as partially funded attempts.


442-453: Reverts as expected for malicious transfer.
This confirms the system halts a malicious infinite recursion. The expectation is that the EVM reverts. This behavior is correct for security.

x/evm/keeper/erc20.go Outdated Show resolved Hide resolved
Copy link

codecov bot commented Dec 28, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 64.99%. Comparing base (3954394) to head (60730b7).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2129      +/-   ##
==========================================
+ Coverage   64.93%   64.99%   +0.05%     
==========================================
  Files         273      273              
  Lines       21656    21661       +5     
==========================================
+ Hits        14063    14078      +15     
+ Misses       6617     6611       -6     
+ Partials      976      972       -4     
Files with missing lines Coverage Δ
x/evm/embeds/embeds.go 73.33% <100.00%> (+0.91%) ⬆️
x/evm/keeper/erc20.go 77.31% <100.00%> (+3.39%) ⬆️

... and 3 files with indirect coverage changes

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
x/evm/keeper/erc20.go (2)

30-36: Enhance technical accuracy of gas calculation

The gas calculation implementation can be made more technically accurate according to EIP-150.

Consider this alternative implementation that more closely follows the EIP-150 specification:

 func getCallGasWithLimit(ctx sdk.Context, gasLimit uint64) uint64 {
 	availableGas := ctx.GasMeter().GasRemaining()
-	callGas := availableGas - uint64(math.Floor(float64(availableGas)/64))
+	callGas := availableGas - (availableGas / 64)
 	return min(callGas, gasLimit)
 }

This change:

  • Removes unnecessary floating-point operations
  • Matches EIP-150's N - floor(N/64) formula more directly
  • Is more gas efficient

72-72: LGTM! Systematic protection against recursive calls

The consistent application of getCallGasWithLimit across all ERC20 operations effectively prevents infinite recursion by implementing EIP-150's gas limiting mechanism. This change systematically protects against potential recursive attacks in:

  • Mint operations
  • Transfer operations
  • Burn operations
  • Loading operations (name, symbol, decimals, etc.)

This implementation follows the defense-in-depth principle by applying gas limits at every contract interaction point, making it resilient against various forms of recursive attacks.

Also applies to: 94-94, 153-153, 186-186, 214-214, 244-244

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2665603 and 60730b7.

📒 Files selected for processing (1)
  • x/evm/keeper/erc20.go (8 hunks)
🧰 Additional context used
📓 Learnings (1)
📓 Common learnings
Learnt from: k-yang
PR: NibiruChain/nibiru#2129
File: x/evm/embeds/contracts/TestInfiniteRecursionERC20.sol:24-32
Timestamp: 2024-12-26T04:43:43.966Z
Learning: The `TestInfiniteRecursionERC20` contract is intentionally designed to test infinite recursion scenarios in ERC20-based contracts for the `FunToken` architecture.
🔇 Additional comments (1)
x/evm/keeper/erc20.go (1)

Line range hint 1-258: Verify complete coverage of gas limiting

Let's ensure all contract calls are protected with the new gas limiting mechanism.

✅ Verification successful

Let me gather more specific information about the unprotected calls.


Based on the analysis of the shell script results, I can now provide the final response:

All ERC20-related contract calls are properly protected with gas limiting mechanism

The verification shows that:

  1. All ERC20 operations in x/evm/keeper/erc20.go consistently use getCallGasWithLimit with appropriate gas limits:

    • Erc20GasLimitDeploy (2M) for deployment
    • Erc20GasLimitQuery (100K) for queries
    • Erc20GasLimitExecute (200K) for state-changing operations
  2. The gas limiting is properly implemented across all ERC20 operations:

    • Mint: Uses getCallGasWithLimit(ctx, Erc20GasLimitExecute)
    • Transfer: Uses getCallGasWithLimit(ctx, Erc20GasLimitExecute)
    • Burn: Uses getCallGasWithLimit(ctx, Erc20GasLimitExecute)
    • Query operations (name, symbol, decimals, balanceOf): Use getCallGasWithLimit(ctx, Erc20GasLimitQuery)

The unprotected CallContract usages found in the codebase are either in test files or in the base implementation itself, which is expected. The ERC20-specific operations are all properly protected against recursive calls through the EIP-150 gas limiting mechanism.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining unprotected contract calls

# Look for CallContract invocations that might not use getCallGasWithLimit
echo "Checking for potentially unprotected contract calls..."
rg "CallContract\(" --type go | while read -r line; do
  if ! rg -C 5 "getCallGasWithLimit" "${line%%:*}" > /dev/null; then
    echo "Potential unprotected call in: $line"
  fi
done

# Look for any other ERC20-related files that might need similar protection
echo "Checking for other ERC20-related files..."
fd -e go -e sol | rg -l "(?i)erc20"

Length of output: 10677


Script:

#!/bin/bash
# Check the context of CallContract usage in non-test files
echo "Checking CallContract usage in non-test files..."
rg "CallContract\(" --type go -g '!*test*' -C 10

echo -e "\nChecking if any other keeper methods directly use CallContract..."
rg "func.*Keeper.*CallContract" --type go -g '!*test*'

echo -e "\nChecking for gas limit usage in non-ERC20 contract calls..."
rg "GasLimit.*=|gas.*:" --type go -g '!*test*'

Length of output: 19339

@onikonychev onikonychev merged commit 1a256f2 into main Dec 28, 2024
16 checks passed
@onikonychev onikonychev deleted the fix/recursive-precompile-execution branch December 28, 2024 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants