From 9ef364b37586ca4a976091d04dd089894df3c602 Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Wed, 7 Oct 2026 19:51:31 +0200 Subject: [PATCH 1/2] fix: disable leafref binary search when xpath predicates filter the list leafref_opt_search_detect() enabled binary search when all xvec entries share the same yang and parent (leaf-list) or grandparent (list key). leafref_opt_search() then searches among ALL siblings of that parent, but xvec may be only a subset of them if the leafref path has a predicate, eg "../iface[enabled='true']/name". A reference to an entry excluded by the predicate was then wrongly accepted. This was the case for a single match before, and for any number of matches after the fix for https://github.com/clicon/clixon/issues/669 allowed list entries with different parents. Only use binary search if xvec contains all such siblings, ie the number of siblings with the same yang equals xlen. Predicates above the common parent (eg selecting a context) do not filter siblings and keep the optimization. --- lib/src/clixon_validate.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/src/clixon_validate.c b/lib/src/clixon_validate.c index 4e0c195b9..e72a0d7ee 100644 --- a/lib/src/clixon_validate.c +++ b/lib/src/clixon_validate.c @@ -283,6 +283,25 @@ leafref_opt_cache_new(yang_stmt *ys, return 0; } +/*! Count children of xp with yang spec ys + * + * @param[in] xp XML parent + * @param[in] ys Yang spec of children to count + * @retval n Number of children + */ +static size_t +leafref_opt_count(cxobj *xp, + yang_stmt *ys) +{ + cxobj *x = NULL; + size_t n = 0; + + while ((x = xml_child_each(xp, x, CX_ELMNT)) != NULL) + if (xml_spec(x) == ys) + n++; + return n; +} + /*! Detect if binary search can be used * * If xlen is very large, this takes time @@ -294,6 +313,7 @@ leafref_opt_cache_new(yang_stmt *ys, * IF yang of all elements is same YANG * AND that yang is LEAF-LIST AND is ordered-by system * OR that yang's parent is LIST AND is ordered-by system + * AND xvec contains all such siblings, ie not filtered by xpath predicates * THEN use binary search * @param[in] xvec Vector of matching XML values * @param[in] xlen Length of xvec @@ -369,6 +389,15 @@ leafref_opt_search_detect(cxobj **xvec, } } if (i == xlen && x0 != NULL && xml_parent(x0) && xml_spec(x0)){ + /* Binary search covers all siblings, but xvec may be a subset filtered by + * xpath predicates, eg ../iface[enabled='true']/name + */ + if (is_list){ + if (xpp == NULL || leafref_opt_count(xpp, y0p) != xlen) + return 0; + } + else if (leafref_opt_count(x0p, y0) != xlen) + return 0; *search = 1; *x0pp = x0; } From f703d1e80c6c9cbdf680549976bba3d193554bce Mon Sep 17 00:00:00 2001 From: khromenokroman Date: Wed, 7 Oct 2026 19:51:31 +0200 Subject: [PATCH 2/2] test: leafref path with predicate on list must not match filtered entries Add sender/active-template leafref "/sender[enabled='true']/name" and check that a reference to a disabled sender fails validation while a reference to an enabled one passes. --- test/test_leafref.sh | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/test_leafref.sh b/test/test_leafref.sh index 7c902e246..69ec1f97d 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -99,7 +99,18 @@ module example{ leaf template{ type leafref{ path "/sender/name"; - require-instance true; + require-instance true; + } + } + leaf enabled{ + type boolean; + } + /* leafref filtered by predicate: binary search must not match + * entries excluded by the predicate */ + leaf active-template{ + type leafref{ + path "/sender[enabled='true']/name"; + require-instance true; } } } @@ -279,6 +290,24 @@ expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" " new "issue 669: discard-changes" expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" +# Leafref path with predicate on the list: xvec is a subset of the list entries. +# LEAFREF_OPTIMIZE binary search must not be used, it searches all entries and +# would accept a reference to an entry excluded by the predicate. +new "leafref predicate: add senders s1,s3 enabled, s2 disabled, s4 refers to disabled s2" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "s1trues2falses3trues4s2" "" "" + +new "leafref predicate: validate expect failure (s2 is disabled)" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "leafref predicate: set s4 to refer to enabled s3" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "s4s3" "" "" + +new "leafref predicate: validate expect OK (s3 is enabled)" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + +new "leafref predicate: discard-changes" +expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "" "" "" + # Leafref to non-key leaf in a list: LEAFREF_OPTIMIZE binary search must not # be used when the target leaf is not the list key. With two routers present # (key "vrf-id"), the target leaf "area-tag" is a non-key unique leaf.