Skip to content

Commit cf43fa7

Browse files
committed
union BUGFIX proper LYB value validation
Fixes #1560
1 parent 6845e81 commit cf43fa7

File tree

2 files changed

+163
-47
lines changed

2 files changed

+163
-47
lines changed

src/plugins_types/union.c

Lines changed: 95 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -151,64 +151,103 @@ lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, c
151151
}
152152

153153
/**
154-
* @brief Store subvalue as a specific type.
154+
* @brief Store (and validate) subvalue as a specific type.
155155
*
156156
* @param[in] ctx libyang context.
157-
* @param[in] type Specific union type to use for storing.
158-
* @param[in] subvalue Union subvalue structure.
157+
* @param[in] type_u Union type.
158+
* @param[in] type_idx Union type index to use for storing (and validating).
159+
* @param[in,out] subvalue Union subvalue structure, its value needs to be filled.
159160
* @param[in] options The store options.
160-
* @param[in] resolve Whether the value needs to be resolved (validated by a callback).
161+
* @param[in] validate Whether the value needs to be validated.
161162
* @param[in] ctx_node Context node for prefix resolution.
162163
* @param[in] tree Data tree for resolving (validation).
163164
* @param[in,out] unres Global unres structure.
164165
* @param[out] err Error information on error.
165166
* @return LY_ERR value.
166167
*/
167168
static LY_ERR
168-
union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue, uint32_t options,
169-
ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
170-
struct ly_err_item **err)
169+
union_store_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, uint32_t type_idx, struct lyd_value_union *subvalue,
170+
uint32_t options, ly_bool validate, const struct lyd_node *ctx_node, const struct lyd_node *tree,
171+
struct lys_glob_unres *unres, struct ly_err_item **err)
171172
{
172-
LY_ERR ret;
173+
LY_ERR rc = LY_SUCCESS;
174+
struct lysc_type *type = type_u->types[type_idx];
173175
const void *value = NULL;
174176
size_t value_len = 0;
175-
uint32_t opts;
177+
ly_bool dynamic = 0;
178+
LY_VALUE_FORMAT format;
179+
void *prefix_data;
180+
uint32_t opts = 0, ti;
176181

177182
*err = NULL;
178183

179184
if (subvalue->format == LY_VALUE_LYB) {
180-
lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
185+
lyb_parse_union(subvalue->original, subvalue->orig_len, &ti, &value, &value_len);
186+
if (ti != type_idx) {
187+
/* value of another type, first store the value properly and then use its JSON value for parsing */
188+
rc = type_u->types[ti]->plugin->store(ctx, type_u->types[ti], value, value_len, LYPLG_TYPE_STORE_ONLY,
189+
subvalue->format, subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err);
190+
if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
191+
/* clear any leftover/freed garbage */
192+
memset(&subvalue->value, 0, sizeof subvalue->value);
193+
return rc;
194+
}
195+
196+
assert(subvalue->value.realtype);
197+
value = subvalue->value.realtype->plugin->print(ctx, &subvalue->value, LY_VALUE_JSON, NULL, &dynamic, &value_len);
198+
199+
/* to avoid leaks, free subvalue->value, but we need the value, which may be stored there */
200+
if (!dynamic) {
201+
value = strndup(value, value_len);
202+
dynamic = 1;
203+
}
204+
type_u->types[ti]->plugin->free(ctx, &subvalue->value);
205+
206+
format = LY_VALUE_JSON;
207+
prefix_data = NULL;
208+
} else {
209+
format = subvalue->format;
210+
prefix_data = subvalue->prefix_data;
211+
}
181212
} else {
182213
value = subvalue->original;
183214
value_len = subvalue->orig_len;
215+
format = subvalue->format;
216+
prefix_data = subvalue->prefix_data;
217+
}
218+
219+
if (options & LYPLG_TYPE_STORE_ONLY) {
220+
opts |= LYPLG_TYPE_STORE_ONLY;
221+
}
222+
if (dynamic) {
223+
opts |= LYPLG_TYPE_STORE_DYNAMIC;
184224
}
185225

186-
opts = (options & LYPLG_TYPE_STORE_ONLY) ? LYPLG_TYPE_STORE_ONLY : 0;
187-
ret = type->plugin->store(ctx, type, value, value_len, opts, subvalue->format, subvalue->prefix_data, subvalue->hints,
226+
rc = type->plugin->store(ctx, type, value, value_len, opts, format, prefix_data, subvalue->hints,
188227
subvalue->ctx_node, &subvalue->value, unres, err);
189-
if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
228+
if ((rc != LY_SUCCESS) && (rc != LY_EINCOMPLETE)) {
190229
/* clear any leftover/freed garbage */
191230
memset(&subvalue->value, 0, sizeof subvalue->value);
192-
return ret;
231+
return rc;
193232
}
194233

195-
if (resolve && (ret == LY_EINCOMPLETE)) {
196-
/* we need the value resolved */
197-
ret = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
198-
if (ret) {
199-
/* resolve failed, we need to free the stored value */
234+
if (validate && (rc == LY_EINCOMPLETE)) {
235+
/* we need the value validated */
236+
rc = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
237+
if (rc) {
238+
/* validate failed, we need to free the stored value */
200239
type->plugin->free(ctx, &subvalue->value);
201240
}
202241
}
203242

204-
return ret;
243+
return rc;
205244
}
206245

207246
/**
208247
* @brief Find the first valid type for a union value.
209248
*
210249
* @param[in] ctx libyang context.
211-
* @param[in] types Sized array of union types.
250+
* @param[in] type_u Union type.
212251
* @param[in] subvalue Union subvalue structure.
213252
* @param[in] options The store options.
214253
* @param[in] resolve Whether the value needs to be resolved (validated by a callback).
@@ -220,7 +259,7 @@ union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_va
220259
* @return LY_ERR value.
221260
*/
222261
static LY_ERR
223-
union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
262+
union_find_type(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct lyd_value_union *subvalue,
224263
uint32_t options, ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree,
225264
uint32_t *type_idx, struct lys_glob_unres *unres, struct ly_err_item **err)
226265
{
@@ -233,43 +272,43 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
233272

234273
*err = NULL;
235274

236-
if (!types || !LY_ARRAY_COUNT(types)) {
237-
return LY_EINVAL;
238-
}
239-
240275
/* alloc errors */
241-
errs = calloc(LY_ARRAY_COUNT(types), sizeof *errs);
276+
errs = calloc(LY_ARRAY_COUNT(type_u->types), sizeof *errs);
242277
LY_CHECK_RET(!errs, LY_EMEM);
243278

244279
/* turn logging temporarily off */
245280
prev_lo = ly_temp_log_options(&temp_lo);
246281

247282
/* use the first usable subtype to store the value */
248-
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
249-
ret = union_store_type(ctx, types[u], subvalue, options, resolve, ctx_node, tree, unres, &e);
283+
for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
284+
ret = union_store_type(ctx, type_u, u, subvalue, options, resolve, ctx_node, tree, unres, &e);
250285
if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
251286
break;
252287
}
253288

254289
errs[u] = e;
255290
}
256291

257-
if (u == LY_ARRAY_COUNT(types)) {
292+
if (u == LY_ARRAY_COUNT(type_u->types)) {
258293
/* create the full error */
259-
msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
260-
(int)subvalue->orig_len, (char *)subvalue->original);
294+
if (subvalue->format == LY_VALUE_LYB) {
295+
msg_len = asprintf(&msg, "Invalid LYB union value - no matching subtype found:\n");
296+
} else {
297+
msg_len = asprintf(&msg, "Invalid union value \"%.*s\" - no matching subtype found:\n",
298+
(int)subvalue->orig_len, (char *)subvalue->original);
299+
}
261300
if (msg_len == -1) {
262301
LY_CHECK_ERR_GOTO(!errs, ret = LY_EMEM, cleanup);
263302
}
264-
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
303+
for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
265304
if (!errs[u]) {
266305
/* no error for some reason */
267306
continue;
268307
}
269308

270-
msg = ly_realloc(msg, msg_len + 4 + strlen(types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
309+
msg = ly_realloc(msg, msg_len + 4 + strlen(type_u->types[u]->plugin->id) + 2 + strlen(errs[u]->msg) + 2);
271310
LY_CHECK_ERR_GOTO(!msg, ret = LY_EMEM, cleanup);
272-
msg_len += sprintf(msg + msg_len, " %s: %s\n", types[u]->plugin->id, errs[u]->msg);
311+
msg_len += sprintf(msg + msg_len, " %s: %s\n", type_u->types[u]->plugin->id, errs[u]->msg);
273312
}
274313

275314
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "%s", msg);
@@ -278,7 +317,7 @@ union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_v
278317
}
279318

280319
cleanup:
281-
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
320+
for (u = 0; u < LY_ARRAY_COUNT(type_u->types); ++u) {
282321
ly_err_free(errs[u]);
283322
}
284323
free(errs);
@@ -334,7 +373,7 @@ lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, cons
334373
}
335374

