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

Deprecate account meta executable read/update in bpf loaders #34194

Merged
merged 2 commits into from
Jan 3, 2024

Conversation

HaoranYi
Copy link
Contributor

@HaoranYi HaoranYi commented Nov 21, 2023

Problem

Part of effort for #33970

We are deprecating executable meta flags on account. Instead of relying on executable flag on account to determine whether the account is executable, we are going to check the owner of the account and the loader's meta data in account data to determine whether the account is executable.

Summary of Changes

  • use owner + metadata in accounts data to determine whether the account is executable in transaction loading/processing.
  • add disable_bpf_loader_instructions feature to disable bpf loader management instructions,
  • add deprecate_executable_meta_update_in_bpf_loader feature to deprecate executable meta update in bpf loader

Feature Gate Issues:

SIMD

Fixes #

@HaoranYi HaoranYi changed the title nov 21 exec flag2 use PROGRAM_OWNER to check executable on account when loading transaction accounts Nov 21, 2023
@Lichtso
Copy link
Contributor

Lichtso commented Nov 21, 2023

It just occurred to me that there is one more complicating factor here, the migration of built-in programs to on-chain programs: solana-foundation/solana-improvement-documents#88

So, in the end almost all programs (except for the loaders) will be on-chain and be owned by the loaders. But until then we still have to support built-in programs which also count as "executable" sometimes, but not always. E.g. built-ins don't need to be loaded into the LoadedPrograms cache.

Thus, we might need a boolean parameter to the is_executable() function replacement or two versions:

  • One for user programs only: Which returns true if the owner is in PROGRAM_OWNERS
  • One for built-ins: Which also returns true if the owner is native_loader::id()

Related code sites:

@HaoranYi HaoranYi force-pushed the nov_21_exec_flag2 branch 3 times, most recently from 1194285 to 462fb24 Compare November 21, 2023 22:37
loaded_accounts[0],
(Err(TransactionError::InvalidProgramForExecution), None,)
);
assert!(loaded_accounts[0].0.is_ok());
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be an un-feature-gated change to consensus.

Copy link
Contributor Author

@HaoranYi HaoranYi Nov 22, 2023

Choose a reason for hiding this comment

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

yeah. this needs to be feature-gated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, I would prefer if we would find a way to do this refactoring transparently, without consensus changes.

Copy link
Contributor Author

@HaoranYi HaoranYi Nov 22, 2023

Choose a reason for hiding this comment

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

ok. let me think about it.

Another question:

https://github.com/solana-labs/solana/blob/master/sdk/src/transaction_context.rs#L1082

With the change of is_executable() on BorrowedAccount, this seems not correct any more? upgradable account can_data_be_changed?

@@ -1079,7 +1081,9 @@ impl<'a> BorrowedAccount<'a> {
     #[cfg(not(target_os = "solana"))]
     pub fn can_data_be_changed(&self) -> Result<(), InstructionError> {
         // Only non-executable accounts data can be changed
-        if self.is_executable() {
+        if self.is_executable()
+            && !solana_program::bpf_loader_upgradeable::check_id(self.account.owner())
+        {
             return Err(InstructionError::ExecutableDataModified);
         }

@HaoranYi
Copy link
Contributor Author

The following are the behavior changes with this PR found in test

  1. if program owner changes to non bpf-loader, while its executable flags are
    set. IncorrectProgramId --> AccountNotExecutable

  2. if program data is unitialized, while its executable flags are
    set. InvalidAccountData --> AccountNotExecutable

  3. for account, whose owner is bpf_loader, while its executable flags are set.
    running LoaderInstruction::write, Ok --> ExecutableDataModified

@Lichtso Can we review them to be sure that these scenarios are not possible practically?

@Lichtso
Copy link
Contributor

Lichtso commented Nov 22, 2023

  1. Such a test should be deleted because transferring the ownership of an executable account is explicitly forbidden:

    if self.is_executable() {

  2. Again, makes no sense. Once an account is marked as executable it becomes immutable (

    if self.is_executable() {
    ), so it could never leave the uninitialized state. Thus, the executable flag is only set after the state is initialized.

  3. Uff, this will actually be a problem. The first two loaders did not write any metadata into the program account, so the only way to distinguish the writing-in-progress from the finalized state is the executable flag. Obviously, that can not be emulated without the executable flag so we do need a feature gate here. Also, we will probably have to disable deployments for everything but the upgradeable loader (v3), which was planned anyways.

@HaoranYi
Copy link
Contributor Author

HaoranYi commented Nov 22, 2023 via email

@HaoranYi
Copy link
Contributor Author

I have tried to add a feature gate for executable flag. But it turns out that
leads to quite a few API changesfor Account and BorrowedAccount, which is
not very clean. I think we may want to avoid adding a feature flag. Instead,
can we partially disable usage of executable flag for the loaders? i.e. for bpf_loader_upgradeable
and loader_v4, we disable the usage of executable flag, while for
'bpf_loader' and 'bpf_loader_deprecated', we still use executable flags. When
we disable deployments for these two loaders, we can remove the code that use
executable flags for them? Wdyt? @Lichtso

Copy link

codecov bot commented Nov 28, 2023

Codecov Report

Attention: 43 lines in your changes are missing coverage. Please review.

Comparison is base (d6146af) 81.8% compared to head (d59193c) 81.8%.
Report is 10 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff            @@
##           master   #34194    +/-   ##
========================================
  Coverage    81.8%    81.8%            
========================================
  Files         824      824            
  Lines      222262   222393   +131     
========================================
+ Hits       181860   182065   +205     
+ Misses      40402    40328    -74     

@HaoranYi
Copy link
Contributor Author

https://explorer.solana.com/tx/4UzC7fuDiB5spFJmb1tBd6b44zvMYNsbHNBzFnawW5PM1z9ZzmfMpAagvbjuMxSPUHuhHqbKzWzgxrtYDezL18mA

Fails because BPFUpgradableLoader was hijacked by fn account_shared_data_from_program, which creates a dummy account with empty data. That's why it fails with this PR.

aaf2748 fixed it.

I have deploy this PR again on my node again to run against mnt overnight.

@HaoranYi HaoranYi force-pushed the nov_21_exec_flag2 branch 5 times, most recently from df9d94c to 954848a Compare November 30, 2023 23:25
runtime/src/accounts/mod.rs Outdated Show resolved Hide resolved
runtime/src/accounts/mod.rs Outdated Show resolved Hide resolved
sdk/src/transaction_context.rs Outdated Show resolved Hide resolved
programs/bpf_loader/src/lib.rs Outdated Show resolved Hide resolved
programs/bpf_loader/src/lib.rs Outdated Show resolved Hide resolved
@HaoranYi HaoranYi changed the title use PROGRAM_OWNER to check executable on account when loading transaction accounts use owner+meta_data to check executable on account when loading transaction accounts Dec 1, 2023
sdk/src/account.rs Outdated Show resolved Hide resolved
@HaoranYi
Copy link
Contributor Author

HaoranYi commented Dec 4, 2023

FYI. A node with this change has been running for the past 5 days against mainnet! So far, it looks good!

@HaoranYi HaoranYi changed the title use owner+meta_data to check executable on account when loading transaction accounts Deprecate account meta executable read/update in bpf loaders Dec 12, 2023
@HaoranYi
Copy link
Contributor Author

HaoranYi commented Dec 15, 2023

I added two featuregates in this PR:
a) disable bpfloader v1 deployment instructions
b) skip updating executable during bpf program deployment.

Aso, I created two SIMDs for these two features.

@Lichtso @pgarg66 @brooksprumo Please take a look. Let me know if you have any more comments. Thanks!

@Lichtso
Copy link
Contributor

Lichtso commented Dec 18, 2023

It seems that deprecate_executable_meta_update_in_bpf_loader only gates one call site of set_executable(), not all of them. Also, the switch from using the account executable flag to the inferred one in is_executable() needs to be feature gated (can be done inside is_executable() with deprecate_executable_meta_update_in_bpf_loader) because the deprecated loader can not be emulated (thus we need to disable its deployment with disable_bpf_loader_instructions beforehand).

// the account. Once we disable the deployment of these two loaders, we can
// remove the following dependency on `executable`.
if bpf_loader::check_id(account.owner()) || bpf_loader_deprecated::check_id(account.owner()) {
return account.executable();
Copy link
Contributor

Choose a reason for hiding this comment

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

return true;
That will be safe once deployments on loader-v2 are disabled. And we don't switch to this emulation here before that feature is activated.

@HaoranYi HaoranYi force-pushed the nov_21_exec_flag2 branch 2 times, most recently from 8872bf2 to 8272151 Compare December 19, 2023 15:25
@Lichtso
Copy link
Contributor

Lichtso commented Dec 20, 2023

@HaoranYi Can you split off the changes where you wire in feature_set in all the methods of BorrowedAccount into a separate PR? Otherwise I would say this looks good, can you run it on a validator against MNB?

@HaoranYi
Copy link
Contributor Author

@HaoranYi Can you split off the changes where you wire in feature_set in all the methods of BorrowedAccount into a separate PR? Otherwise I would say this looks good, can you run it on a validator against MNB?

Yeah. #34542 is the refactor PR that pass feature_set into BorrowedAccount.
I just started a validator on my node, will keep it running.

@HaoranYi
Copy link
Contributor Author

HaoranYi commented Jan 2, 2024

@HaoranYi Can you split off the changes where you wire in feature_set in all the methods of BorrowedAccount into a separate PR? Otherwise I would say this looks good, can you run it on a validator against MNB?

Yeah. #34542 is the refactor PR that pass feature_set into BorrowedAccount. I just started a validator on my node, will keep it running.

My node has been running fine with the PR over the holidays.

@@ -1818,7 +1848,7 @@ mod tests {
Err(InstructionError::NotEnoughAccountKeys),
);

Copy link
Contributor

Choose a reason for hiding this comment

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

For all the tests of loader-v2 it would be preferable to just force deactivate the feature instead of altering the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah. restored.

Lichtso
Lichtso previously approved these changes Jan 3, 2024
Copy link
Contributor

@Lichtso Lichtso left a comment

Choose a reason for hiding this comment

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

After loader-v2 is disabled its management instructions will be removed. Thus, we need to rework the tests in programs/sbf/tests/programs.rs to use loader-v3 instead. However, that can happen in a separate PR.

HaoranYi added 2 commits January 3, 2024 18:06
mock account data with executable_meta in precompiled program and update
test_bank_hash_consistency test

pr: return const slice and add comments

pr: use ReadableAccount

use const to get rid of magic number

add featuregate disable_bpf_loader_instructions to disable bpf loader management instructions, and deprecate_executable_meta_update_in_bpf_loader to deprecate executable flag update in bpf loader

deprecate usage of executable in Account

fix a test

fix sbp bench

fix sbf program tests

add feature gate to account and borrowed account apis

fix tests

more test fixes
@HaoranYi HaoranYi merged commit 5a3a10e into solana-labs:master Jan 3, 2024
45 checks passed
@HaoranYi HaoranYi deleted the nov_21_exec_flag2 branch January 3, 2024 21:12
@t-nelson
Copy link
Contributor

t-nelson commented Feb 5, 2024

please DO NOT introduce multiple feature gates in the same pull request!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-gate Pull Request adds or modifies a runtime feature gate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants