Skip to content

[Bug] Heap-use-after-free in All_vertexs_of_edges_visited during file load #980

@oneafter

Description

@oneafter

Description

We discovered a Heap-use-after-free vulnerability in sc-im. The crash occurs during the file loading process (readfile), specifically within the dependency graph evaluation (EvalBottomUp), when accessing a vertex that has already been freed by the parser (yyparse).

The ASAN report confirms a READ violation on a heap object that was allocated in GraphAddVertex and freed in gram.y.

Environment

  • OS: Linux x86_64
  • Complier: Clang
  • Build Configuration: Release mode with ASan enabled.

Vulnerability Details

  • Target: sc-im
  • Vulnerability Type: CWE-416: Use After Free
  • Function: All_vertexs_of_edges_visited
  • Location: src/graph.c:510
  • Root Cause Analysis:
  1. The PoC contains complex cell definitions and re-definitions (e.g., let A3 = ..., let A3 = ...).
  2. GraphAddVertex allocates memory for dependency graph nodes when let commands are parsed.
  3. At some point during parsing (gram.y:456), a graph node (or associated data) is freed. This might be due to a cell being redefined or cleared.
  4. However, EvalBottomUp (called by readfile) iterates over the graph to update dependencies. It encounters the dangling pointer to the freed vertex, causing the crash.

The graph evaluation logic (EvalBottomUp) seems to assume all graph nodes are valid, but the parser's logic allows freeing nodes that are still referenced in the evaluation queue or list.

Reproduce

  1. Build sc-im with Release optimization and ASAN enabled.
  2. Run with the crashing file:
poc
 = A
n
A10
let A2 = 20
let A3 = @sum(M1:Q2,A2)
l?t
let
l2
A
let A3 =/X1+A2
g
A102
goto A
l
format n
A10
let& 20
let A3 = @sum(M1:Q2,A2)
l?t
let
l2
A
let A3 =/2
gorin
A10
let A2 = A1+A2
goto A
let A
let
l2
got
le3 .
format ltrin
A10
let A2 = A1+A2 = A1+A2
goto A
l
format n
A10
let= 20
let A3 = @sum(M1:Q2,A2)
l?t
let
l2
A
let A3 =/2
g102
goto A
l
format n
A10
let& 20
let A3 = @sum(M1:Q2,A2)
l?t
let
l21:
 A20
let A3 =?
let A21:1 "
let B1 = 1.>=---1.5A80?
A3 = A2)
let A4 = @avg(A1:A3)
let A5 = (s"4
l0

A
let A3 =/2
gorin
A10
let A2 = A1+A2
gorTh.
# it.
format A 10 2A2 a"
let B2 = 0.8 0
format B @ = ""
leftstring B0 = "Cost"
leftstring A1 = B0
lena"
let B2 = 0.ormat B 10
leftstring A0 = "Item"
leftstring Bmmmmmmmmm
lefat A 10 2 0
for?at B 10 "
             0
lef@piring A0 =%"Item"
leftstrimmmmmmmmmmmemmmmmmmmmmm
lefat A ng B0 = "Cost"
leftstring A1 = "Apple"
let B! =eftstring A1 1.50
10
let A2 = A1+Ato A0
./src/sc-im --nocurses --quit_afterload poc.sc

ASAN report

==35403==ERROR: AddressSanitizer: heap-use-after-free on address 0x504000000fa4 at pc 0x556aca65ec65 bp 0x7ffef1c59150 sp 0x7ffef1c59148
READ of size 4 at 0x504000000fa4 thread T0
    #0 0x556aca65ec64 in All_vertexs_of_edges_visited /src/sc-im/src/graph.c:510:50
    #1 0x556aca65ec64 in EvalBottomUp /src/sc-im/src/graph.c:718:62
    #2 0x556aca61a56e in readfile /src/sc-im/src/file.c:915:5
    #3 0x556aca631abf in load_tbl /src/sc-im/src/file.c:2287:37
    #4 0x556aca6315b6 in load_file /src/sc-im/src/file.c:2248:5
    #5 0x556aca688e04 in main /src/sc-im/src/main.c:285:9
    #6 0x7f2b7f1001c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #7 0x7f2b7f10028a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e)
    #8 0x556aca528ba4 in _start (/src/sc-im/src/sc-im+0x4eba4) (BuildId: a0e0444e0e3f03f87f10e6f4f651691f85705c2c)

0x504000000fa4 is located 20 bytes inside of 48-byte region [0x504000000f90,0x504000000fc0)
freed by thread T0 here:
    #0 0x556aca5c873a in free (/src/sc-im/src/sc-im+0xee73a) (BuildId: a0e0444e0e3f03f87f10e6f4f651691f85705c2c)
    #1 0x556aca642315 in yyparse /src/sc-im/src/gram.y:456:146
    #2 0x556aca619f3b in readfile /src/sc-im/src/file.c:901:36
    #3 0x556aca631abf in load_tbl /src/sc-im/src/file.c:2287:37
    #4 0x556aca6315b6 in load_file /src/sc-im/src/file.c:2248:5

previously allocated by thread T0 here:
    #0 0x556aca5c89d3 in malloc (/src/sc-im/src/sc-im+0xee9d3) (BuildId: a0e0444e0e3f03f87f10e6f4f651691f85705c2c)
    #1 0x556aca6558a7 in GraphAddVertex /src/sc-im/src/graph.c:130:39
    #2 0x556aca67d0dd in let /src/sc-im/src/interp.c:1551:9

SUMMARY: AddressSanitizer: heap-use-after-free /src/sc-im/src/graph.c:510:50 in All_vertexs_of_edges_visited
Shadow bytes around the buggy address:
  0x504000000d00: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000d80: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000e00: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fd
  0x504000000e80: fa fa fd fd fd fd fd fa fa fa fd fd fd fd fd fa
  0x504000000f00: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00
=>0x504000000f80: fa fa fd fd[fd]fd fd fd fa fa fd fd fd fd fd fa
  0x504000001000: fa fa 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
  0x504000001080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000001100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000001180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000001200: 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
==35403==ABORTING

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions