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

Linter: Warn about strict balance equality #1914

Merged
merged 24 commits into from
Oct 25, 2023

Conversation

jubnzv
Copy link
Collaborator

@jubnzv jubnzv commented Sep 15, 2023

Summary

Closes #1811

  • [n] Does it introduce breaking changes?
  • [n] Is it dependant on the specific version of cargo-contract or pallet-contracts?

This PR adds the lint that warns about strict balance equality to the linting module.

Description

The lint is implemented as a forward dataflow problem using MIR dataflow framework. The goal of the analysis is to propagate values tainted with self.env().balance() throughout the MIR, handling all possible MIR statements along the way, including propagation through different kinds of references and across contract function calls. Then the tainted values are reported when they appear in conditions.

The implementation of the traits needed for MIR dataflow framework includes the definitions of the transfer function and the lattice that keeps state during the analysis. I created a struct TransferFunction that encapsulates all the logic related to visiting MIR, as it is done in the compiler itself and used BitSet<Local> as a lattice, which is powerset: inclusion means that the value is tainted with balance, exclusion that it is not.

MIR dataflow framework works only with bodies of single functions. Therefore, we have to implement interprocedural analysis to propagate tainted values across function calls. This analysis was included in the lint itself and includes a cache (see: VisitedFunctionsCache) that stores the final states after executing the analysis on functions defined in user code.

For more information about MIR dataflow, I found the source code of rustc to be useful, as it provides several examples of MIR dataflow. Additionally, I found the following thesis to be helpful because it offers a comprehensive description of the rustc internals involved in dataflow analysis.

Current status

It works on my tests. I'd like to additionally try it on the real-world smart contracts from the ecosystem to make sure I don't miss something.

There are a few cases that are not handled with this lint, therefore it will generate false negatives:

  • let env = self.env(); let balance = env.balance(); /* use balance */
  • Assignment of self.env().balance() to contract fields
  • Any unusual ways to compare integers. Only standard binary operations and PartialEq::{eq, ne} are supported.

I intentionally skipped these, since it seems not very realistic to appear in the production code. Please let me know, if there are any cases, including these, that should be included in the lint.

Checklist before requesting a review

  • My code follows the style guidelines of this project
  • I have added an entry to CHANGELOG.md
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • Any dependent changes have been merged and published in downstream modules

@SkymanOne
Copy link
Contributor

Can you please add a changelog entry?

@jubnzv
Copy link
Collaborator Author

jubnzv commented Sep 17, 2023

@SkymanOne Added.

I wasn't sure about this before because we didn't include any information about the linter in the previous release, and it doesn't contain any documentation yet.

Should we consider adding information about the available lints to the documentation and/or changelog before the next release?

@SkymanOne
Copy link
Contributor

It is still important to include the entry in the changelog for traceability. Lints are already enabled by default in cargo contract, and the documentation will follow

jubnzv added a commit to jubnzv/ink that referenced this pull request Sep 27, 2023
It reuses functions implemented in use-ink#1914, so we need to rebase and
modify it when it is merged.
jubnzv added a commit that referenced this pull request Oct 5, 2023
* feat(lint+test): add skeleton of storage_never_freed (#1431)

* feat(lint): add utils and logic to collect `Vec`/`Mapping` types

It reuses functions implemented in #1914, so we need to rebase and
modify it when it is merged.

* feat(lint): Make it pass simple tests

* feat(lint): Support index operations (`vec[]`)

* feat(lint): Ignore fields that has mutable unsafe pointers

* chore(lint): Refactor

* feat(test): Inserting in method arg

* chore(test): Refactor

* feat(test): Insert inside insert

* chore(lint): Refactor

* feat(lint): Support local type aliases

* chore(lint): More accurate warning text

* chore(lint): Refactor

* feat(lint): Additional check for `self`

* chore(lint+tests): Refactor; update tests output

* chore: Update changelog

* chore: fmt
@jubnzv
Copy link
Collaborator Author

jubnzv commented Oct 5, 2023

@SkymanOne Thanks for the review.
Could you please take another look? I just updated it according to changes in master.

@cmichi
Copy link
Collaborator

cmichi commented Oct 25, 2023

@jubnzv Regarding false positives:

  • let env = self.env(); let balance = env.balance(); /* use balance */
  • Assignment of self.env().balance() to contract fields

How does the error message look if those are used?

Do you see any way of getting rid of those false positives?

@jubnzv
Copy link
Collaborator Author

jubnzv commented Oct 25, 2023

@cmichi These cases are false negatives. It means the linter doesn't raise any warnings on them, but it should ideally. I intentionally chose to skip them to simplify and optimize the implementation, as these cases probably won't occur in real production smart contracts.

Copy link
Collaborator

@cmichi cmichi left a comment

Choose a reason for hiding this comment

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

Thank you, @jubnzv! Just two nitpicks.

linting/src/strict_balance_equality.rs Show resolved Hide resolved
linting/src/strict_balance_equality.rs Outdated Show resolved Hide resolved
@paritytech-cicd-pr
Copy link

🦑 📈 ink! Example Contracts ‒ Changes Report 📉 🦑

These are the results when building the integration-tests/* contracts from this branch with cargo-contract 4.0.0-alpha-acc02b1 and comparing them to ink! master:

Contract Upstream Size (kB) PR Size (kB) Diff (kB) Diff (%) Change
basic-contract-caller 2.998 2.998 0 0
basic-contract-caller/other-contract 1.338 1.338 0 0
call-builder-return-value 8.74 8.74 0 0
call-runtime 1.774 1.774 0 0
conditional-compilation 1.21 1.21 0 0
contract-storage 7.142 7.142 0 0
contract-terminate 1.092 1.092 0 0
contract-transfer 1.449 1.449 0 0
custom-allocator 7.429 7.429 0 0
dns 7.313 7.313 0 0
e2e-call-runtime 1.062 1.062 0 0
e2e-runtime-only-backend 1.64 1.64 0 0
erc1155 14.119 14.125 0.006 0.0424959 📈
erc20 6.729 6.729 0 0
erc721 9.716 9.716 0 0
events 4.823 4.823 0 0
flipper 1.394 1.394 0 0
incrementer 1.223 1.223 0 0
lang-err-integration-tests/call-builder-delegate 2.327 2.327 0 0
lang-err-integration-tests/call-builder 4.88 4.88 0 0
lang-err-integration-tests/constructors-return-value 1.783 1.783 0 0
lang-err-integration-tests/contract-ref 4.363 4.363 0 0
lang-err-integration-tests/integration-flipper 1.572 1.572 0 0
mapping-integration-tests 3.216 3.216 0 0
mother 9.555 9.555 0 0
multi-contract-caller 5.993 5.993 0 0
multi-contract-caller/accumulator 1.096 1.096 0 0
multi-contract-caller/adder 1.672 1.672 0 0
multi-contract-caller/subber 1.693 1.693 0 0
multisig 21.628 21.628 0 0
payment-channel 5.559 5.559 0 0
sr25519-verification 0.87 0.87 0 0
static-buffer 1.418 1.418 0 0
trait-dyn-cross-contract-calls 2.491 2.491 0 0
trait-dyn-cross-contract-calls/contracts/incrementer 1.306 1.306 0 0
trait-erc20 7.113 7.113 0 0
trait-flipper 1.21 1.21 0 0
trait-incrementer 1.371 1.371 0 0
upgradeable-contracts/delegator 2.914 2.914 0 0
upgradeable-contracts/delegator/delegatee 1.374 1.374 0 0
upgradeable-contracts/set-code-hash 1.466 1.466 0 0
upgradeable-contracts/set-code-hash/updated-incrementer 1.445 1.445 0 0
wildcard-selector 2.625 2.625 0 0

Link to the run | Last update: Wed Oct 25 15:19:00 CEST 2023

@cmichi cmichi merged commit 9cf5b3d into use-ink:master Oct 25, 2023
23 checks passed
@jubnzv
Copy link
Collaborator Author

jubnzv commented Oct 25, 2023

Thanks for the review @cmichi

@SkymanOne SkymanOne mentioned this pull request Nov 30, 2023
@SkymanOne SkymanOne mentioned this pull request Mar 4, 2024
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.

Linter: Warn when conditional behavior depends on contract's balance
4 participants