Skip to content

feat: Add emergency-abort command to Solana CLI #6932

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

Aristide021
Copy link

@Aristide021 Aristide021 commented Jul 11, 2025

Summary

Adds a new emergency-abort command to the Solana CLI that allows developers to quickly shut down problematic upgradeable programs by replacing them with a minimal abort program.

Features

  • 24-byte emergency abort sBPF program that fails all transactions
  • Proper authority validation and safety warnings
  • Requires --bypass-warning flag to prevent accidental usage
  • Supports all standard program upgrade options

Usage

solana program emergency-abort <PROGRAM_ID> --bypass-warning

Implementation Details

  • Secure Assembly: Program compiled from .s source at build time using solana-sbpf::assembler::assemble()
  • No Fallback: Build fails if assembly cannot be compiled (no hardcoded data)
  • Transparent: All code visible in source files
  • Performs program upgrade to replace the target program
  • All transactions to the program will fail until it's upgraded with a fix
  • Includes comprehensive error checking and authority validation

Credits

The emergency abort program binary is based on the work from:

This implementation integrates the emergency abort functionality directly into the Solana CLI, making it easily accessible to developers who need to quickly disable problematic programs.

Testing

  • Compiles successfully
  • CLI help shows new command
  • Command-specific help displays correctly
  • Emergency abort program assembled from source at build time
  • Build fails appropriately if source cannot be compiled

- Add emergency-abort subcommand to replace upgradeable programs with abort program
- Includes 352-byte emergency abort program binary that fails all transactions
- Implements proper authority checks and safety warnings
- Supports all standard program upgrade options (fee-payer, compute-unit-price, etc.)
- Requires --bypass-warning flag to prevent accidental usage
- Creates temporary buffer and performs program upgrade to abort program
- Command: solana program emergency-abort <PROGRAM_ID> --bypass-warning
@mergify mergify bot requested a review from a team July 11, 2025 04:36
- Replace hardcoded 544-byte binary with build-time compilation
- Add emergency-abort program source files (assembly + Rust)
- Implement build.rs that compiles emergency abort program
- Include proper attribution to deanmlittle/sbpf-asm-abort
- Generate emergency abort binary during build process
- Fix incorrect size comment (544 bytes, not 352)
- Address reviewer feedback about embedding raw bytes in source

This approach satisfies the reviewer's requirements:
- ✅ No raw bytes in source code
- ✅ Host program source files
- ✅ Compile as build step
- ✅ Include bytes from built program
Address reviewer feedback by eliminating hardcoded binary data:

Security Improvements:
- Remove all hardcoded 544-byte binary fallbacks
- Implement build-time assembly compilation using solana-sbpf
- Generate optimal 24-byte binary from transparent .s source
- Fail-fast: build fails if assembly cannot be compiled from source

Implementation:
- Add cli/emergency-abort/src/sbpf-asm-abort.s with minimal assembly
- Add cli/build.rs to assemble using solana-sbpf at build time
- Add minimal build dependencies for assembly compilation
- Use solana-sbpf::assembler::assemble() like ledger-tool
- Verify executable with RequisiteVerifier
- Generate emergency_abort.rs with compiled binary at build time

Technical Details:
- Assembly: 'lddw r0, 1; exit' (24 bytes vs 544 bytes previously)
- No hardcoded binary data in source code (satisfies security requirements)
- Transparent source-to-binary compilation process
- Production-safe minimal Cargo.toml changes

Based on: https://github.com/deanmlittle/sbpf-asm-abort
Author: deanmlittle
@Aristide021
Copy link
Author

Updated the PR summary due to significant revisions. Please review the updated information above.

@deanmlittle
Copy link

deanmlittle commented Jul 11, 2025

Updated the PR summary due to significant revisions. Please review the updated information above.

Sorry to make you go back and forth, but I have deployed the program on mainnet: fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx

It is not upgradable. You can decompile the binary and prove that it does what it says it does. Imo the path of least resistance is to use program dump to grab this binary from mainnet then redeploy to your own program. That way we do not rely on any offchain source of truth and also do not have to build or bundle the binary :)

@Aristide021
Copy link
Author

Updated the PR summary due to significant revisions. Please review the updated information above.

Sorry to make you go back and forth, but I have deployed the program on mainnet: fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx

It is not upgradable. You can decompile the binary and prove that it does what it says it does. Imo the path of least resistance is to use program dump to grab this binary from mainnet then redeploy to your own program. That way we do not rely on any offchain source of truth and also do not have to build or bundle the binary :)

@deanmlittle Thanks for deploying this on mainnet! Using the immutable on-chain version as the source of truth makes sense to me as well.

@steviez My understanding of your original feedback was to "host the program source, compile it as a build step and then include the bytes from built program." The current implementation does exactly that.

Would you prefer we switch to deanmlittle's proposal of dumping the binary from the immutable mainnet program at fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx, or stick with the current build-from-source implementation?

@deanmlittle
Copy link

Updated the PR summary due to significant revisions. Please review the updated information above.

Sorry to make you go back and forth, but I have deployed the program on mainnet: fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx
It is not upgradable. You can decompile the binary and prove that it does what it says it does. Imo the path of least resistance is to use program dump to grab this binary from mainnet then redeploy to your own program. That way we do not rely on any offchain source of truth and also do not have to build or bundle the binary :)

@deanmlittle Thanks for deploying this on mainnet! Using the immutable on-chain version as the source of truth makes sense to me as well.

@steviez My understanding of your original feedback was to "host the program source, compile it as a build step and then include the bytes from built program." The current implementation does exactly that.

Would you prefer we switch to deanmlittle's proposal of dumping the binary from the immutable mainnet program at fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx, or stick with the current build-from-source implementation?

Although its kind of a shame because you already did the work to build it, imo, using the binary directly has much less chance of build issues and is way less verbose in terms of code. Anyone who is concerned about not being able to "verify the contents of the binary" is welcome to download ezbpf or solana-rbpf and disassemble it back into identical assembly code.

@Aristide021 Aristide021 requested a review from steviez July 11, 2025 22:16
@0xbrw
Copy link

0xbrw commented Jul 14, 2025

hi @Lichtso, would you also be able to take a look when you get a chance? I'm hearing from community devs this a very helpful feature.

@steviez
Copy link

steviez commented Jul 14, 2025

My understanding of your original feedback was to "host the program source, compile it as a build step and then include the bytes from built program." The current implementation does exactly that.

Correct, I had concerns about merging the program into our source as a byte array

Sorry to make you go back and forth

Similar sentiment from me - let's get agreement on a path forward to avoid any further churn on you

but I have deployed the program on mainnet ... Imo the path of least resistance is to use program dump to grab this binary from mainnet then redeploy to your own program. That way we do not rely on any offchain source of truth and also do not have to build or bundle the binary :)

This is not me telling you to go deploy on other clusters immediately, but what about testnet, devnet and/or any other private clusters ?

Would you prefer we switch to deanmlittle's proposal of dumping the binary from the immutable mainnet program at fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx, or stick with the current build-from-source implementation?

Let's wait for some more opinions from other Anza folks. Generally, I believe we'd have a preference for hosting the source + compiling. But, I'd agree with Dean that pasting the binary into the source simplifies some aspects (at the expense of the read).

Also, is it correct that we'd never expect this binary to change / need update ? Given Dean's comment about deploying "non-upgradeable", I assume this is the case but figured I'd double check

@Aristide021
Copy link
Author

My understanding of your original feedback was to "host the program source, compile it as a build step and then include the bytes from built program." The current implementation does exactly that.

Correct, I had concerns about merging the program into our source as a byte array

Sorry to make you go back and forth

Similar sentiment from me - let's get agreement on a path forward to avoid any further churn on you

but I have deployed the program on mainnet ... Imo the path of least resistance is to use program dump to grab this binary from mainnet then redeploy to your own program. That way we do not rely on any offchain source of truth and also do not have to build or bundle the binary :)

This is not me telling you to go deploy on other clusters immediately, but what about testnet, devnet and/or any other private clusters ?

Would you prefer we switch to deanmlittle's proposal of dumping the binary from the immutable mainnet program at fkv4NqZfGSfoAATFYGovrxaMHzeG69i525zEEpfUPyx, or stick with the current build-from-source implementation?

Let's wait for some more opinions from other Anza folks. Generally, I believe we'd have a preference for hosting the source + compiling. But, I'd agree with Dean that pasting the binary into the source simplifies some aspects (at the expense of the read).

Also, is it correct that we'd never expect this binary to change / need update ? Given Dean's comment about deploying "non-upgradeable", I assume this is the case but figured I'd double check

The abort program is intentionally designed to be extremely minimalistic. The only scenario in which this binary would ever need to change is if a flaw were discovered in its logic. However, given that it consists of just two instructions—one that loads the immediate value 1 into a register, and another that simply exits, the likelihood of such a flaw is improbably low. This is very much a "set it and forget it" command.

That said, I agree with finding a consensus on the implementation and am ready to modify the current implementation if needed.

@deanmlittle
Copy link

Also, is it correct that we'd never expect this binary to change / need update ? Given Dean's comment about deploying "non-upgradeable", I assume this is the case but figured I'd double check

Correct, we would not update or rebuild this binary ever. It is intentionally written in assembly with everything possible stripped from the binary to make it bytecode optimal. The idea of deploying it to mainnet as opposed to devnet or testnet is that mainnet is guaranteed to maintain state whereas the others could be reset. If we have a non-upgradable program on mb as a single source of truth, we can skip bundling it directly and lean on the existing dump command in the CLI. I think it's quite elegant?

@buffalojoec
Copy link

I'm in favor of this change, provided we clean a couple of things up. Here are my suggestions:

  • The command should not be called emergency-abort. You're deploying an emergency "abort" program under your program's ID, but the command itself isn't aborting anything. Just make it a tad more explicit: deploy-emergency-abort.
  • The workflow should:
    • Call solana program dump to dump the program ELF to somewhere like /tmp
    • Deploy the ELF to a buffer account
    • Execute the deployment
    • Clean up the fetched ELF
  • The command should accept a program ID parameter (ie. --abort-program-id), which would allow the user to provide an ID to a buffer or program of their choice. Dean's can be the default. Note: solana program dump works on both buffers and programs.

The idea of deploying it to mainnet as opposed to devnet or testnet is that mainnet is guaranteed to maintain state whereas the others could be reset.

Only mainnet is fine IMO. It also has an auditable transaction history demonstrating only aborts (if anyone cares to invoke it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants