Skip to content

Commit 29b284f

Browse files
committed
Fix initialization of multiple sub-members within a union
The attached test shows that we could reach recursive initialisation of unions with a byte_update instead of a union expression, which was added in a8592a7. That previous fix did not consider the case of multiple sub-members being initialised. The test was constructed using creduce, starting from code included in the Linux kernel.
1 parent 4889328 commit 29b284f

File tree

3 files changed

+54
-10
lines changed

3 files changed

+54
-10
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
union {
2+
int a;
3+
struct
4+
{
5+
unsigned b : 4;
6+
unsigned c : 4;
7+
};
8+
} u1 = {.b = 5, .c = 1};
9+
10+
#ifndef _MSC_VER
11+
union {
12+
int;
13+
struct
14+
{
15+
unsigned b : 4;
16+
unsigned c : 4;
17+
};
18+
} u2 = {.b = 5, .c = 1};
19+
#endif
20+
21+
int main()
22+
{
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
Invariant check failed
8+
--
9+
This test previously failed an internal invariant:
10+
File: ../src/ansi-c/c_typecheck_initializer.cpp:517 function: do_designated_initializer
11+
Condition: dest->id() == ID_union
12+
Reason: union should be zero initialized

src/ansi-c/c_typecheck_initializer.cpp

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -514,22 +514,22 @@ exprt::operandst::const_iterator c_typecheck_baset::do_designated_initializer(
514514

515515
const union_typet::componentt &component = components[index];
516516

517-
DATA_INVARIANT(
518-
dest->id() == ID_union, "union should be zero initialized");
519-
520-
if(dest->get(ID_component_name) == component.get_name())
517+
if(
518+
dest->id() == ID_union &&
519+
to_union_expr(*dest).get_component_name() == component.get_name())
521520
{
522521
// Already right union component. We can initialize multiple submembers,
523522
// so do not overwrite this.
524523
dest = &(to_union_expr(*dest).op());
525524
}
526-
else
525+
else if(dest->id() == ID_union)
527526
{
528-
// The first component is not the maximum member, which the (default)
529-
// zero initializer prepared. Replace this by a component-specific
530-
// initializer; other bytes have an unspecified value (C Standard
531-
// 6.2.6.1(7)). In practice, objects of static lifetime are fully zero
532-
// initialized.
527+
// The designated initializer does not initialize the maximum member,
528+
// which the (default) zero initializer prepared. Replace this by a
529+
// component-specific initializer; other bytes have an unspecified value
530+
// (C Standard 6.2.6.1(7)). In practice, objects of static lifetime are
531+
// fully zero initialized, so we just byte-update on top of the existing
532+
// zero initializer.
533533
const auto zero =
534534
zero_initializer(component.type(), value.source_location(), *this);
535535
if(!zero.has_value())
@@ -556,6 +556,15 @@ exprt::operandst::const_iterator c_typecheck_baset::do_designated_initializer(
556556
dest = &(to_union_expr(*dest).op());
557557
}
558558
}
559+
else if(
560+
dest->id() == ID_byte_update_big_endian ||
561+
dest->id() == ID_byte_update_little_endian)
562+
{
563+
// handle the byte update introduced by an earlier initializer (if
564+
// current_symbol.is_static_lifetime)
565+
byte_update_exprt &byte_update = to_byte_update_expr(*dest);
566+
dest = &byte_update.op2();
567+
}
559568
}
560569
else
561570
UNREACHABLE;

0 commit comments

Comments
 (0)