From 46ad4ec769557e7c97259f6a404af0a3c0d1f99f Mon Sep 17 00:00:00 2001 From: rakshitha-marvell Date: Thu, 7 May 2026 11:58:27 +0530 Subject: [PATCH] Fix gNMI SET failure for union with leafref and string literals Fixes : https://github.com/sonic-net/sonic-gnmi/issues/669 Problem: gNMI SET fails for SONiC models such as PFCWD, SFLOW, and QUEUE when union types contain a combination of leafref and string literals. The validation logic incorrectly rejects valid literal values. Root Cause: Union validation in CVL/yparser does not correctly handle mixed union types involving leafref and literal strings. Literal values are not properly matched against union members, leading to validation failure. Fix: - Update union handling logic in yparser to correctly validate literal values alongside leafref types. - Ensure proper resolution and comparison of union members. Signed-off-by: rakshitha-marvell --- cvl/internal/yparser/yparser.go | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/cvl/internal/yparser/yparser.go b/cvl/internal/yparser/yparser.go index a6a867e35..fd1242b0c 100644 --- a/cvl/internal/yparser/yparser.go +++ b/cvl/internal/yparser/yparser.go @@ -39,6 +39,7 @@ import ( #include #include #include +#include extern int lyd_check_mandatory_tree(struct lyd_node *root, struct ly_ctx *ctx, const struct lys_module **modules, int mod_count, int options); @@ -137,6 +138,7 @@ int lyd_node_leafref_match_in_union(struct lys_module *module, const char *xpath struct lys_node *node = NULL; int idx = 0; struct lys_node_leaflist* lNode; + int pat_idx = 0; if (module == NULL) { @@ -154,6 +156,72 @@ int lyd_node_leafref_match_in_union(struct lys_module *module, const char *xpath //Now check if it matches with any leafref node lNode = (struct lys_node_leaflist*)node; + // Step 1: Check non-leafref union members FIRST. + // Return -1 only when the value satisfies a non-leafref type AND does NOT + // also satisfy the leafref target type — i.e. it is a pure literal that + // can never be a real port/interface name. + for (idx = 0; idx < lNode->type.info.uni.count; idx++) + { + struct lys_type *utype = &lNode->type.info.uni.types[idx]; + + if (utype->base == LY_TYPE_LEAFREF) continue; + + if (utype->base == LY_TYPE_STRING) + { + // Walk der chain — patterns may be in typedef, not directly on type + struct lys_type *stype = utype; + while (stype->info.str.pat_count == 0 && stype->der != NULL) + stype = &stype->der->type; + + // Loophole guard: string with NO pattern matches any value, + // including real port names — do not treat as a definitive + // non-leafref match; fall through to leafref check. + if (stype->info.str.pat_count == 0) + continue; + + // All patterns must match (AND semantics per RFC 7950 §9.4.5). + int all_match = 1; + for (pat_idx = 0; pat_idx < stype->info.str.pat_count; pat_idx++) + { + const char *pat = stype->info.str.patterns[pat_idx].expr; + if (pat == NULL) { all_match = 0; break; } + + // libyang stores a one-byte prefix before the regex text: + // 0x06 = normal match, 0x15 = invert-match + int invert = 0; + if (*pat == 0x06) { pat++; } + else if (*pat == 0x15) { invert = 1; pat++; } + + char anchored[1024]; + int alen = snprintf(anchored, sizeof(anchored), "^(%s)$", pat); + int matched = 0; + if (alen > 0 && (size_t)alen < sizeof(anchored)) { + regex_t re; + if (regcomp(&re, anchored, REG_EXTENDED|REG_NOSUB) == 0) { + matched = (regexec(&re, value, 0, NULL, 0) == 0); + if (invert) matched = !matched; + regfree(&re); + } else { + // regex compile failed — treat as non-match + all_match = 0; + break; + } + } else { + // snprintf truncated — treat as non-match + all_match = 0; + break; + } + if (!matched) { all_match = 0; break; } + } + + if (all_match) + { + return -1; // pure string literal — skip Redis check + } + } + } + + // Step 2: Value did not exclusively match a non-leafref type. for (idx = 0; idx < lNode->type.info.uni.count; idx++) { if (lNode->type.info.uni.types[idx].base != LY_TYPE_LEAFREF)