- May 14, 2026
OpenZeppelin Security
OpenZeppelin Security
Security Audits
The PoC in this post was implemented for SBPF v0 and the relocation-related internals described in this post apply to SBPF v0/v1/v2 only. Relocation logic has been removed in SBPF v3.
Solana's syscall resolution mechanism contains a little-known behavior: programs can invoke syscalls using arbitrary names, as long as those names hash to the same Murmur3 value as a valid syscall. Here's what that means for security.
Syscalls (aka system calls) are fundamental building blocks in Solana's runtime environment that enable user-space programs to interact with the blockchain's core functionality. Understanding syscalls is crucial for Solana developers who want to build efficient and secure onchain applications.
What Are Syscalls in Solana?
In Solana, syscalls are predefined functions provided by the runtime that programs can invoke to perform essential operations. These low-level interfaces allow programs to:
- Interact with cross-program invocations (CPIs)
- Log messages
- Perform cryptographic operations (hashing, signature verification)
- Access blockchain state (e.g., current time) and transaction-related information (e.g., current stack height)
- Execute memory operations efficiently
Think of syscalls as the bridge between your program's logic and Solana's underlying infrastructure.
Currently, Solana provides over 30 syscalls, all following a consistent naming convention: they start with the sol_ prefix (such as sol_log_ or sol_get_return_data), with the exception of the abort syscall. Since syscalls are treated as external functions, they aren't defined within the programs themselves.
Syscalls in Action
To demonstrate how syscalls work, let's examine a simple "Hello, World!" program that uses the sol_log_ syscall to log a message:
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
};
extern "C" {
fn sol_log_(message: *const u8, length: u64);
}
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8]
) -> ProgramResult {
let message = "Hello, World!";
unsafe {
sol_log_(message.as_ptr(), message.len() as u64);
}
Ok(())
}
Note: This differs from the typical "Hello, World!" implementation that uses the msg! macro. However, the macro itself uses sol_log_ syscall internally. For demonstration purposes, we're calling the syscall directly.
Running this program produces the following output:
Logs: [
"Program iNpeDqVKmv1sSUeCDY4pLKRyJUqNRxpJMv3j7GsB2wD invoke [1]",
"Program log: Hello, World!",
"Program iNpeDqVKmv1sSUeCDY4pLKRyJUqNRxpJMv3j7GsB2wD consumed 211 of 200000 compute units",
"Program iNpeDqVKmv1sSUeCDY4pLKRyJUqNRxpJMv3j7GsB2wD success",
]
test test::test_sol_log ... ok
As expected, the second log entry contains our "Hello, World!" message. While we could use any other available syscall by adjusting the arguments accordingly, something interesting happens when we deviate from the expected naming convention.
Invalid Syscalls
What happens if we specify an invalid syscall name—one without the sol_ prefix and different from abort? Let's modify our program to use a seemingly random name txvtope instead of sol_log_, and change the message to "Hello, OZ!":
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
};
extern "C" {
fn txvtope(message: *const u8, length: u64);
}
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8]
) -> ProgramResult {
let message = "Hello, OZ!";
unsafe {
txvtope(message.as_ptr(), message.len() as u64);
}
Ok(())
}
Running this program yields a surprising result:
Logs: [
"Program ECR4wpVLACeR6gtM1hWXSnudeYKBZ6LAVY9JLBgtDVJM invoke [1]",
"Program log: Hello, OZ!",
"Program ECR4wpVLACeR6gtM1hWXSnudeYKBZ6LAVY9JLBgtDVJM consumed 211 of 200000 compute units",
"Program ECR4wpVLACeR6gtM1hWXSnudeYKBZ6LAVY9JLBgtDVJM success",
]
test test::test_txvtope ... ok
The program not only executed successfully, but also logged our modified message. Somehow, the Solana runtime recognized that txvtope should be treated as sol_log_. How did it know this? And why didn't it reject the transaction when attempting to call an unknown function?
To understand this behavior, we need to examine how syscalls are represented and handled internally within Solana.
Syscalls' Internals
User-space programs on Solana are represented as ELF (Executable and Linkable Format) files. Each file contains the program's code, data, and metadata describing how the program should be loaded.
Since syscalls aren't implemented in user-space programs, there must be a mechanism for programs to request them from the runtime. This is achieved through the CALL_IMM bytecode instruction, which normally encodes an internal function call by specifying a relative offset. However, if the specified offset equals a Murmur3 hash of an existing syscall name, the runtime interprets it as a syscall request.
When programs are compiled, the Solana runtime handles this process when loading a program in a transaction, before execution begins.
For each syscall, the compiler generates a CALL_IMM instruction with a placeholder value of -1 and adds a relocation entry to the ELF file. This entry specifies the offset of the relevant CALL_IMM target in the ELF file and the name of the syscall to be invoked.
During program loading, the Solana runtime parses each syscall-related relocation entry and replaces the -1 placeholder with the Murmur3 hash of the specified syscall name. If the calculated hash doesn't correspond to any known syscall hash, an error is reported and the transaction is reverted.
Hidden Syscalls
Here's the key insight: syscall validation isn't performed on the syscall's name itself, but on the Murmur3 hash of its name. This means it's possible to invoke any syscall using an arbitrary name, as long as that name produces the same Murmur3 hash as the target syscall name.
Revisiting our "Hello OZ!" example, we can now understand what happened: the txvtope string wasn't truly random—it happens to produce the same Murmur3 hash as sol_log_, making it an alternative name for the same syscall.
Implications
This mechanism has two noteworthy side effects.
First, consider the performance implications. For optimization, during program loading the runtime keeps a temporary syscall_cache. For each unresolved syscall reference, it looks up the cache by the symbol reference and only computes the Murmur3 hash on cache miss. Since syscall validity is checked against the resolved hash, multiple alias names that collide to the same hash can still trigger separate cache misses and increase load-time overhead.
Second, this mechanism could be used as an obfuscation technique, to hide the true behavior of the code. A malicious actor could declare syscalls using alternative names that produce matching Murmur3 hashes, making it harder for auditors and security researchers to understand what the program actually does. This makes static code analysis more difficult, as automated tools and human reviewers would need to calculate hash values to determine which syscalls are actually being invoked.
However, while this behavior is currently possible, there's no guarantee it will remain supported in future versions of the Solana runtime.
Conclusion
Our exploration of Solana's syscall resolution mechanism reveals an interesting quirk: syscalls are identified by hash values rather than their actual names. While this allows for alternative syscall names through hash collisions, it's a technical curiosity rather than a practical feature.
For developers, the takeaway is straightforward: stick to the standard syscall names. They're well-documented, widely recognized, and ensure your code remains maintainable and compatible with future runtime updates. Understanding these internals helps us write better programs, even if we never make use of these hidden capabilities.
Looking for a security partner?
FAQs
What is a syscall in Solana, and why does it matter for smart contract security?
A syscall (system call) in Solana is a runtime-provided function that onchain programs invoke to perform core operations — logging, cryptographic verification, cross-program invocations, and accessing blockchain state. Because syscalls serve as the interface between program logic and Solana's infrastructure, understanding how they are resolved is essential for security assessments. If a program can invoke syscalls using obfuscated names, its true behavior may not be apparent from source or bytecode inspection alone.
How does Solana resolve syscall names at program load time in SBPF v0, v1 and v2?
Solana resolves syscalls by computing the Murmur3 hash of a syscall's name at program load time, then matching it against a table of known syscall hashes. The compiler inserts a CALL_IMM instruction with a -1 placeholder and a relocation entry specifying the syscall name. When the program is loaded, the runtime replaces that placeholder with the Murmur3 hash of the name. If no known syscall matches that hash, the transaction is rejected. Critically, this means the name itself is never directly validated — only its hash is.
Can a Solana program call a syscall using a different name?
Yes. Because syscall resolution relies on Murmur3 hash matching rather than string comparison, any name that produces the same hash as a valid syscall name will successfully invoke that syscall. This is a hash collision, and it means that in particular, a program could declare txvtope as an external function and have it execute as sol_log_ at runtime.
What are the security risks of syscalls resolution process in Solana programs?
The syscalls resolution mechanism creates an obfuscation risk. A malicious actor could use hash-aliased syscall names to obscure a program's true behavior — making it more difficult for auditors, automated analysis tools, and security researchers to assess what the program actually does during a review. Standard static analysis tools that check for known syscall name strings in relocation entries of the program would fail to detect these aliases. Security assessments of Solana programs might need to include Murmur3 hash resolution as part of their methodology to reliably identify all syscalls being invoked.