gr-satellites is an open-source GNU Radio out-of-tree module designed for decoding telemetry from a wide range of satellite protocols. It is widely used by researchers, hobbyists, and professionals to process live RF signals and convert raw satellite transmissions into structured telemetry data.
A recent security assessment of `gr-satellites v5.9.0` identified a security issue in the processing of malformed inputs by two blocks in the gr-satellites module of GNU Radio. Using a specially crafted payload can lead to an integer overflow and crash the flowgraph, resulting in a denial of service in the communication pipeline.
Key Findings
The assessment identified a vulnerability in the telemetry forwarding mechanism, where untrusted input received over RF is passed directly to Python execution contexts:
- Local Code Execution via malicious telemetry frames
- Code execution triggered automatically during normal decoding workflows
- Exploitation possible through over-the-air transmissions
- No authentication, validation, or sanitization of telemetry payloads
This issue maps to CWE-94: Improper Control of Generation of Code (Code Injection) and represents a rare but impactful class of over-the-air attack vectors.
1. Vulnerability Assessment – Findings
1.1 Memory corruption in varlen_packet_framer (CWE: 131, CWE: 190)
1.1.1 Introduction
A memory safety vulnerability exists in the varlen_packet_framer block of the gr-satellites GNU Radio module. The block fails to validate the value of the given tag before using it in a memcpy() operation (shown below). When a crafted tag with a large value (e.g., 0xFFFFFFFF) is injected into the input stream, the block calculates a negative size for memcpy(), resulting in undefined behavior and a crash (Figure 2). Below is a section of the vulnerable code:
# varlen_packet_framer_impl.cc
int varlen_packet_framer_impl::general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items,
gr_vector_void_star& output_items) {
const unsigned char* in = (const unsigned char*)input_items[0];
unsigned char* out = (unsigned char*)output_items[0];
int packet_len = 0;
std::vector<tag_t> tags;
uint32_t header = 0;
unsigned int asm_len = d_sync_word.size();
. . .
if (ninput_items[0] >= d_ninput_items_required) {
// copy the option sync word
if (asm_len > 0) {
memcpy(out, (const void*)&d_sync_word[0], asm_len);
out += asm_len;
}
// create the length field header
header = packet_len / 8;
if (d_use_golay) {
encode_golay24(&header);
}
// copy header
if (d_endianness == GR_MSB_FIRST) {
for (int ii = 0; ii < d_header_length; ii++) {
out[ii] = (header >> (d_header_length - ii - 1)) & 0x01;
}
} else {
for (int ii = 0; ii < d_header_length; ii++) {
out[ii] = (header >> ii) & 0x01;
}
}
// copy data and tag
memcpy(&out[d_header_length], in, packet_len);
add_item_tag(0,
nitems_written(0),
d_packet_tag,
pmt::from_long(packet_len + d_header_length + asm_len),
alias_pmt());
consume_each(packet_len);
This vulnerability can be triggered using a headless GNU Radio flowgraph (Figure 1) implemented in Python or C++. Depending on the memory layout and compiler behavior, it may lead to a denial of service (DoS) or memory corruption.

Figure 1: Example GNU Radio flowgraph to demonstrate the vulnerability

Figure 2: Crash due to memcpy() receiving a negative input

Figure 3: gdb output showing the segmentation fault

Figure 4: GRC GUI program exits due to segmentation fault
1.1.2 Steps To Reproduce
- Create the following flowgraph in the GRC application:
Python Block – Variable Length Packet Framer – Null Sink
- Insert the python code found in Append 1.1.2 into the Python Block.
- Set the options in the Variable Length Packet Framer block to the following:

- Press the Play button at the top of the screen and observe the program exit as shown in Figure 4.
Alternatively, run the python file generated by the GRC GUI directly in the terminal with Valgrind or gdb to see the details of the segmentation fault shown in Figures 2 and 3.
1.1.3 Summary
Vulnerability Type:
- CWE-131: Incorrect Calculation of Buffer Size
- CWE-190: Integer Overflow or Wraparound
Attack type: Direct Library Invocation
Impact: Exploitation may lead to denial of service (DoS) or memory corruption.
Affected components: varlen_packet_framer_impl::general_work()
1.1.4 Recommendations
Validate the tag value before using it in memory operations:
- Ensure it is within a safe, expected range (e.g., 0 < example_tag <= MAX_PACKET_SIZE)
- Reject or drop packets with unreasonable lengths
1.2 Memory corruption in varlen_packet_tagger (CWE: 131, CWE: 190)
1.2.1 Introduction
A memory safety vulnerability exists in the varlen_packet_tagger block of the gr-satellites GNU Radio module. The block decodes a variable-length packet size from a bitstream header without validating the result. If a crafted sync tag followed by a header decodes to a large value, the block calculates a packet_len that overflows the int type. This results in a negative size being passed to memcpy() (shown below), causing a crash or potential memory corruption (Figure 5).
It is worth noting that the length required to overflow the integer variable is larger than would typically be used in normal communications. The section of vulnerable code is below:
# varlen_packet_tagger_impl.cc
int varlen_packet_tagger_impl::general_work(int noutput_items, gr_vector_int& ninput_items, gr_vector_const_void_star& input_items, gr_vector_void_star& output_items)
{
const unsigned char* in = (const unsigned char*)input_items[0];
unsigned char* out = (unsigned char*)output_items[0];
int packet_len = 0;
std::vector<tag_t> tags;
uint32_t golay_field;
int golay_res = 0;
. . .
if (d_use_golay) {
d_debug_logger->debug("Header: {#06x}, Len: {:d}",
0xFFFFFF & golay_field,
0xFFF & packet_len);
if (golay_res >= 0) {
d_debug_logger->debug("Golay decoded. Errors: {:d}, Length: {:d}",
golay_res,
packet_len);
}
}
memcpy(out, &in[d_header_length], packet_len);
add_item_tag(0,
nitems_written(0),
d_packet_tag,
pmt::from_long(packet_len),
alias_pmt());
d_have_sync = false;
// consuming only the header allows for
// ... multiple syncs per 'packet',
// ... in case the sync was incorrectly tagged
consume_each(d_header_length);
d_ninput_items_required = d_header_length + 1;
return packet_len;
}
This vulnerability can be triggered via a GNU Radio flowgraph (Figure 4) implemented in Python or C++ (see Appendix B). Depending on the memory layout and compiler behavior, it may lead to a denial of service (DoS) or heap corruption.

Figure 5: GNU Radio flowgraph to demonstrate the vulnerability

Figure 6: SEGV observed in the memmove (memcpy) call

Figure 7: gdb output showing segmentation fault
Figures 6 and 7 show the terminal output of the underlying segmentation fault that occurs after the invalid memory operation, while Figure 8 shows the program exiting with return code 3221225477 in the GRC GUI application.

Figure 8: GRC GUI program exits due to segmentation fault
1.2.2 Steps To Reproduce
- Create the following flowgraph in the GRC application:
Python Block – Variable Length Packet Tagger – Null Sink
- Insert the python code found in Appendix 1.2.2 into the Python Block.
- Set the options in the Variable Length Packet Tagger block to the following:

- Press the Play button at the top of the screen and observe the program exit as shown in Figure 8.
Alternatively, run the python file generated by the GRC GUI directly in the terminal with Valgrind or gdb to see the details of the segmentation fault shown in Figures 6 and 7.
1.2.3 Summary
Vulnerability Type:
- CWE-131: Incorrect Calculation of Buffer Size
- CWE-190: Integer Overflow or Wraparound
Attack type: GRC GUI Flowgraph, Direct Library Invocation
Impact: Exploitation may lead to denial of service (DoS) or heap corruption.
Affected components: varlen_packet_tagger_impl::general_work()
1.2.4 Recommendations
- Consider using size_t instead of int for packet_len to avoid signed overflow.
- Validate the decoded packet_len before using it:
if (packet_len <= 0 || packet_len > d_mtu) {
d_have_sync = false;
consume_each(1);
return 0;
}
Conclusion
The vulnerability identified in gr-satellites demonstrates how trust boundaries can blur when RF-derived data is implicitly trusted. While gr-satellites does not contain classic memory-safety flaws, its current handling of decoded telemetry allows attacker-controlled RF input to reach Python execution paths.
This assessment does not aim to discredit the project, but to raise awareness of how powerful and flexible SDR tooling can unintentionally expose high-impact attack surfaces if untrusted inputs are not adequately constrained.
As software-defined radio systems continue to bridge the physical and digital domains, security considerations must extend all the way down to the signal layer.
For further information or collaboration on secure SDR and space-system assessments, contact the VisionSpace Cybersecurity Team.


