Skip to content

Commit 7787136

Browse files
authored
[BUG] Anonymous union initialized using 'from_bytes' in some cases #447 (#467)
1 parent d98a8b1 commit 7787136

File tree

6 files changed

+147
-58
lines changed

6 files changed

+147
-58
lines changed

server/src/Tests.cpp

+85-16
Original file line numberDiff line numberDiff line change
@@ -252,33 +252,81 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
252252
size_t offsetInBits,
253253
types::PointerUsage usage) {
254254
std::vector<InitReference> tmpInitReferences;
255-
return structView(byteArray, curStruct, offsetInBits, usage, {}, "", {}, tmpInitReferences);
255+
return structView(byteArray, curStruct, offsetInBits, usage, {}, false, "", {}, tmpInitReferences);
256256
}
257257

258258
std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector<char> &byteArray,
259259
const StructInfo &curStruct,
260260
size_t offsetInBits,
261261
PointerUsage usage,
262262
const std::optional<const Tests::MethodDescription> &testingMethod,
263+
const bool anonymousField,
263264
const std::string &name,
264265
const MapAddressName &fromAddressToName,
265266
std::vector<InitReference> &initReferences) {
266267
std::vector<std::shared_ptr<AbstractValueView>> subViews;
267268

269+
size_t fieldIndexToInitUnion = SIZE_MAX;
270+
size_t sizeOfFieldToInitUnion = 0;
271+
size_t prevFieldEndOffset = offsetInBits;
272+
size_t structEndOffset = offsetInBits + curStruct.size;
273+
size_t fieldIndex = 0;
274+
bool dirtyInitializedStruct = false;
268275
for (const auto &field: curStruct.fields) {
276+
bool dirtyInitializedField = false;
269277
size_t fieldLen = typesHandler.typeSize(field.type);
270-
size_t fieldOffset = offsetInBits + field.offset;
278+
size_t fieldStartOffset = offsetInBits + field.offset;
279+
size_t fieldEndOffset = fieldStartOffset + fieldLen;
280+
if (curStruct.subType == types::SubType::Union) {
281+
prevFieldEndOffset = offsetInBits;
282+
}
283+
284+
auto dirtyCheck = [&](int i) {
285+
if (i >= byteArray.size()) {
286+
LOG_S(ERROR) << "Bad type size info: " << field.name << " index: " << fieldIndex;
287+
} else if (byteArray[i] == 0) {
288+
return false;
289+
}
290+
// the field cannot init the union in this state
291+
dirtyInitializedField = true;
292+
return true;
293+
};
294+
295+
if (prevFieldEndOffset < fieldStartOffset) {
296+
// check an alignment gap
297+
for (int i = prevFieldEndOffset/8; i < fieldStartOffset/8; ++i) {
298+
if (dirtyCheck(i)) {
299+
break;
300+
}
301+
}
302+
}
303+
if (!dirtyInitializedField && curStruct.subType == types::SubType::Union) {
304+
// check the rest of the union
305+
for (int i = fieldEndOffset/8; i < structEndOffset/8; ++i) {
306+
if (dirtyCheck(i)) {
307+
break;
308+
}
309+
}
310+
}
271311

272312
switch (typesHandler.getTypeKind(field.type)) {
273313
case TypeKind::STRUCT_LIKE:
274-
subViews.push_back(structView(byteArray, typesHandler.getStructInfo(field.type), fieldOffset, usage, testingMethod,
275-
PrinterUtils::getFieldAccess(name, field), fromAddressToName, initReferences));
314+
{
315+
auto sv = structView(byteArray, typesHandler.getStructInfo(field.type),
316+
fieldStartOffset, usage, testingMethod, field.anonymous,
317+
PrinterUtils::getFieldAccess(name, field), fromAddressToName,
318+
initReferences);
319+
dirtyInitializedField |= sv->isDirtyInit();
320+
subViews.push_back(sv);
321+
}
276322
break;
277323
case TypeKind::ENUM:
278-
subViews.push_back(enumView(byteArray, typesHandler.getEnumInfo(field.type), fieldOffset, fieldLen));
324+
subViews.push_back(enumView(byteArray, typesHandler.getEnumInfo(field.type),
325+
fieldStartOffset, fieldLen));
279326
break;
280327
case TypeKind::PRIMITIVE:
281-
subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), fieldOffset,
328+
subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(),
329+
fieldStartOffset,
282330
std::min(field.size, fieldLen)));
283331
break;
284332
case TypeKind::ARRAY: {
@@ -296,21 +344,22 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
296344
}
297345
if (onlyArrays) {
298346
size *= typesHandler.typeSize(field.type.baseTypeObj());
299-
subViews.push_back(multiArrayView(byteArray, field.type, size,
300-
fieldOffset, usage));
347+
subViews.push_back(multiArrayView(byteArray, field.type, size, fieldStartOffset, usage));
301348
} else {
302349
std::vector<std::shared_ptr<AbstractValueView>> nullViews(
303350
size, std::make_shared<JustValueView>(PrinterUtils::C_NULL));
304351
subViews.push_back(std::make_shared<ArrayValueView>(nullViews));
305352
}
306353
} else {
307-
auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen, fieldOffset, usage);
354+
auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen,
355+
fieldStartOffset, usage);
308356
subViews.push_back(view);
309357
}
310358
}
311359
break;
312360
case TypeKind::OBJECT_POINTER: {
313-
std::string res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset,
361+
std::string res = readBytesAsValueForType(byteArray, PointerWidthType,
362+
fieldStartOffset,
314363
PointerWidthSizeInBits);
315364
subViews.push_back(getLazyPointerView(fromAddressToName, initReferences,
316365
PrinterUtils::getFieldAccess(name, field), res, field.type));
@@ -330,15 +379,35 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
330379
LOG_S(ERROR) << message;
331380
throw NoSuchTypeException(message);
332381
}
382+
383+
if (!dirtyInitializedField && sizeOfFieldToInitUnion < fieldLen) {
384+
fieldIndexToInitUnion = fieldIndex;
385+
sizeOfFieldToInitUnion = fieldLen;
386+
} else {
387+
dirtyInitializedStruct = true;
388+
}
389+
prevFieldEndOffset = fieldEndOffset;
390+
++fieldIndex;
333391
}
334392

335393
std::optional<std::string> entryValue;
336-
if (curStruct.hasAnonymousStructOrUnion) {
337-
auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte");
338-
const std::shared_ptr<AbstractValueView> rawDataView = arrayView(byteArray, bytesType, curStruct.size, offsetInBits, usage);
339-
entryValue = PrinterUtils::convertBytesToUnion(curStruct.name, rawDataView->getEntryValue(nullptr));
394+
if (curStruct.subType == types::SubType::Union) {
395+
if (fieldIndexToInitUnion == SIZE_MAX && !curStruct.name.empty()) {
396+
// init by memory copy
397+
entryValue = PrinterUtils::convertBytesToUnion(
398+
curStruct.name,
399+
arrayView(byteArray,
400+
types::Type::createSimpleTypeFromName("utbot_byte"),
401+
curStruct.size,
402+
offsetInBits, usage)->getEntryValue(nullptr));
403+
dirtyInitializedStruct = false;
404+
}
405+
if (fieldIndexToInitUnion != SIZE_MAX) {
406+
dirtyInitializedStruct = false;
407+
}
340408
}
341-
return std::make_shared<StructValueView>(curStruct, subViews, entryValue);
409+
return std::make_shared<StructValueView>(curStruct, subViews, entryValue,
410+
anonymousField, dirtyInitializedStruct, fieldIndexToInitUnion);
342411
}
343412

344413
std::string KTestObjectParser::primitiveCharView(const types::Type &type, std::string value) {
@@ -962,7 +1031,7 @@ std::shared_ptr<AbstractValueView> KTestObjectParser::testParameterView(
9621031
switch (typesHandler.getTypeKind(paramType)) {
9631032
case TypeKind::STRUCT_LIKE:
9641033
return structView(rawData, typesHandler.getStructInfo(paramType), 0,
965-
usage, testingMethod, param.varName, fromAddressToName, initReferences);
1034+
usage, testingMethod, false, param.varName, fromAddressToName, initReferences);
9661035
case TypeKind::ENUM:
9671036
return enumView(rawData, typesHandler.getEnumInfo(paramType), 0, SizeUtils::bytesToBits(rawData.size()));
9681037
case TypeKind::PRIMITIVE:

server/src/Tests.h

+32-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33

44
#include "Include.h"
55
#include "LineInfo.h"
6+
#include "NameDecorator.h"
67
#include "types/Types.h"
78
#include "utils/CollectionUtils.h"
89
#include "utils/PrinterUtils.h"
910
#include "utils/SizeUtils.h"
1011
#include "json.hpp"
11-
1212
#include <klee/KTest.h>
1313
#include <klee/TestCase.h>
1414
#include <tsl/ordered_map.h>
@@ -21,8 +21,8 @@
2121
#include <iterator>
2222
#include <memory>
2323
#include <optional>
24-
#include <type_traits>
2524
#include <queue>
25+
#include <type_traits>
2626
#include <utility>
2727
#include <vector>
2828

@@ -233,9 +233,27 @@ namespace tests {
233233
struct StructValueView : AbstractValueView {
234234
explicit StructValueView(const types::StructInfo &_structInfo,
235235
std::vector<std::shared_ptr<AbstractValueView>> _subViews,
236-
std::optional<std::string> _entryValue)
237-
: AbstractValueView(std::move(_subViews)), entryValue(std::move(_entryValue)),
238-
structInfo(_structInfo){
236+
std::optional<std::string> _entryValue,
237+
bool _anonymous,
238+
bool _dirtyInit,
239+
size_t _fieldIndexToInitUnion)
240+
: AbstractValueView(std::move(_subViews))
241+
, entryValue(std::move(_entryValue))
242+
, structInfo(_structInfo)
243+
, anonymous(_anonymous)
244+
, dirtyInit(_dirtyInit)
245+
, fieldIndexToInitUnion(_fieldIndexToInitUnion){}
246+
247+
bool isDirtyInit() const {
248+
return dirtyInit;
249+
}
250+
251+
bool isAnonymous() const {
252+
return anonymous;
253+
}
254+
255+
size_t getFieldIndexToInitUnion() const {
256+
return fieldIndexToInitUnion;
239257
}
240258

241259
[[nodiscard]] const std::vector<std::shared_ptr<AbstractValueView>> &getSubViews() const override {
@@ -254,7 +272,7 @@ namespace tests {
254272
std::vector<std::string> entries;
255273
size_t i = 0;
256274
for (const auto &subView : subViews) {
257-
if (structInfo.subType == types::SubType::Struct || structInfo.longestFieldIndexForUnionInit == i) {
275+
if (structInfo.subType == types::SubType::Struct || fieldIndexToInitUnion == i) {
258276
entries.push_back(subView->getEntryValue(nullptr));
259277
}
260278
++i;
@@ -273,6 +291,9 @@ namespace tests {
273291
}
274292

275293
[[nodiscard]] std::string getFieldPrefix(int i) const {
294+
if (structInfo.fields[i].name.empty())
295+
return "";
296+
276297
std::string prefix = "." + structInfo.fields[i].name + " = ";
277298
if (structInfo.isCLike) {
278299
return prefix;
@@ -291,6 +312,10 @@ namespace tests {
291312
private:
292313
const types::StructInfo structInfo;
293314
std::optional<std::string> entryValue;
315+
316+
bool anonymous;
317+
bool dirtyInit;
318+
size_t fieldIndexToInitUnion;
294319
};
295320

296321
struct InitReference {
@@ -679,6 +704,7 @@ namespace tests {
679704
size_t offsetInBits,
680705
types::PointerUsage usage,
681706
const std::optional<const Tests::MethodDescription> &testingMethod,
707+
const bool anonymous,
682708
const std::string &name,
683709
const MapAddressName &fromAddressToName,
684710
std::vector<InitReference> &initReferences);

server/src/printers/TestsPrinter.cpp

+8-6
Original file line numberDiff line numberDiff line change
@@ -713,12 +713,12 @@ std::string printer::MultiLinePrinter::print(TestsPrinter *printer,
713713
const tests::StructValueView *view) {
714714
auto subViews = view->getSubViews();
715715
std::stringstream structuredValuesWithPrefixes;
716-
structuredValuesWithPrefixes << "{" << NL;
716+
717+
structuredValuesWithPrefixes << (view->isAnonymous() ? "/* { */" : "{") << NL;
717718
++printer->tabsDepth;
718719

719-
const types::StructInfo &structInfo = view->getStructInfo();
720-
const size_t longestFieldIndexForUnionInit = structInfo.longestFieldIndexForUnionInit;
721-
const bool isStruct = structInfo.subType == types::SubType::Struct;
720+
const size_t fieldIndexToInitUnion = view->getFieldIndexToInitUnion();
721+
const bool isStruct = view->getStructInfo().subType == types::SubType::Struct;
722722

723723
size_t i = 0;
724724
for (const auto &sview : subViews) {
@@ -728,7 +728,7 @@ std::string printer::MultiLinePrinter::print(TestsPrinter *printer,
728728
structuredValuesWithPrefixes << NL;
729729
}
730730

731-
bool printInComment = !(isStruct || longestFieldIndexForUnionInit == i);
731+
bool printInComment = !(isStruct || fieldIndexToInitUnion == i);
732732
if (printInComment) {
733733
++printer->commentDepth;
734734
}
@@ -743,7 +743,9 @@ std::string printer::MultiLinePrinter::print(TestsPrinter *printer,
743743
}
744744

745745
--printer->tabsDepth;
746-
structuredValuesWithPrefixes << NL << printer->LINE_INDENT() << "}";
746+
structuredValuesWithPrefixes << NL
747+
<< printer->LINE_INDENT()
748+
<< (view->isAnonymous() ? "/* } */" : "}");
747749

748750
return structuredValuesWithPrefixes.str();
749751
}

server/src/types/Types.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ namespace types {
258258

259259
struct Field {
260260
types::Type type;
261+
bool anonymous;
261262
std::string name;
262263
/// size in @b bits
263264
size_t size;
@@ -291,7 +292,6 @@ namespace types {
291292

292293
struct StructInfo: TypeInfo {
293294
std::vector<Field> fields{};
294-
size_t longestFieldIndexForUnionInit;
295295
FPointerMap functionFields{};
296296
bool hasAnonymousStructOrUnion;
297297
bool isCLike;

server/src/types/TypesResolver.cpp

+3-9
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,15 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin
121121
<< "\tFile path: " << structInfo.filePath.string() << "";
122122
std::vector<types::Field> fields;
123123

124-
structInfo.longestFieldIndexForUnionInit = SIZE_MAX;
125-
size_t i = 0;
126-
size_t maxFieldSize = 0;
127124
for (const clang::FieldDecl *F : D->fields()) {
128125
if (F->isUnnamedBitfield()) {
129126
continue;
130127
}
131-
structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion();
132128
types::Field field;
129+
field.anonymous = F->isAnonymousStructOrUnion();
133130
field.name = F->getNameAsString();
131+
structInfo.hasAnonymousStructOrUnion |= field.anonymous;
132+
134133
const clang::QualType paramType = F->getType().getCanonicalType();
135134
field.type = types::Type(paramType, paramType.getAsString(), sourceManager);
136135
if (field.type.isPointerToFunction()) {
@@ -175,11 +174,6 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin
175174
field.accessSpecifier = types::Field::AS_pubic;
176175
}
177176
fields.push_back(field);
178-
if (subType == types::SubType::Union && maxFieldSize < field.size) {
179-
structInfo.longestFieldIndexForUnionInit = i;
180-
maxFieldSize = field.size;
181-
}
182-
++i;
183177
}
184178
structInfo.fields = fields;
185179
structInfo.size = getRecordSize(D);

0 commit comments

Comments
 (0)