336375
/* use the specific type to store the value */
337-
ret = union_store_type(ctx, type_u->types[type_idx], subvalue, *options, 0, NULL, NULL, unres, err);
376+
ret = union_store_type(ctx, type_u, type_idx, subvalue, *options, 0, NULL, NULL, unres, err);
338377

339378
return ret;
340379
}
@@ -362,7 +401,7 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
362401
ret = lyb_fill_subvalue(ctx, type_u, value, value_len, prefix_data, subvalue, &options, unres, err);
363402
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
364403
} else {
365-
/* Store @p value to subvalue. */
404+
/* store value to subvalue */
366405
ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
367406
LY_CHECK_GOTO(ret, cleanup);
368407

@@ -372,7 +411,7 @@ lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, c
372411
LY_CHECK_GOTO(ret, cleanup);
373412

374413
/* use the first usable subtype to store the value */
375-
ret = union_find_type(ctx, type_u->types, subvalue, options, 0, NULL, NULL, NULL, unres, err);
414+
ret = union_find_type(ctx, type_u, subvalue, options, 0, NULL, NULL, NULL, unres, err);
376415
LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
377416
}
378417

@@ -399,22 +438,31 @@ lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type
399438
struct lysc_type_union *type_u = (struct lysc_type_union *)type;
400439
struct lyd_value_union *subvalue = storage->subvalue;
401440
uint32_t type_idx;
441+
ly_bool validated = 0;
402442

403443
*err = NULL;
404444

405445
/* because of types that do not store their own type as realtype (leafref), we are not able to call their
406-
* validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so
407-
* we have to perform union value storing again from scratch */
446+
* validate callback (there is no way to get the type) but even if possible, the value may be invalid
447+
* for the type, so we may have to perform union value storing again from scratch */
408448
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
409449

410450
if (subvalue->format == LY_VALUE_LYB) {
411-
/* use the specific type to store the value */
451+
/* use the specific type to store and validate the value */
412452
lyb_parse_union(subvalue->original, 0, &type_idx, NULL, NULL);
413-
ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, 1, ctx_node, tree, NULL, err);
414-
LY_CHECK_RET(ret);
415-
} else {
453+
ret = union_store_type(ctx, type_u, type_idx, subvalue, 0, 1, ctx_node, tree, NULL, err);
454+
if (ret) {
455+
/* validation failed, we need to try storing the value again */
456+
ly_err_free(*err);
457+
*err = NULL;
458+
} else {
459+
validated = 1;
460+
}
461+
}
462+
463+
if (!validated) {
416464
/* use the first usable subtype to store the value */
417-
ret = union_find_type(ctx, type_u->types, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
465+
ret = union_find_type(ctx, type_u, subvalue, 0, 1, ctx_node, tree, NULL, NULL, err);
418466
LY_CHECK_RET(ret);
419467
}
420468

@@ -495,7 +543,7 @@ lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct
495543
ctx = subvalue->ctx_node->module->ctx;
496544
}
497545
subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
498-
r = union_find_type(ctx, type_u->types, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
546+
r = union_find_type(ctx, type_u, subvalue, 0, 0, NULL, NULL, &type_idx, NULL, &err);
499547
ly_err_free(err);
500548
LY_CHECK_RET((r != LY_SUCCESS) && (r != LY_EINCOMPLETE), NULL);
501549

tests/utests/types/union.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ test_validation(void **state)
192192
const char *schema, *data;
193193
struct lyd_node *tree;
194194
char *out;
195+
uint32_t uint_val;
195196

196197
schema = MODULE_CREATE_YANG("val",
197198
"leaf l1 {\n"
@@ -230,6 +231,73 @@ test_validation(void **state)
230231

231232
free(out);
232233
lyd_free_all(tree);
234+
235+
schema = MODULE_CREATE_YANG("lref",
236+
"container test {\n"
237+
" list a {\n"
238+
" key \"name\";\n"
239+
" leaf name {\n"
240+
" type enumeration {\n"
241+
" enum zero;\n"
242+
" enum one;\n"
243+
" enum two;\n"
244+
" }\n"
245+
" }\n"
246+
" }\n"
247+
"\n"
248+
" list b {\n"
249+
" key \"name\";\n"
250+
" leaf name {\n"
251+
" type uint32;\n"
252+
" }\n"
253+
" }\n"
254+
"\n"
255+
" list community {\n"
256+
" key \"name\";\n"
257+
" leaf name {\n"
258+
" type string;\n"
259+
" }\n"
260+
" leaf view {\n"
261+
" type union {\n"
262+
" type leafref {\n"
263+
" path \"../../a/name\";\n"
264+
" }\n"
265+
" type leafref {\n"
266+
" path \"../../b/name\";\n"
267+
" }\n"
268+
" }\n"
269+
" }\n"
270+
" }\n"
271+
"}\n");
272+
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
273+
274+
/* parse from LYB #1 */
275+
data = "<test xmlns=\"urn:tests:lref\"><b><name>2</name></b><community><name>test</name><view>2</view></community></test>";
276+
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
277+
assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
278+
lyd_free_all(tree);
279+
CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
280+
free(out);
281+
lyd_free_all(tree);
282+
283+
/* parse from LYB #2 */
284+
data = "<test xmlns=\"urn:tests:lref\"><a><name>one</name></a><community><name>test</name><view>one</view></community></test>";
285+
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
286+
assert_int_equal(LY_SUCCESS, lyd_print_mem(&out, tree, LYD_LYB, LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS));
287+
lyd_free_all(tree);
288+
CHECK_PARSE_LYD_PARAM(out, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
289+
free(out);
290+
291+
/* remove the target and create another, which is represented the same way in LYB */
292+
lyd_free_tree(lyd_child(tree));
293+
uint_val = 1;
294+
assert_int_equal(LY_SUCCESS, lyd_new_list(tree, NULL, "b", LYD_NEW_VAL_BIN, NULL, &uint_val, sizeof uint_val));
295+
assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
296+
CHECK_LOG_CTX("Invalid LYB union value - no matching subtype found:\n"
297+
" libyang 2 - leafref, version 1: Invalid leafref value \"one\" - no target instance \"../../a/name\" with the same value.\n"
298+
" libyang 2 - leafref, version 1: Invalid type uint32 value \"one\".\n", "/lref:test/community[name='test']/view", 0);
299+
300+
lyd_free_all(tree);
233301
}
234302

235303
int

0 commit comments

Comments
 (0)