Project: libsass (sass/libsass), language: c++
Vulnerability instance: libsass.cve-2018-19797
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/sass/libsass/issues/2779
## Description:
Issue: sass/libsass#2779
Title: A crash in the Selector_List::populate_extends function in both version 3.5.5 and the latest code
State: closed
Created by: zyingp
Created at: 2018-12-01 14:15:32+00:00
Labels: Bug - Should Error, Fuzzy

Issue Body:
I found a crash by fuzzing libsass&sassc. It can be reproduced both in version 3.5.5 and the master branch (accessed on 2018/11/27) code. Run sassc (built with ASan) with a special file sass_id01 (https://github.com/zyingp/temp/blob/master/sass_id01).

`./sassc/bin/sassc sass_id01  `

The outputs are as follows:
```
AddressSanitizer:DEADLYSIGNAL
=================================================================
==88483==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000098 (pc 0x00010dd0bc29 bp 0x7ffee1f26050 sp 0x7ffee1f25d40 T0)
==88483==The signal is caused by a READ memory access.
==88483==Hint: address points to the zero page.
    #0 0x10dd0bc28 in Sass::Selector_List::populate_extends(Sass::SharedImpl<Sass::Selector_List>, Sass::Subset_Map&) SharedPtr.hpp:151
    #1 0x10de1034f in Sass::Functions::selector_extend(Sass::Environment<Sass::SharedImpl<Sass::AST_Node> >&, Sass::Environment<Sass::SharedImpl<Sass::AST_Node> >&, Sass::Context&, char const*, Sass::ParserState, std::__1::vector<Sass::Backtrace, std::__1::allocator<Sass::Backtrace> >, std::__1::vector<Sass::SharedImpl<Sass::Selector_List>, std::__1::allocator<Sass::SharedImpl<Sass::Selector_List> > >) functions.cpp:2133
    #2 0x10dfce2c4 in Sass::Eval::operator()(Sass::Function_Call*) eval.cpp:1014
    #3 0x10dfb4371 in Sass::Eval::operator()(Sass::Debug*) eval.cpp:406
    #4 0x10e000471 in Sass::Expand::operator()(Sass::Debug*) expand.cpp:389
    #5 0x10dff30d6 in Sass::Expand::append_block(Sass::Block*) expand.cpp:811
    #6 0x10dff26c8 in Sass::Expand::operator()(Sass::Block*) expand.cpp:72
    #7 0x10dd72ce9 in Sass::Context::compile() context.cpp:670
    #8 0x10dd70096 in Sass::File_Context::parse() context.cpp:597
    #9 0x10e1112f1 in sass_compiler_parse sass_context.cpp:234
    #10 0x10e110b29 in sass_compile_context(Sass_Context*, Sass::Context*) sass_context.cpp:371
    #11 0x10dcd79a6 in compile_file sassc.c:158
    #12 0x10dcd82e6 in main sassc.c:370
    #13 0x7fff701cb014 in start (libdyld.dylib:x86_64+0x1014)

==88483==Register values:
rax = 0x0000100000000013  rbx = 0x00007ffee1f25ee0  rcx = 0x0000100000000000  rdx = 0x00001c2200000f59  
rdi = 0x0000000000000098  rsi = 0x0000000000000000  rbp = 0x00007ffee1f26050  rsp = 0x00007ffee1f25d40  
 r8 = 0x0000611000007c90   r9 = 0x0000602000002440  r10 = 0x00007ffee1f25e60  r11 = 0x00007ffee1f25e20  
r12 = 0x00007ffee1f25dc8  r13 = 0x0000100000000000  r14 = 0x00007ffee1f25de0  r15 = 0x0000100000000000  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV SharedPtr.hpp:151 in Sass::Selector_List::populate_extends(Sass::SharedImpl<Sass::Selector_List>, Sass::Subset_Map&)
==88483==ABORTING
Abort trap: 6
```


I found the cause is that in the `populate_extends` function in ast.cpp (the function was moved to ast_selectors.cpp several days ago), the `pIter` variable is NULL when coming to the crashing line (noted below). **Adding a null check on `pIter` indeed could fix the problem but not sure whether there are better ways.**
```C++
  void Selector_List::populate_extends(Selector_List_Obj extendee, Subset_Map& extends)
  {

    Selector_List_Ptr extender = this;
    for (auto complex_sel : extendee->elements()) {
      // ... omit some code
      Complex_Selector_Obj pIter = complex_sel;
      while (pIter) {
        Compound_Selector_Obj pHead = pIter->head();
        if (pHead && Cast<Parent_Selector>(pHead->elements()[0]) == NULL) {
          compound_sel = pHead;
          break;
        }

        pIter = pIter->tail();
      }

      if (!pIter->head() || pIter->tail()) {  // !!!! crashed here !!!!  Suggest at least adding a null check like pIter != NULL at first
        coreError("nested selectors may not be extended", c->pstate());
      }
     // ... omit some code
}
```






Comments:
Comment by zyingp on 2018-12-03 06:05:03+00:00:
Assigned CVE-2018-19797

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
export BUILD='static'
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool
RUN git clone https://github.com/sass/libsass libsass
RUN git -C libsass checkout 39e30874b9a5dd6a802c20e8b0470ba44eeba929
WORKDIR $SRC/libsass
COPY build.sh $SRC/

Additional files:
filename: data_context_fuzzer.cc
content: #include "sass.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  char* sass_data = (char*) malloc(sizeof(char) * size + 1);
  if (sass_data == NULL) return 0;

  memcpy(sass_data, data, size);
  sass_data[size] = '\0';

  struct Sass_Data_Context* ctx = sass_make_data_context(sass_data);
  if (ctx == NULL) {
    free(sass_data);
    return 0;
  }

  struct Sass_Options* options = sass_make_options();
  if (options == NULL) {
    sass_delete_data_context(ctx);
    return 0;
  }

  sass_option_set_output_style(options, SASS_STYLE_NESTED);
  sass_option_set_precision(options, 5);

  sass_data_context_set_options(ctx, options);
  sass_compile_data_context(ctx);

  sass_delete_data_context(ctx);
  sass_delete_options(options);

  return 0;
}