Skip to content

SIMD-0163: Lift the CPI caller restriction#163

Merged
joncinque merged 2 commits into
solana-foundation:mainfrom
Lichtso:lift-cpi-caller-restriction
Nov 25, 2024
Merged

SIMD-0163: Lift the CPI caller restriction#163
joncinque merged 2 commits into
solana-foundation:mainfrom
Lichtso:lift-cpi-caller-restriction

Conversation

@Lichtso
Copy link
Copy Markdown
Contributor

@Lichtso Lichtso commented Aug 1, 2024

No description provided.

Comment thread proposals/0163-lift-cpi-caller-restriction.md
Copy link
Copy Markdown
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

This is a great change! Eliminating the requirement for programs to contain the callee program account - in the cases of loaders v2 and v4 - will be a drastic improvement on CUs.

Comment thread proposals/0163-lift-cpi-caller-restriction.md
Comment thread proposals/0163-lift-cpi-caller-restriction.md
@Lichtso Lichtso force-pushed the lift-cpi-caller-restriction branch from f5abfcf to 55578ff Compare August 15, 2024 12:25
Comment thread proposals/0163-lift-cpi-caller-restriction.md
@cavemanloverboy
Copy link
Copy Markdown

cavemanloverboy commented Aug 21, 2024

Can you clarify why this is a massive improvement in CU cost? It seems to me like it's more of a general improvement to VM performance and just removes the need to construct one or a few more AccountInfos. Programs don't ever copy the program data.

As an extreme example, a noop program

pub extern "C" fn entrypoint(input: *mut u8) -> u64 {
    0
}

would see no reduction in CUs from being invoked with one less 10 MB account

@Lichtso
Copy link
Copy Markdown
Contributor Author

Lichtso commented Aug 21, 2024

@cavemanloverboy For top-level instructions this is in deed irrelevant. But, not so for nested CPI.

In nested CPI the program runtime does copy the program data (except for those owned by loader-v3) of instruction accounts, and taxes the caller for it here: https://github.com/anza-xyz/agave/blob/77b4d131502e095d18f3df7caab8b6a1cf7c1887/programs/bpf_loader/src/syscalls/cpi.rs#L886

Copy link
Copy Markdown
Contributor

@jstarry jstarry left a comment

Choose a reason for hiding this comment

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

I really like this SIMD but I'm wondering if we should also update the tx format to make it explicit which programs may be invoked during tx execution. Rather than just appending program ids to the readonly accounts in a transaction, we could have a new message header value which defines how many of the trailing readonly accounts are invokable and should therefore be loaded into the program cache before execution.

@Lichtso
Copy link
Copy Markdown
Contributor Author

Lichtso commented Nov 6, 2024

we could have a new message header value which defines how many of the trailing readonly accounts are invokable

Good idea, however outside of this SIMD. This is only about CPI, it does not affect the message accounts.

@cavemanloverboy
Copy link
Copy Markdown

cavemanloverboy commented Nov 6, 2024

I agree. New transaction format needs to be thought through in great detail alongside #172.

I will draft something up in the next few weeks.

@2501babe
Copy link
Copy Markdown
Contributor

should we specify that non-instruction non-payer accounts must be read-only and enforce this in sanitize? or specify that writable non-instruction non-payer accounts are demoted to read-only?

it wouldnt really affect lock contention because the only reason to append a writable account is to be annoying, and they could be just as annoying by taking it as an instruction account, but it would mean we dont need to check if a non-instruction account was invoked to skip it during account saving

@Lichtso
Copy link
Copy Markdown
Contributor Author

Lichtso commented Nov 14, 2024

I don't think this SIMD is that much related to account loading. It needs all accounts containing programs to be loaded in the program cache, that's it. And that happens anyway, independent of writeability and later account saving.

@joncinque
Copy link
Copy Markdown
Contributor

It looks like there's consensus here. If no one objects within the next week, I'll merge this on 25 November.

@Nicola-Osec
Copy link
Copy Markdown

We think there is a risk that some programs rely on inspecting instructions to determine whether other instructions might make a CPI call to a particular program.
For example, right now is it possible to use the instructions sysvar to inspect other Instructions executing in the same tx, and you can guarantee that an instruction doesn't make a CPI call to a program if that program is not listed on the Instruction. Lifting this restriction (i.e. allowing instructions to call into programs not listed as instruction accounts) would make such a check insufficient.

@Lichtso Lichtso deleted the lift-cpi-caller-restriction branch June 10, 2025 15:02
@febo
Copy link
Copy Markdown
Contributor

febo commented Jun 13, 2025

We think there is a risk that some programs rely on inspecting instructions to determine whether other instructions might make a CPI call to a particular program. For example, right now is it possible to use the instructions sysvar to inspect other Instructions executing in the same tx, and you can guarantee that an instruction doesn't make a CPI call to a program if that program is not listed on the Instruction. Lifting this restriction (i.e. allowing instructions to call into programs not listed as instruction accounts) would make such a check insufficient.

Related to this, the changes proposed in this SIMD expands the behaviour of what instructions can access in unpredictable ways, since now all instructions have access to program accounts even if they are not explicitly listed. It will be practically impossible for programs to make sure third-party programs they CPI into are not going to do something that they should not. It would also make transaction building a bit complicated, since an instruction might not list a program account as required, but only succeed if a particular program is in the transaction.

The model that we have today provides a way to restrict what programs can access by requiring program accounts to be explicitly listed – this behaviour most likely should be maintained.

@joncinque
Copy link
Copy Markdown
Contributor

I had thought that this feature only restricts at the level of top-level instruction, meaning that each program in a top-level instruction could CPI into other programs declared within the top-level instruction, but I was told this isn't the case.

While I think it's OK to lift the CPI caller restriction at the level of each top-level instruction, if we lift it at the transaction level, there are some dangerous possibilities, as Febo mentioned.

Currently, it's possible to see dangers by looking at top-level instructions. For example, if you see an instruction take in the system program and your wallet as a signer, then you know that program could drain your wallet.

With this change, however, if you provide your wallet as a signer to any top-level instruction, then that top-level instruction could rug you if the system program happens to be elsewhere in the same transaction. It becomes safer to send multiple transactions rather than put everything in one transaction, which is not behavior we should encourage.

Copy link
Copy Markdown
Contributor

@buffalojoec buffalojoec left a comment

Choose a reason for hiding this comment

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

EDIT: I didn't read the latest comments before commenting. My bad! Anyway, here's the same point articulated again.


We were discussing offline about some potential concerns with this SIMD. I had previously cast my approval, but I believe the following concerns should be explored before this SIMD is implemented, allowing for any revisions to the proposal and its design.

A few other SIMDs have requested more information from what we call in Agave the "transaction context". An example is SIMD-0258, which requests access to the signatures. Furthermore, instruction introspection via the Instructions sysvar can also give a program insight into the entire list of accounts involved in a transaction.

If on-chain programs can - now or eventually - access the entire list of transaction accounts, then they can behave maliciously as a result of this SIMD.

Consider the following scenario:

  • A user crafts a transaction where the first instruction is a System program transfer from their wallet to some destination.
  • They include a second transaction to interact with some protocol.
  • Even though they did not place the System program in the list of account metas for the second instruction, the protocol can CPI into the System program and drain the user's wallet.

One could argue that there are plenty of exploits like these that are already possible, simply due to the inherent nature of untrusted third-party permissionless software. Regardless, it's worth understanding what kind of footguns this SIMD can create, even if we retain that users should always only interact with trusted protocols.

At a minimum, I request this concern be added to the Impact section.

cc @febo @joncinque

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jan 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants