Skip to content

Use After Free in eval_else #853

@sae-as-me

Description

@sae-as-me

Affected Projects

Augeas 1.14.1 (https://github.com/hercules-team/augeas)

Problem Type

CWE-416: Use After Free

Description

A heap-use-after-free vulnerability was discovered in the eval_else function within Augeas. This issue occurs when evaluating certain expressions, potentially leading to a crash.

Details

The vulnerability arises in the eval_else function defined in src/pathx.c at line 1014.
The vulnerability is caused by the fact that in function rpl_realloc, when pointer p exists and n is a number larger than the address space pointed to by the current pointer p, a pointer pointing to a different address from p is returned. However, in function eval_else, no effective judgment is made and the original address is still used, resulting in heap-use-after-free.

static void eval_else(struct state *state, struct expr *expr, struct locpath_trace *lpt_right) {
    struct value *r = pop_value(state);
    struct value *l = pop_value(state);

    if ( l->tag == T_NODESET && r->tag == T_NODESET ) {
        int discard_maxns=0;
        struct nodeset **discard_ns=NULL;
        struct locpath_trace *lpt = state->locpath_trace;
        value_ind_t vind = make_value(T_NODESET, state);
        if (l->nodeset->used >0 || expr->left_matched) {    //heap-use-after-free
            expr->left_matched = 1;
            state->value_pool[vind].nodeset = clone_nodeset(l->nodeset, state);
            if( lpt_right != NULL ) {
                discard_maxns = lpt_right->maxns;
                discard_ns    = lpt_right->ns;
            }
        } 
		// ...
    } else {
        // ...
    }
}
void *
rpl_realloc (void *p, size_t n)
{
  if (p == NULL)
    return malloc (n);

  if (n == 0)
    {
      free (p);    //set p to NULL after free?
      return NULL;
    }

  if (xalloc_oversized (n, 1))
    {
      errno = ENOMEM;
      return NULL;
    }

  void *result = realloc (p, n);    //Free original address when n is greater than the current address space size

#if !HAVE_MALLOC_POSIX
  if (result == NULL)
    errno = ENOMEM;
#endif

  return result;
}

Image

PoC

Steps to reproduce:

  1. Clone the Augeas repository and build it with the OSS-Fuzz configuration.
  • ENV
export CC='clang'
export CXX='clang++'
export CFLAGS='-fsanitize=address -g'
export CXXFLAGS='-fsanitize=address -g'
  • harness from OSS-Fuzz
#include "config.h"
#include "augeas.h"
#include "internal.h"
#include "memory.h"
#include "syntax.h"
#include "transform.h"
#include "errcode.h"


#include <fnmatch.h>
#include <argz.h>
#include <string.h>
#include <stdarg.h>
#include <locale.h>



extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
	if(size<3){
		return 0;
	}
	
	char *loadpath = NULL;
	const char *value;
	const char *label;
	char *new_str = (char *)malloc(size+1);
	if (new_str == NULL){
		return 0;
	}
	memcpy(new_str, data, size);
	new_str[size] = '\0';
	
	struct augeas *aug = aug_init(new_str, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
	aug_defvar(aug, new_str, &new_str[1]);
	aug_get(aug, new_str, &value);
	aug_label(aug, new_str, &label);
	
	aug_rename(aug, new_str, &new_str[1]);
	aug_text_store(aug, &new_str[1], new_str, &new_str[2]);	
	aug_print(aug, stdout, new_str);
	aug_setm(aug, new_str, NULL, &new_str[1]);	
	
	free(new_str);
	aug_close(aug);
	return 0;
}
  1. Compile the fuzzer
clang++ -fsanitize=address,fuzzer -g -std=c++11 -Isrc/ `xml2-config --cflags` augeas_api_fuzzer.cc -o augeas_api_fuzzer src/.libs/libaugeas.a src/.libs/libfa.a ./gnulib/lib/.libs/libgnu.a /usr/lib/x86_64-linux-gnu/libxml2.a
  1. Run the fuzzer to trigger the segmentation fault:

eval_else-huaf.zip

./augeas_api_fuzzer ./eval_else-huaf

The invalid read access will cause AddressSanitizer to report a segmentation fault during the execution of the post-processing logic.

Report

Running: ./eval_else-huaf
=================================================================
==115092==ERROR: AddressSanitizer: heap-use-after-free on address 0x60c000002ca8 at pc 0x5d2d9466589e bp 0x7ffd8eb7fb20 sp 0x7ffd8eb7fb18
READ of size 8 at 0x60c000002ca8 thread T0
    #0 0x5d2d9466589d in eval_else /fuzz/project/augeas/src/pathx.c:1014:16
    #1 0x5d2d946604fd in eval_binary /fuzz/project/augeas/src/pathx.c:1209:9
    #2 0x5d2d9465ee55 in eval_expr /fuzz/project/augeas/src/pathx.c:1499:9
    #3 0x5d2d9464ab1e in pathx_eval /fuzz/project/augeas/src/pathx.c:2869:5
    #4 0x5d2d9464d8b0 in pathx_symtab_define /fuzz/project/augeas/src/pathx.c:3159:13
    #5 0x5d2d9463ce68 in aug_defvar /fuzz/project/augeas/src/augeas.c:900:18
    #6 0x5d2d946351fc in LLVMFuzzerTestOneInput /fuzz/project/augeas/augeas_api_fuzzer.cc:53:2
    #7 0x5d2d9455b563 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x68563) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #8 0x5d2d945452df in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x522df) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #9 0x5d2d9454b036 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x58036) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #10 0x5d2d94574e52 in main (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x81e52) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #11 0x7d01ffb4dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #12 0x7d01ffb4de3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #13 0x5d2d9453fba4 in _start (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x4cba4) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)

0x60c000002ca8 is located 40 bytes inside of 128-byte region [0x60c000002c80,0x60c000002d00)
freed by thread T0 here:
    #0 0x5d2d945f8006 in __interceptor_realloc (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x105006) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #1 0x5d2d9473e4b3 in rpl_realloc /fuzz/project/augeas/gnulib/lib/realloc.c:55:18
    #2 0x5d2d9466cdc1 in mem_realloc_n /fuzz/project/augeas/src/memory.c:98:11
    #3 0x5d2d94657a7b in make_value /fuzz/project/augeas/src/pathx.c:564:13
    #4 0x5d2d94665874 in eval_else /fuzz/project/augeas/src/pathx.c:1013:28
    #5 0x5d2d946604fd in eval_binary /fuzz/project/augeas/src/pathx.c:1209:9
    #6 0x5d2d9465ee55 in eval_expr /fuzz/project/augeas/src/pathx.c:1499:9
    #7 0x5d2d9464ab1e in pathx_eval /fuzz/project/augeas/src/pathx.c:2869:5
    #8 0x5d2d9464d8b0 in pathx_symtab_define /fuzz/project/augeas/src/pathx.c:3159:13
    #9 0x5d2d9463ce68 in aug_defvar /fuzz/project/augeas/src/augeas.c:900:18
    #10 0x5d2d946351fc in LLVMFuzzerTestOneInput /fuzz/project/augeas/augeas_api_fuzzer.cc:53:2
    #11 0x5d2d9455b563 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x68563) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #12 0x5d2d945452df in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x522df) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #13 0x5d2d9454b036 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x58036) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #14 0x5d2d94574e52 in main (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x81e52) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #15 0x7d01ffb4dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

previously allocated by thread T0 here:
    #0 0x5d2d945f7dc8 in __interceptor_calloc (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x104dc8) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #1 0x5d2d9466cbbc in mem_alloc_n /fuzz/project/augeas/src/memory.c:66:23
    #2 0x5d2d94648b3b in pathx_parse /fuzz/project/augeas/src/pathx.c:2669:9
    #3 0x5d2d946365e3 in pathx_aug_parse /fuzz/project/augeas/src/augeas.c:295:5
    #4 0x5d2d9463cdd7 in aug_defvar /fuzz/project/augeas/src/augeas.c:898:13
    #5 0x5d2d946351fc in LLVMFuzzerTestOneInput /fuzz/project/augeas/augeas_api_fuzzer.cc:53:2
    #6 0x5d2d9455b563 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x68563) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #7 0x5d2d945452df in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x522df) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #8 0x5d2d9454b036 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x58036) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #9 0x5d2d94574e52 in main (/fuzz/fuzzers/cs/augeas_api_fuzzer+0x81e52) (BuildId: c970f2227c313c5fcf7dac784e2536ef9e443a1e)
    #10 0x7d01ffb4dd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-use-after-free /fuzz/project/augeas/src/pathx.c:1014:16 in eval_else
Shadow bytes around the buggy address:
  0x0c187fff8540: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c187fff8550: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
  0x0c187fff8560: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c187fff8570: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
  0x0c187fff8580: fd fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa
=>0x0c187fff8590: fd fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd
  0x0c187fff85a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff85b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff85c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff85d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff85e0: 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
==115092==ABORTING

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