-
Notifications
You must be signed in to change notification settings - Fork 60
Description
Bug report
Issue details
The FileHandling module contains a vulnerability that leads to invalid memory access when processing malformed RINEX v2 observation headers. Specifically, a null pointer dereference occurs when the header does not follow the expected structure, resulting in a segmentation fault (SIGSEGV) and application crash. An attacker can exploit this flaw to cause a Denial of Service (DoS), disrupting system availability and preventing legitimate processing of GNSS data.
Steps to reproduce
For demonstration purposes, the minimal test program Rinex3Header_read.cpp was used to perform simple reading of a RINEX v3 file, parsing its header and printing it out to the terminal.
#include "Rinex3ObsHeader.hpp"
#include "Rinex3ObsStream.hpp"
using namespace std;
using namespace gnsstk;
int main(int argc, char *argv[])
{
// Initialize RINEX stream from a file
Rinex3ObsStream roffs(argv[1]);
// Initialize RINEX header
Rinex3ObsHeader roh;
// Read RINEX header
roffs >> roh;
// Print RINEX header
roh.dump(cout);
return 0;
} The issue is triggered when a specially crafted RINEX v2 observation file with a malformed header provided as input, with a single whitespace character removed from second obervatio count record, misaligning the “PRN / # OF OBS” label with subsequent columns.
2.10 OBSERVATION DATA M (MIXED) RINEX VERSION / TYPE
gLAB gAGE 17-MAR-10 12:14 PGM / RUN BY / DATE
MRKR MARKER NAME
gAGE UPC: Technical University of Catalonia OBSERVER / AGENCY
IR2200716006 ASHTECH UZ-12 CQ00 REC # / TYPE / VERS
482 AOAD/M_T NONE ANT # / TYPE
4789028.4701 176610.0133 4195017.0310 APPROX POSITION XYZ
0.9030 0.0000 0.0000 ANTENNA: DELTA H/E/N
7 L1 L2 P1 P2 C1 S1 S2 # / TYPES OF OBSERV
2010 3 5 0 0 0.0000000 GPS TIME OF FIRST OBS
G07 815 815 815 815 815 815 815 PRN / # OF OBS
G09 246 246 246 246 246 246 246 PRN / # OF OBS
END OF HEADER
Running the test program using a crafted file as input resulted in a segmentation fault (SIGSEGV) during PRN enumeration made by dump function.
./Rinex3ObsHeader_read rinex2-minimal-v1
---------------------------------- REQUIRED ----------------------------------
Rinex Version 2.10, File type OBSERVATION DATA, System MIXED.
Prgm: gLAB, Run: 17-MAR-10 12:14, By: gAGE
Marker name: MRKR, Marker type: .
Observer : gAGE, Agency: UPC: Technical University of Catalonia
Rec#: IR2200716006, Type: ASHTECH UZ-12, Vers: CQ00
Antenna # : 482, Type : AOAD/M_T NONE
Position (XYZ,m) : (4789028.4700, 1.0000, 33.0000).
Antenna Delta (HEN,m) : (0.9030, 0.0000, 0.0000).
R2ObsTypes: L1 L2 P1 P2 C1 S1 S2
Time of first obs 2010/03/05 00:00:00.000 UNK
(This header is VALID)
---------------------------------- OPTIONAL ----------------------------------
PRN and number of observations for each obs type:
AddressSanitizer:DEADLYSIGNAL
=================================================================
==5209==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x7f49f2b14932 bp 0x000000000000 sp 0x7ffc406f1240 T0)
==5209==The signal is caused by a READ memory access.
==5209==Hint: address points to the zero page.
#0 0x7f49f2b14932 in std::_Rb_tree<gnsstk::ObservationType, std::pair<gnsstk::ObservationType const, char>, std::_Select1st<std::pair<gnsstk::ObservationType const, char> >, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::_M_lower_bound(std::_Rb_tree_node<std::pair<gnsstk::ObservationType const, char> >*, std::_Rb_tree_node_base*, gnsstk::ObservationType const&) /usr/include/c++/13/bits/stl_tree.h:1952
#1 0x7f49f2b14932 in std::_Rb_tree<gnsstk::ObservationType, std::pair<gnsstk::ObservationType const, char>, std::_Select1st<std::pair<gnsstk::ObservationType const, char> >, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::lower_bound(gnsstk::ObservationType const&) /usr/include/c++/13/bits/stl_tree.h:1271
#2 0x7f49f2b14932 in std::map<gnsstk::ObservationType, char, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::lower_bound(gnsstk::ObservationType const&) /usr/include/c++/13/bits/stl_map.h:1309
#3 0x7f49f2b14932 in std::map<gnsstk::ObservationType, char, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::operator[](gnsstk::ObservationType const&) /usr/include/c++/13/bits/stl_map.h:509
#4 0x7f49f2b14932 in gnsstk::RinexObsID::asString[abi:cxx11](double) const /home/ubuntu/gnsstk/core/lib/GNSSCore/RinexObsID.cpp:156
#5 0x7f49f28c4000 in gnsstk::Rinex3ObsHeader::dump(std::ostream&, double) const /home/ubuntu/gnsstk/core/lib/FileHandling/RINEX3/Rinex3ObsHeader.cpp:2474
#6 0x564d94e944a4 in gnsstk::Rinex3ObsHeader::dump(std::ostream&) const /home/ubuntu/gnsstk/core/lib/FileHandling/RINEX3/Rinex3ObsHeader.hpp:569
#7 0x564d94e944a4 in main /home/ubuntu/gnsstk/examples/Rinex3ObsHeader_read.cpp:16
#8 0x7f49f1dce1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#9 0x7f49f1dce28a in __libc_start_main_impl ../csu/libc-start.c:360
#10 0x564d94e94264 in _start (/home/ubuntu/gnsstk/build/examples/Rinex3ObsHeader_read+0x2264) (BuildId: 01637e45cf0838c450a8243cc379425d0ab41a06)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /usr/include/c++/13/bits/stl_tree.h:1952 in std::_Rb_tree<gnsstk::ObservationType, std::pair<gnsstk::ObservationType const, char>, std::_Select1st<std::pair<gnsstk::ObservationType const, char> >, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::_M_lower_bound(std::_Rb_tree_node<std::pair<gnsstk::ObservationType const, char> >*, std::_Rb_tree_node_base*, gnsstk::ObservationType const&)
==5209==ABORTING
Root cause analysis
The parseHeaderRecord function raised an exception when encountering the corrupted line, interpreting the label as “RN / # OF OBS” instead of “PRN / # OF OBS”. As a result, the parser aborted further header processing without terminating the program, leaving the in-memory header structure in an invalid state.
After the partial parsing, the dump function attempted to print header contents by iterating over observation records. At this stage, a RinexObsID object stored in vec pointed to a null memory address 0x0. That resulted in a null pointer dereference made by asString.
The variables ot2char, cb2char, and tc2char are mapping containers that translate the enumeration values of observation types (type), carrier bands (band), and tracking codes (code) to their respective RINEX 3 notation characters. For instance:
• ot2char maps an ObservationType to a single character, such as L for phase observations.
• cb2char maps a CarrierBand enum to a frequency character (e.g., 1 for L1).
• tc2char maps a TrackingCode enum to a character representing signal code (e.g., C for civilian tracking).
These mappings are used by the asString function to build a 3-character string representing the observation ID (like “L1C”) stored in the buffer buff.
The root cause of this vulnerability is the use of an uninitialized RinexObsID object. Because the type, band and code parameters were not correctly initialized, direct lookup in map containers such as ot2char using these parameters, resulted in invalid memory access triggering SIGSEGV.
Improperly (above) and properly (below) initialized values of parameters type / band / code and RinexObsID.
Backtrace
(gdb) backtrace
#0 0x00007ffff6a99932 in std::_Rb_tree<gnsstk::ObservationType, std::pair<gnsstk::ObservationType const, char>, std::_Select1st<std::pair<gnsstk::ObservationType const, char> >, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::_M_lower_bound (
__k=<error reading variable: Cannot access memory at address 0x8>,
__y=0x7ffff78c8f68 <gnsstk::RinexObsID::ot2char+8>, __x=0x5040000026d0, this=<optimized out>)
at /usr/include/c++/13/bits/stl_tree.h:1952
#1 std::_Rb_tree<gnsstk::ObservationType, std::pair<gnsstk::ObservationType const, char>, std::_Select1st<std::pair<gnsstk::ObservationType const, char> >, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::lower_bound (
__k=<error reading variable: Cannot access memory at address 0x8>, this=<optimized out>)
at /usr/include/c++/13/bits/stl_tree.h:1271
#2 std::map<gnsstk::ObservationType, char, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::lower_bound (
__x=<error reading variable: Cannot access memory at address 0x8>, this=<optimized out>)
at /usr/include/c++/13/bits/stl_map.h:1309
#3 std::map<gnsstk::ObservationType, char, std::less<gnsstk::ObservationType>, std::allocator<std::pair<gnsstk::ObservationType const, char> > >::operator[] (
__k=<error reading variable: Cannot access memory at address 0x8>, this=<optimized out>)
at /usr/include/c++/13/bits/stl_map.h:509
#4 gnsstk::RinexObsID::asString[abi:cxx11](double) const (this=0x0,
version=version@entry=2.1000000000000001) at /home/ubuntu/gnsstk/core/lib/GNSSCore/RinexObsID.cpp:156
#5 0x00007ffff6849001 in gnsstk::Rinex3ObsHeader::dump (this=this@entry=0x7ffff4406030, s=...,
dumpVersion=<optimized out>)
at /home/ubuntu/gnsstk/core/lib/FileHandling/RINEX3/Rinex3ObsHeader.cpp:2474
#6 0x00005555555564a5 in gnsstk::Rinex3ObsHeader::dump (s=..., this=0x7ffff4406030)
at /home/ubuntu/gnsstk/core/lib/FileHandling/RINEX3/Rinex3ObsHeader.hpp:569
#7 main (argc=<optimized out>, argv=0x7fffffffde98)
at /home/ubuntu/gnsstk/examples/Rinex3ObsHeader_read.cpp:16