Skip to content

Known Defect | CIccXmlArrayType::ParseText() heap-buffer-overflow | IccUtilXml.cpp#L995 #178

@xsscx

Description

@xsscx

A Buffer Overflow in ParseText exists from the Initial Commit. This reproduction is made with Clang and fil-c toolchains.

The Code

template <class T, icTagTypeSignature Tsig>
icUInt32Number CIccXmlArrayType<T, Tsig>::ParseText(T* pBuf, icUInt32Number nSize, const char *szText)
{	
  icUInt32Number n = 0, b = 0;
  bool bInNum = false;
  char num[256] = {0};

  while (*szText && n<nSize) {	  
	  if (icIsNumChar(*szText)) {
      if (!bInNum) {
        bInNum = true;
        b=0;
      }
      num[b] = *szText;
 
      if (b+2<sizeof(num))
        b++;
    }
    else if (bInNum) {
      num[b] = 0;
      if (!strncmp(num, "nan", 3) || !strncmp(num, "-nan", 4)) {
        pBuf[n] = (T)nanf(num);
      }
      else {
        pBuf[n] = (T)atof(num);
      }
      n++;
      bInNum = false;
    }
    szText++;
  }
  if (bInNum) {
    num[b] = 0;
    if (!strncmp(num, "nan", 3) || !strncmp(num, "-nan", 4)) {
      pBuf[n] = (T)nanf(num);
    }
    else {
      pBuf[n] = (T)atof(num);
    }
    n++;
  } 

  return n;
}
...

The Bug

  • The loop exits because n == nSize
  • But bInNum remains true (last number didn’t get flushed inside the loop)
  • The final block writes pBuf[n], where n == nSize

→ Heap-buffer-overflow by one element

fil-c

Fil-C is a memory-safe implementation of C and C++ created by Filip Pizlo at Epic Games. It provides full C/C++ compatibility while preventing all memory safety errors through a combination of concurrent garbage co
llection and invisible capabilities (InvisiCap system).

Key characteristics:

  • Memory Safety: All pointers carry capabilities tracking bounds and type information
  • GIMSO Principle: "Garbage In, Memory Safety Out" - no unsafe escape hatches
  • Garbage Collection: Uses FUGC (Fil's Unbelievable Garbage Collector) - concurrent, non-moving GC
  • Platform: Currently Linux/X86_64 only
  • Performance: 1.5x-5x slower than standard C (actively being optimized)

Build fil-c

Required: Namespace builds for all dependencies

Build iccDEV

Cmake Configure for iccDEV

export PATH="$HOME/fil-c/build/bin:$PATH" CC=filcc CXX=fil++ AR=llvm-ar RANLIB=llvm-ranlib LD_LIBRARY_PATH="$HOME/fil-c/build/lib:$LD_LIBRARY_PATH" && \
       LIBCXX="$HOME/fil-c/build/bin/../include/c++/v1" && rm -rf CMakeCache.txt CMakeFiles && cmake -G Ninja -D CMAKE_C_COMPILER="$CC" \
       -D CMAKE_CXX_COMPILER="$CXX" -D CMAKE_AR="$AR" -D CMAKE_RANLIB="$RANLIB" -D CMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -fuse-ld=lld" \
       -D CMAKE_SHARED_LINKER_FLAGS="-stdlib=libc++ -fuse-ld=lld" -D CMAKE_MODULE_LINKER_FLAGS="-stdlib=libc++ -fuse-ld=lld" \
       -D CMAKE_CXX_FLAGS="-isystem $LIBCXX -isystem /usr/include -isystem /usr/include/x86_64-linux-gnu -stdlib=libc++" \
       -D LIBXML2_INCLUDE_DIR="$HOME/fil-c/build/include/libxml2" -D LIBXML2_LIBRARY="$HOME/fil-c/build/lib/libxml2.so" \
       -D TIFF_INCLUDE_DIR=/usr/include/x86_64-linux-gnu -D TIFF_LIBRARY=/usr/lib/x86_64-linux-gnu/libtiff.so \
       -D PNG_PNG_INCLUDE_DIR=/usr/include -D PNG_LIBRARY=/usr/lib/x86_64-linux-gnu/libpng16.so -D ZLIB_INCLUDE_DIR=/usr/include \
       -D ZLIB_LIBRARY=/usr/lib/x86_64-linux-gnu/libz.so -D JPEG_INCLUDE_DIR=/usr/include 
       -D JPEG_LIBRARY=/usr/lib/x86_64-linux-gnu/libjpeg.so \
       Cmake
make -j$(nproc)

Add Tools to PATH

        cd Testing/
        echo "=== Updating PATH ==="
         for d in ../Build/Tools/*; do
          [ -d "$d" ] && export PATH="$(realpath "$d"):$PATH"
         done

fil-c reproduction

$ Testing/CMYK-3DLUTs$ iccFromXml CMYK-3DLUTs2.xml CMYK-3DLUTs2.icc

filc safety error: cannot read pointer with ptr >= upper.
    pointer: 0x718f0b40ea20,0x718f0b408c10,0x718f0b40ea20
    expected 1 bytes.
semantic origin:
    IccXML/IccLibXML/IccUtilXml.cpp:995:10: CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*)
check scheduled at:
    IccXML/IccLibXML/IccUtilXml.cpp:995:10: CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*)
    IccXML/IccLibXML/IccUtilXml.cpp:813:12: CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseTextArrayNum(char const*, unsigned int, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp:750:19: CIccSinglSampledeCurveXml::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp:1106:17: ParseXmlCurve(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>)
    IccXML/IccLibXML/IccMpeXml.cpp:1164:37: CIccMpeXmlCurveSet::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp: non-virtual thunk to CIccMpeXmlCurveSet::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp:2582:26: CIccMpeXmlCalculator::ParseImport(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp:3099:8: CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccMpeXml.cpp: non-virtual thunk to CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccTagXml.cpp:4059:20: CIccTagXmlMultiProcessElement::ParseElement(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccTagXml.cpp:4119:12: CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccTagXml.cpp: non-virtual thunk to CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccProfileXml.cpp:719:20: CIccProfileXml::ParseTag(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccProfileXml.cpp:828:12: CIccProfileXml::ParseXml(_xmlNode*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&)
    IccXML/IccLibXML/IccProfileXml.cpp:885:13: CIccProfileXml::LoadXml(char const*, char const*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*)
    IccXML/CmdLine/IccFromXml/IccFromXml.cpp:68:18: main
    ../sysdeps/nptl/libc_start_call_main.h:58:16: __libc_start_call_main
    ../csu/libc-start.c:161:3: __libc_start_main
    <runtime>: start_program
[3554241] filc panic: thwarted a futile attempt to violate memory safety.
Trace/breakpoint trap (core dumped)

Clang Asan Repro

$ iccFromXml CMYK-3DLUTs2.xml CMYK-3DLUTs2.icc
=================================================================
==477335==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x52b00001b04f at pc 0x733f0371998c bp 0x7ffc63aa7810 sp 0x7ffc63aa7808
READ of size 1 at 0x52b00001b04f thread T0
    #0 0x733f0371998b in CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*) IccXML/IccLibXML/IccUtilXml.cpp:995:10
    #1 0x733f0371bdba in CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseTextArrayNum(char const*, unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccUtilXml.cpp:813:12
    #2 0x733f03575f70 in CIccSinglSampledeCurveXml::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:750:19
    #3 0x733f03581f35 in ParseXmlCurve(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>) IccXML/IccLibXML/IccMpeXml.cpp:1106:17
    #4 0x733f0358143b in CIccMpeXmlCurveSet::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:1164:37
    #5 0x733f035b0dad in CIccMpeXmlCalculator::ParseImport(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:2582:26
    #6 0x733f035c4b9f in CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:3099:8
    #7 0x733f036a3665 in CIccTagXmlMultiProcessElement::ParseElement(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccTagXml.cpp:4059:20
    #8 0x733f036a56a3 in CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccTagXml.cpp:4119:12
    #9 0x733f03626682 in CIccProfileXml::ParseTag(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccProfileXml.cpp:719:20
    #10 0x733f0362a9f7 in CIccProfileXml::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccProfileXml.cpp:828:12
    #11 0x733f0362afe9 in CIccProfileXml::LoadXml(char const*, char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*) IccXML/IccLibXML/IccProfileXml.cpp:885:13
    #12 0x5b81abd66f07 in main IccXML/CmdLine/IccFromXml/IccFromXml.cpp:68:18
    #13 0x733f01c2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #14 0x733f01c2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #15 0x5b81abc8a574 in _start (Build/Tools/IccFromXml/iccFromXml+0x2f574) (BuildId: 15ca05e0f3102b7bd8d1a018f19c97e7552121b7)

0x52b00001b04f is located 0 bytes after 24143-byte region [0x52b000015200,0x52b00001b04f)
allocated by thread T0 here:
    #0 0x5b81abd63b01 in operator new[](unsigned long) (Build/Tools/IccFromXml/iccFromXml+0x108b01) (BuildId: 15ca05e0f3102b7bd8d1a018f19c97e7552121b7)
    #1 0x733f03574a2d in CIccSinglSampledeCurveXml::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:686:19
    #2 0x733f03581f35 in ParseXmlCurve(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>) IccXML/IccLibXML/IccMpeXml.cpp:1106:17
    #3 0x733f0358143b in CIccMpeXmlCurveSet::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:1164:37
    #4 0x733f035b0dad in CIccMpeXmlCalculator::ParseImport(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:2582:26
    #5 0x733f035c4b9f in CIccMpeXmlCalculator::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccMpeXml.cpp:3099:8
    #6 0x733f036a3665 in CIccTagXmlMultiProcessElement::ParseElement(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccTagXml.cpp:4059:20
    #7 0x733f036a56a3 in CIccTagXmlMultiProcessElement::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccTagXml.cpp:4119:12
    #8 0x733f03626682 in CIccProfileXml::ParseTag(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccProfileXml.cpp:719:20
    #9 0x733f0362a9f7 in CIccProfileXml::ParseXml(_xmlNode*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) IccXML/IccLibXML/IccProfileXml.cpp:828:12
    #10 0x733f0362afe9 in CIccProfileXml::LoadXml(char const*, char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*) IccXML/IccLibXML/IccProfileXml.cpp:885:13
    #11 0x5b81abd66f07 in main IccXML/CmdLine/IccFromXml/IccFromXml.cpp:68:18
    #12 0x733f01c2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #13 0x733f01c2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #14 0x5b81abc8a574 in _start (Build/Tools/IccFromXml/iccFromXml+0x2f574) (BuildId: 15ca05e0f3102b7bd8d1a018f19c97e7552121b7)

SUMMARY: AddressSanitizer: heap-buffer-overflow IccXML/IccLibXML/IccUtilXml.cpp:995:10 in CIccXmlArrayType<unsigned short, (icTagTypeSignature)1969828150>::ParseText(unsigned short*, unsigned int, char const*)
Shadow bytes around the buggy address:
  0x52b00001ad80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x52b00001ae00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x52b00001ae80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x52b00001af00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x52b00001af80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x52b00001b000: 00 00 00 00 00 00 00 00 00[07]fa fa fa fa fa fa
  0x52b00001b080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x52b00001b100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x52b00001b180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x52b00001b200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x52b00001b280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==477335==ABORTING

Potential Fix

...
  if (bInNum && n < nSize) {
    num[b] = 0;
    if (!strncmp(num, "nan", 3) || !strncmp(num, "-nan", 4))
      pBuf[n] = (T)nanf(num);
    else
      pBuf[n] = (T)atof(num);
    n++;
  }
  ...

Summary

This issue to be addressed in a future PR.

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions