union BUGFIX proper LYB value validation

Fixes #1560
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index c973a28..bd6105f 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -151,13 +151,14 @@
 }
 
 /**
- * @brief Store subvalue as a specific type.
+ * @brief Store (and validate) subvalue as a specific type.
  *
  * @param[in] ctx libyang context.
- * @param[in] type Specific union type to use for storing.
- * @param[in] subvalue Union subvalue structure.
+ * @param[in] type_u Union type.
+ * @param[in] type_idx Union type index to use for storing (and validating).
+ * @param[in,out] subvalue Union subvalue structure, its value needs to be filled.
  * @param[in] options The store options.
- * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] validate Whether the value needs to be validated.
  * @param[in] ctx_node Context node for prefix resolution.
  * @param[in] tree Data tree for resolving (validation).
  * @param[in,out] unres Global unres structure.
@@ -165,50 +166,88 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue, uint32_t options,
-        ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
-        struct ly_err_item **err)
+union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint32_t type_idx, struct lyd_value_union *subvalue,
+        uint32_t options, ly_bool validate, const struct lyd_node *ctx_node, const struct lyd_node *tree,
+        struct lys_glob_unres *unres, struct ly_err_item **err)
 {
-    LY_ERR ret;
+    LY_ERR rc = LY_SUCCESS;
+    struct lysc_type *type = type_u->types[type_idx];
     const void *value = NULL;
     size_t value_len = 0;
-    uint32_t opts;
+    ly_bool dynamic = 0;
+    LY_VALUE_FORMAT format;
+    void *prefix_data;
+    uint32_t opts = 0, ti;
 
     *err = NULL;
 
     if (subvalue->format == LY_VALUE_LYB) {
-        lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
+        lyb_parse_union(subvalue->original, subvalue->orig_len, &ti, &value, &value_len);
+        if (ti != type_idx) {
+            /* value of another type, first store the value properly and then use its JSON value for parsing */
+            rc = type_u->types[ti]->plugin->store(ctx, type_u->types[ti], value, value_len, LYPLG_TYPE_STORE_ONLY,
+                    subvalue->format, subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err);
+            if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
+                /* clear any leftover/freed garbage */
+                memset(&subvalue->value, 0, sizeof subvalue->value);
+                return rc;
+            }
+
+            assert(subvalue->value.realtype);
+            value = subvalue->value.realtype->plugin->print(ctx, &subvalue->value, LY_VALUE_JSON, NULL, &dynamic, &value_len);
+
+            /* to avoid leaks, free subvalue->value, but we need the value, which may be stored there */
+            if (!dynamic) {
+                value = strndup(value, value_len);
+                dynamic = 1;
+            }
+            type_u->types[ti]->plugin->free(ctx, &subvalue->value);
+
+            format = LY_VALUE_JSON;
+            prefix_data = NULL;
+        } else {
+            format = subvalue->format;
+            prefix_data = subvalue->prefix_data;
+        }
     } else {
         value = subvalue->original;
         value_len = subvalue->orig_len;
+        format = subvalue->format;
+        prefix_data = subvalue->prefix_data;
     }
 
-    opts = (options & LYPLG_TYPE_STORE_ONLY) ? LYPLG_TYPE_STORE_ONLY : 0;
-    ret = type->plugin->store(ctx, type, value, value_len, opts, subvalue->format, subvalue->prefix_data, subvalue->hints,
+    if (options & LYPLG_TYPE_STORE_ONLY) {
+        opts |= LYPLG_TYPE_STORE_ONLY;
+    }
+    if (dynamic) {
+        opts |= LYPLG_TYPE_STORE_DYNAMIC;
+    }
+
+    rc = type->plugin->store(ctx, type, value, value_len, opts, format, prefix_data, subvalue->hints,
             subvalue->ctx_node, &subvalue->value, unres, err);
-    if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+    if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
         /* clear any leftover/freed garbage */
         memset(&subvalue->value, 0, sizeof subvalue->value);
-        return ret;
+        return rc;
     }
 
-    if (resolve && (ret == LY_EINCOMPLETE)) {
-        /* we need the value resolved */
-        ret = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
-        if (ret) {
-            /* resolve failed, we need to free the stored value */
+    if (validate && (rc == LY_EINCOMPLETE)) {
+        /* we need the value validated */
+        rc = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+        if (rc) {
+            /* validate failed, we need to free the stored value */
             type->plugin->free(ctx, &subvalue->value);
         }
     }
 
-    return ret;
+    return rc;
 }
 
 /**
  * @brief Find the first valid type for a union value.
  *
  * @param[in] ctx libyang context.
- * @param[in] types Sized array of union types.
+ * @param[in] type_u Union type.
  * @param[in] subvalue Union subvalue structure.
  * @param[in] options The store options.
  * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
@@ -220,7 +259,7 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
+union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct lyd_value_union *subvalue,
         uint32_t options, ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree,
         uint32_t *type_idx, struct lys_glob_unres *unres, struct ly_err_item **err)
 {
@@ -233,20 +272,16 @@
 
     *err = NULL;
 
-    if (!types || !LY_ARRAY_COUNT(types)) {
-        return LY_EINVAL;
-    }
-
     /* alloc errors */
-    errs = calloc(LY_ARRAY_COUNT(types), sizeof *errs);
+    errs = calloc(LY_ARRAY_COUNT(type_u->types), sizeof *errs);
     LY_CHECK_RET(!errs, LY_EMEM);
 
     /* turn logging temporarily off */
     prev_lo = ly_temp_log_options(&temp_lo);
 
     /* use the first usable subtype to store the value */
-    for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
-        ret = union_store_type(ctx, types[u], subvalue, options, resolve, ctx_node, tree, unres, &e);
+    for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
+        ret = union_store_type(ctx, type_u, u, subvalue, options, resolve, ctx_node, tree, unres, &e);
         if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
             break;
         }
@@ -254,22 +289,26 @@
         errs[u] = e;
     }
 
-    if (u == LY_ARRAY_COUNT(types)) {
+    if (u == LY_ARRAY_COUNT(type_u->types)) {
         /* create the full error */
-        msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
-                (int)subvalue->orig_len, (char *)subvalue->original);
+        if (subvalue->format == LY_VALUE_LYB) {
+            msg_len = asprintf(&msg, "Invalid LYB union value - no matching subtype found:\n");
+        } else {
+            msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
+                    (int)subvalue->orig_len, (char *)subvalue->original);
+        }
         if (msg_len == -1) {
             LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup);
         }
-        for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+        for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
             if (!errs[u]) {
                 /* no error for some reason */
                 continue;
             }
 
-            msg = ly_realloc(msg, msg_len + 4 + strlen(types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
+            msg = ly_realloc(msg, msg_len + 4 + strlen(type_u->types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
             LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
-            msg_len += sprintf(msg + msg_len, "    %s: %s\n", types[u]->plugin->id, errs[u]->msg);
+            msg_len += sprintf(msg + msg_len, "    %s: %s\n", type_u->types[u]->plugin->id, errs[u]->msg);
         }
 
         ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
@@ -278,7 +317,7 @@
     }
 
 cleanup:
-    for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+    for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
         ly_err_free(errs[u]);
     }
     free(errs);
@@ -334,7 +373,7 @@
     }
 
     /* use the specific type to store the value */
-    ret = union_store_type(ctx, type_u->types[type_idx], subvalue, *options, 0, NULL, NULL, unres, err);
+    ret = union_store_type(ctx, type_u, type_idx, subvalue, *options, 0, NULL, NULL, unres, err);
 
     return ret;
 }
@@ -362,7 +401,7 @@
         ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
         LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
     } else {
-        /* Store @p value to subvalue. */
+        /* store value to subvalue */
         ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
         LY_CHECK_GOTO(ret, cleanup);
 
@@ -372,7 +411,7 @@
         LY_CHECK_GOTO(ret, cleanup);
 
         /* use the first usable subtype to store the value */
-        ret = union_find_type(ctx, type_u->types, subvalue, options, 0, NULL, NULL, NULL, unres, err);
+        ret = union_find_type(ctx, type_u, subvalue, options, 0, NULL, NULL, NULL, unres, err);
         LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
     }
 
@@ -399,22 +438,31 @@
     struct lysc_type_union *type_u = (struct lysc_type_union *)type;
     struct lyd_value_union *subvalue = storage->subvalue;
     uint32_t type_idx;
+    ly_bool validated = 0;
 
     *err = NULL;
 
     /* because of types that do not store their own type as realtype (leafref), we are not able to call their
-     * validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so
-     * we have to perform union value storing again from scratch */
+     * validate callback (there is no way to get the type) but even if possible, the value may be invalid
+     * for the type, so we may have to perform union value storing again from scratch */
     subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
 
     if (subvalue->format == LY_VALUE_LYB) {
-        /* use the specific type to store the value */
+        /* use the specific type to store and validate the value */
         lyb_parse_union(subvalue->original, 0, &type_idx, NULL, NULL);
-        ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, 1, ctx_node, tree, NULL, err);
-        LY_CHECK_RET(ret);
-    } else {
+        ret = union_store_type(ctx, type_u, type_idx, subvalue, 0, 1, ctx_node, tree, NULL, err);
+        if (ret) {
+            /* validation failed, we need to try storing the value again */
+            ly_err_free(*err);
+            *err = NULL;
+        } else {
+            validated = 1;
+        }
+    }
+
+    if (!validated) {
         /* use the first usable subtype to store the value */
-        ret = union_find_type(ctx, type_u->types, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
+        ret = union_find_type(ctx, type_u, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
         LY_CHECK_RET(ret);
     }
 
@@ -495,7 +543,7 @@
         ctx = subvalue->ctx_node->module->ctx;
     }
     subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
-    r = union_find_type(ctx, type_u->types, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
+    r = union_find_type(ctx, type_u, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
     ly_err_free(err);
     LY_CHECK_RET((r != LY_SUCCESS) && (r != LY_EINCOMPLETE), NULL);
 
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
index 50593dc..c2541aa 100644
--- a/tests/utests/types/union.c
+++ b/tests/utests/types/union.c
@@ -192,6 +192,7 @@
     const char *schema, *data;
     struct lyd_node *tree;
     char *out;
+    uint32_t uint_val;
 
     schema = MODULE_CREATE_YANG("val",
             "leaf l1 {\n"
@@ -230,6 +231,73 @@
 
     free(out);
     lyd_free_all(tree);
+
+    schema = MODULE_CREATE_YANG("lref",
+            "container test {\n"
+            "    list a {\n"
+            "        key \"name\";\n"
+            "        leaf name {\n"
+            "            type enumeration {\n"
+            "                enum zero;\n"
+            "                enum one;\n"
+            "                enum two;\n"
+            "            }\n"
+            "        }\n"
+            "    }\n"
+            "\n"
+            "    list b {\n"
+            "        key \"name\";\n"
+            "        leaf name {\n"
+            "            type uint32;\n"
+            "        }\n"
+            "    }\n"
+            "\n"
+            "    list community {\n"
+            "        key \"name\";\n"
+            "        leaf name {\n"
+            "            type string;\n"
+            "        }\n"
+            "        leaf view {\n"
+            "            type union {\n"
+            "                type leafref {\n"
+            "                    path \"../../a/name\";\n"
+            "                }\n"
+            "                type leafref {\n"
+            "                    path \"../../b/name\";\n"
+            "                }\n"
+            "            }\n"
+            "        }\n"
+            "    }\n"
+            "}\n");
+    UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+
+    /* parse from LYB #1 */
+    data = "<test xmlns=\"urn:tests:lref\"><b><name>2</name></b><community><name>test</name><view>2</view></community></test>";
+    CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+    assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+    lyd_free_all(tree);
+    CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+    free(out);
+    lyd_free_all(tree);
+
+    /* parse from LYB #2 */
+    data = "<test xmlns=\"urn:tests:lref\"><a><name>one</name></a><community><name>test</name><view>one</view></community></test>";
+    CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+    assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
+    lyd_free_all(tree);
+    CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+    free(out);
+
+    /* remove the target and create another, which is represented the same way in LYB */
+    lyd_free_tree(lyd_child(tree));
+    uint_val = 1;
+    assert_int_equal(LY_SUCCESS, lyd_new_list(tree, NULL, "b", LYD_NEW_VAL_BIN, NULL, &uint_val, sizeof uint_val));
+    assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
+    CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n"
+            "    libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n"
+            "    libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0);
+
+    lyd_free_all(tree);
 }
 
 int