Skip to content

Commit f976324

Browse files
committed
Fix multiple must deviations to same node
A segmentation fault can occur when multiple deviations add must deviations to the same target node. The sequence of events is as follows: 1. Some number of must deviations have already been parsed, and the module's deviation array and the target node are populated to point an array of `struct lys_restr`. 2. An additional must deviation is parsed, deviating the same target node, and the `ly_realloc()` in `yang_check_deviate()` (for YANG parsing) or `yin_fill_deviation()` (for YIN parsing) cannot extend the existing buffer any further, and so it frees the original buffer and allocates a new, larger buffer. 3. Any existing must deviations on the module that point to that same target node now point to memory freed by `ly_realloc()`. Attempts to use that memory will result in undefined behavior, including a segmentation fault. I've added some additional leafs and must deviations to those leafs in the test models that demonstrate this problem. `test_parse_print` and `test_deviation` will crash with a segmentation fault given these models. (Unrelated to these changes, I also updated leafs 9-10 of all.yang to match all.yin. These nodes were changed in all.yin in c27114c, but all.yang was never updated to match.) I've also implemented a possible fix. If the deviation is an `add`, the module's deviation now contains a shallow copy of the `struct lys_restr` array pointed to by the target node, instead of them pointing to the same memory. (This meant I had to add a shallow free of this copy when the module was cleaned up. See `lys_sub_module_remove_devs_augs()`.) Another possible solution, which I did not implement, might be for the parser to revisit all siblings of the module's deviations when `ly_realloc()` freed the original memory. We would also have to keep track of what offset each module deviation pointed to within the larger target node array.
1 parent 0e701c1 commit f976324

File tree

10 files changed

+125
-12
lines changed

10 files changed

+125
-12
lines changed

src/parser_yang.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,8 +1878,6 @@ yang_check_deviate_must(struct lys_module *module, struct unres_schema *unres,
18781878
LY_CHECK_ERR_GOTO(!must, LOGMEM(ctx), error);
18791879
*trg_must = must;
18801880
memcpy(&(*trg_must)[*trg_must_size], deviate->must, deviate->must_size * sizeof *must);
1881-
free(deviate->must);
1882-
deviate->must = &must[*trg_must_size];
18831881
*trg_must_size = *trg_must_size + deviate->must_size;
18841882
erase_must = 0;
18851883
} else if (deviate->mod == LY_DEVIATE_DEL) {

src/parser_yin.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,7 @@ fill_yin_deviation(struct lys_module *module, struct lyxml_elem *yin, struct lys
21862186
struct unres_schema tmp_unres;
21872187
struct lys_module *mod;
21882188
void *reallocated;
2189+
size_t deviate_must_index;
21892190

21902191
GETVAL(ctx, value, yin, "target-node");
21912192
dev->target_name = transform_schema2json(module, value);
@@ -2727,10 +2728,10 @@ fill_yin_deviation(struct lys_module *module, struct lyxml_elem *yin, struct lys
27272728
goto error;
27282729
} else if (d->mod == LY_DEVIATE_ADD) {
27292730
/* reallocate the must array of the target */
2730-
d->must = ly_realloc(*trg_must, (c_must + *trg_must_size) * sizeof *d->must);
2731-
LY_CHECK_ERR_GOTO(!d->must, LOGMEM(ctx), error);
2732-
*trg_must = d->must;
2733-
d->must = &((*trg_must)[*trg_must_size]);
2731+
struct lys_restr *must = ly_realloc(*trg_must, (c_must + *trg_must_size) * sizeof *d->must);
2732+
LY_CHECK_ERR_GOTO(!must, LOGMEM(ctx), error);
2733+
*trg_must = must;
2734+
d->must = calloc(c_must, sizeof *d->must);
27342735
d->must_size = c_must;
27352736
} else { /* LY_DEVIATE_DEL */
27362737
d->must = calloc(c_must, sizeof *d->must);
@@ -2824,6 +2825,7 @@ fill_yin_deviation(struct lys_module *module, struct lyxml_elem *yin, struct lys
28242825
}
28252826

28262827
/* process deviation properties with 0..n cardinality */
2828+
deviate_must_index = 0;
28272829
LY_TREE_FOR_SAFE(develem->child, next2, child) {
28282830
if (strcmp(child->ns->value, LY_NSYIN)) {
28292831
/* extension */
@@ -2879,6 +2881,8 @@ fill_yin_deviation(struct lys_module *module, struct lyxml_elem *yin, struct lys
28792881
if (fill_yin_must(module, child, &((*trg_must)[*trg_must_size]), unres)) {
28802882
goto error;
28812883
}
2884+
memcpy(d->must + deviate_must_index, &((*trg_must)[*trg_must_size]), sizeof *d->must);
2885+
++deviate_must_index;
28822886
(*trg_must_size)++;
28832887
}
28842888

src/tree_schema.c

100644100755
Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4702,7 +4702,7 @@ lys_sub_module_apply_devs_augs(struct lys_module *module)
47024702
void
47034703
lys_sub_module_remove_devs_augs(struct lys_module *module)
47044704
{
4705-
uint8_t u, v;
4705+
uint8_t u, v, w;
47064706
struct unres_schema *unres;
47074707

47084708
unres = calloc(1, sizeof *unres);
@@ -4714,6 +4714,14 @@ lys_sub_module_remove_devs_augs(struct lys_module *module)
47144714
if (module->deviation[u].orig_node) {
47154715
remove_dev(&module->deviation[u], module, unres);
47164716
}
4717+
4718+
/* Free the deviation's must array(s). These are shallow copies of the arrays
4719+
on the target node(s), so a deep free is not needed. */
4720+
for (v = 0; v < module->deviation[u].deviate_size; ++v) {
4721+
if (module->deviation[u].deviate[v].mod == LY_DEVIATE_ADD) {
4722+
free(module->deviation[u].deviate[v].must);
4723+
}
4724+
}
47174725
}
47184726
/* remove applied augments */
47194727
for (u = 0; u < module->augment_size; ++u) {
@@ -4726,6 +4734,14 @@ lys_sub_module_remove_devs_augs(struct lys_module *module)
47264734
if (module->inc[v].submodule->deviation[u].orig_node) {
47274735
remove_dev(&module->inc[v].submodule->deviation[u], module, unres);
47284736
}
4737+
4738+
/* Free the deviation's must array(s). These are shallow copies of the arrays
4739+
on the target node(s), so a deep free is not needed. */
4740+
for (w = 0; w < module->inc[v].submodule->deviation[u].deviate_size; ++w) {
4741+
if (module->inc[v].submodule->deviation[u].deviate[w].mod == LY_DEVIATE_ADD) {
4742+
free(module->inc[v].submodule->deviation[u].deviate[w].must);
4743+
}
4744+
}
47294745
}
47304746

47314747
for (u = 0; u < module->inc[v].submodule->augment_size; ++u) {

tests/data/files/all-dev.yang

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,18 @@ module all-dev {
3939
deviation "/all_mod:cont1/all_mod:leaf23" {
4040
deviate not-supported;
4141
}
42+
43+
deviation "/all_mod:cont1/all_mod:must-deviations-container" {
44+
deviate add {
45+
must "all_mod:leaf30 != 'foo'";
46+
}
47+
}
48+
49+
deviation "/all_mod:cont1/all_mod:must-deviations-container" {
50+
deviate add {
51+
must "all_mod:leaf30 != 'bar'";
52+
must "all_mod:leaf30 != 'baz'";
53+
must "all_mod:leaf30 != 'qux'";
54+
}
55+
}
4256
}

tests/data/files/all-dev.yin

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,16 @@
3838
<deviation target-node="/all_mod:cont1/all_mod:leaf23">
3939
<deviate value="not-supported"/>
4040
</deviation>
41+
<deviation target-node="/all_mod:cont1/all_mod:must-deviations-container">
42+
<deviate value="add">
43+
<must condition="all_mod:leaf30 != 'foo'"/>
44+
</deviate>
45+
</deviation>
46+
<deviation target-node="/all_mod:cont1/all_mod:must-deviations-container">
47+
<deviate value="add">
48+
<must condition="all_mod:leaf30 != 'bar'"/>
49+
<must condition="all_mod:leaf30 != 'baz'"/>
50+
<must condition="all_mod:leaf30 != 'qux'"/>
51+
</deviate>
52+
</deviation>
4153
</module>

tests/data/files/all.yang

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,23 @@ module all {
100100
}
101101

102102
choice choic1 {
103-
default "leaf10";
104-
leaf leaf9 {
103+
default "leaf9b";
104+
leaf leaf9a {
105105
type decimal64 {
106106
fraction-digits 9;
107107
}
108108
}
109109

110-
leaf leaf10 {
110+
leaf leaf9b {
111111
type boolean;
112112
default "false";
113113
}
114114
}
115115

116+
leaf leaf10 {
117+
type boolean;
118+
}
119+
116120
leaf leaf11 {
117121
type enumeration {
118122
enum "one";
@@ -218,6 +222,13 @@ module all {
218222
type instance-identifier;
219223
}
220224

225+
container must-deviations-container {
226+
presence "Allows deviations on the leaf";
227+
leaf leaf30 {
228+
type string;
229+
}
230+
}
231+
221232
leaf leaf23 {
222233
type empty;
223234
}

tests/data/files/all.yin

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@
191191
<leaf name="leaf29">
192192
<type name="instance-identifier"/>
193193
</leaf>
194+
<container name="must-deviations-container">
195+
<presence value="Allows deviations on the leaf"/>
196+
<leaf name="leaf30">
197+
<type name="string"/>
198+
</leaf>
199+
</container>
194200
<leaf name="leaf23">
195201
<type name="empty"/>
196202
</leaf>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
submodule deviation1-dv-sub {
2+
belongs-to deviation1-dv {
3+
prefix dev1-dv;
4+
}
5+
6+
import deviation1 {
7+
prefix dev1;
8+
}
9+
10+
deviation "/dev1:deviation1/dev1:cont" {
11+
description "An additional must deviation.";
12+
deviate add {
13+
must "dev1:cont-leaf-2 != 'quux'";
14+
}
15+
}
16+
}

tests/schema/yang/files/deviation1-dv.yang

100644100755
Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module deviation1-dv{
33
namespace "urn:fri:params:xml:ns:yang:deviation1-dv";
44
prefix dev1-dv;
55

6+
include deviation1-dv-sub;
7+
68
import deviation1 {
79
prefix dev1;
810
}
@@ -11,7 +13,7 @@ module deviation1-dv{
1113
"This module contains a deviation which caused a double free crash...";
1214

1315
deviation "/dev1:deviation1/dev1:cont"
14-
+ "/dev1:cont-leaf" {
16+
+ "/dev1:cont-leaf-1" {
1517
description
1618
"Restrict the values for the c-tag cont to only allow 0x8100
1719
(33024) and 0x88A8 (34984).";
@@ -24,4 +26,32 @@ module deviation1-dv{
2426
}
2527
}
2628
}
29+
30+
deviation "/dev1:deviation1/dev1:cont" {
31+
description "An additional must deviation.";
32+
deviate add {
33+
must "dev1:cont-leaf-2 != 'foo'";
34+
}
35+
}
36+
37+
deviation "/dev1:deviation1/dev1:cont" {
38+
description "An additional must deviation.";
39+
deviate add {
40+
must "dev1:cont-leaf-2 != 'bar'";
41+
}
42+
}
43+
44+
deviation "/dev1:deviation1/dev1:cont" {
45+
description "An additional must deviation.";
46+
deviate add {
47+
must "dev1:cont-leaf-2 != 'baz'";
48+
}
49+
}
50+
51+
deviation "/dev1:deviation1/dev1:cont" {
52+
description "An additional must deviation.";
53+
deviate add {
54+
must "dev1:cont-leaf-2 != 'qux'";
55+
}
56+
}
2757
}

tests/schema/yang/files/deviation1.yang

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ module deviation1 {
1616
container cont {
1717
description "Just a container.";
1818

19-
leaf cont-leaf {
19+
leaf cont-leaf-1 {
2020
if-feature a-feature;
2121
type uint16;
2222
default 0x8100;
2323
description
2424
"A container leaf.";
2525
}
26+
27+
leaf cont-leaf-2 {
28+
type string;
29+
description
30+
"A second container leaf.";
31+
}
2632
}
2733
}
2834
}

0 commit comments

Comments
 (0)