Skip to content

SegFault (via pointer dereference) on RINEX v2 header #29

@ctrlsill

Description

@ctrlsill

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.

Image Image

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.

Image Image

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.

Image

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.

Image Image

Improperly (above) and properly (below) initialized values of parameters type / band / code and RinexObsID.

Image

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions