Skip to content

[BUG] Anonymous union initialized using 'from_bytes' in some cases #447 #467

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 85 additions & 16 deletions server/src/Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,33 +252,81 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
size_t offsetInBits,
types::PointerUsage usage) {
std::vector<InitReference> tmpInitReferences;
return structView(byteArray, curStruct, offsetInBits, usage, {}, "", {}, tmpInitReferences);
return structView(byteArray, curStruct, offsetInBits, usage, {}, false, "", {}, tmpInitReferences);
}

std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector<char> &byteArray,
const StructInfo &curStruct,
size_t offsetInBits,
PointerUsage usage,
const std::optional<const Tests::MethodDescription> &testingMethod,
const bool anonymousField,
const std::string &name,
const MapAddressName &fromAddressToName,
std::vector<InitReference> &initReferences) {
std::vector<std::shared_ptr<AbstractValueView>> subViews;

size_t fieldIndexToInitUnion = SIZE_MAX;
size_t sizeOfFieldToInitUnion = 0;
size_t prevFieldEndOffset = offsetInBits;
size_t structEndOffset = offsetInBits + curStruct.size;
size_t fieldIndex = 0;
bool dirtyInitializedStruct = false;
for (const auto &field: curStruct.fields) {
bool dirtyInitializedField = false;
size_t fieldLen = typesHandler.typeSize(field.type);
size_t fieldOffset = offsetInBits + field.offset;
size_t fieldStartOffset = offsetInBits + field.offset;
size_t fieldEndOffset = fieldStartOffset + fieldLen;
if (curStruct.subType == types::SubType::Union) {
prevFieldEndOffset = offsetInBits;
}

auto dirtyCheck = [&](int i) {
if (i >= byteArray.size()) {
LOG_S(ERROR) << "Bad type size info: " << field.name << " index: " << fieldIndex;
} else if (byteArray[i] == 0) {
return false;
}
// the field cannot init the union in this state
dirtyInitializedField = true;
return true;
};

if (prevFieldEndOffset < fieldStartOffset) {
// check an alignment gap
for (int i = prevFieldEndOffset/8; i < fieldStartOffset/8; ++i) {
if (dirtyCheck(i)) {
break;
}
}
}
if (!dirtyInitializedField && curStruct.subType == types::SubType::Union) {
// check the rest of the union
for (int i = fieldEndOffset/8; i < structEndOffset/8; ++i) {
if (dirtyCheck(i)) {
break;
}
}
}

switch (typesHandler.getTypeKind(field.type)) {
case TypeKind::STRUCT_LIKE:
subViews.push_back(structView(byteArray, typesHandler.getStructInfo(field.type), fieldOffset, usage, testingMethod,
PrinterUtils::getFieldAccess(name, field), fromAddressToName, initReferences));
{
auto sv = structView(byteArray, typesHandler.getStructInfo(field.type),
fieldStartOffset, usage, testingMethod, field.anonymous,
PrinterUtils::getFieldAccess(name, field), fromAddressToName,
initReferences);
dirtyInitializedField |= sv->isDirtyInit();
subViews.push_back(sv);
}
break;
case TypeKind::ENUM:
subViews.push_back(enumView(byteArray, typesHandler.getEnumInfo(field.type), fieldOffset, fieldLen));
subViews.push_back(enumView(byteArray, typesHandler.getEnumInfo(field.type),
fieldStartOffset, fieldLen));
break;
case TypeKind::PRIMITIVE:
subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(), fieldOffset,
subViews.push_back(primitiveView(byteArray, field.type.baseTypeObj(),
fieldStartOffset,
std::min(field.size, fieldLen)));
break;
case TypeKind::ARRAY: {
Expand All @@ -296,21 +344,22 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
}
if (onlyArrays) {
size *= typesHandler.typeSize(field.type.baseTypeObj());
subViews.push_back(multiArrayView(byteArray, field.type, size,
fieldOffset, usage));
subViews.push_back(multiArrayView(byteArray, field.type, size, fieldStartOffset, usage));
} else {
std::vector<std::shared_ptr<AbstractValueView>> nullViews(
size, std::make_shared<JustValueView>(PrinterUtils::C_NULL));
subViews.push_back(std::make_shared<ArrayValueView>(nullViews));
}
} else {
auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen, fieldOffset, usage);
auto view = arrayView(byteArray, field.type.baseTypeObj(), fieldLen,
fieldStartOffset, usage);
subViews.push_back(view);
}
}
break;
case TypeKind::OBJECT_POINTER: {
std::string res = readBytesAsValueForType(byteArray, PointerWidthType, fieldOffset,
std::string res = readBytesAsValueForType(byteArray, PointerWidthType,
fieldStartOffset,
PointerWidthSizeInBits);
subViews.push_back(getLazyPointerView(fromAddressToName, initReferences,
PrinterUtils::getFieldAccess(name, field), res, field.type));
Expand All @@ -330,15 +379,35 @@ std::shared_ptr<StructValueView> KTestObjectParser::structView(const std::vector
LOG_S(ERROR) << message;
throw NoSuchTypeException(message);
}

if (!dirtyInitializedField && sizeOfFieldToInitUnion < fieldLen) {
fieldIndexToInitUnion = fieldIndex;
sizeOfFieldToInitUnion = fieldLen;
} else {
dirtyInitializedStruct = true;
}
prevFieldEndOffset = fieldEndOffset;
++fieldIndex;
}

std::optional<std::string> entryValue;
if (curStruct.hasAnonymousStructOrUnion) {
auto bytesType = types::Type::createSimpleTypeFromName("utbot_byte");
const std::shared_ptr<AbstractValueView> rawDataView = arrayView(byteArray, bytesType, curStruct.size, offsetInBits, usage);
entryValue = PrinterUtils::convertBytesToUnion(curStruct.name, rawDataView->getEntryValue(nullptr));
if (curStruct.subType == types::SubType::Union) {
if (fieldIndexToInitUnion == SIZE_MAX && !curStruct.name.empty()) {
// init by memory copy
entryValue = PrinterUtils::convertBytesToUnion(
curStruct.name,
arrayView(byteArray,
types::Type::createSimpleTypeFromName("utbot_byte"),
curStruct.size,
offsetInBits, usage)->getEntryValue(nullptr));
dirtyInitializedStruct = false;
}
if (fieldIndexToInitUnion != SIZE_MAX) {
dirtyInitializedStruct = false;
}
}
return std::make_shared<StructValueView>(curStruct, subViews, entryValue);
return std::make_shared<StructValueView>(curStruct, subViews, entryValue,
anonymousField, dirtyInitializedStruct, fieldIndexToInitUnion);
}

std::string KTestObjectParser::primitiveCharView(const types::Type &type, std::string value) {
Expand Down Expand Up @@ -962,7 +1031,7 @@ std::shared_ptr<AbstractValueView> KTestObjectParser::testParameterView(
switch (typesHandler.getTypeKind(paramType)) {
case TypeKind::STRUCT_LIKE:
return structView(rawData, typesHandler.getStructInfo(paramType), 0,
usage, testingMethod, param.varName, fromAddressToName, initReferences);
usage, testingMethod, false, param.varName, fromAddressToName, initReferences);
case TypeKind::ENUM:
return enumView(rawData, typesHandler.getEnumInfo(paramType), 0, SizeUtils::bytesToBits(rawData.size()));
case TypeKind::PRIMITIVE:
Expand Down
38 changes: 32 additions & 6 deletions server/src/Tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

#include "Include.h"
#include "LineInfo.h"
#include "NameDecorator.h"
#include "types/Types.h"
#include "utils/CollectionUtils.h"
#include "utils/PrinterUtils.h"
#include "utils/SizeUtils.h"
#include "json.hpp"

#include <klee/KTest.h>
#include <klee/TestCase.h>
#include <tsl/ordered_map.h>
Expand All @@ -21,8 +21,8 @@
#include <iterator>
#include <memory>
#include <optional>
#include <type_traits>
#include <queue>
#include <type_traits>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -233,9 +233,27 @@ namespace tests {
struct StructValueView : AbstractValueView {
explicit StructValueView(const types::StructInfo &_structInfo,
std::vector<std::shared_ptr<AbstractValueView>> _subViews,
std::optional<std::string> _entryValue)
: AbstractValueView(std::move(_subViews)), entryValue(std::move(_entryValue)),
structInfo(_structInfo){
std::optional<std::string> _entryValue,
bool _anonymous,
bool _dirtyInit,
size_t _fieldIndexToInitUnion)
: AbstractValueView(std::move(_subViews))
, entryValue(std::move(_entryValue))
, structInfo(_structInfo)
, anonymous(_anonymous)
, dirtyInit(_dirtyInit)
, fieldIndexToInitUnion(_fieldIndexToInitUnion){}

bool isDirtyInit() const {
return dirtyInit;
}

bool isAnonymous() const {
return anonymous;
}

size_t getFieldIndexToInitUnion() const {
return fieldIndexToInitUnion;
}

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

[[nodiscard]] std::string getFieldPrefix(int i) const {
if (structInfo.fields[i].name.empty())
return "";

std::string prefix = "." + structInfo.fields[i].name + " = ";
if (structInfo.isCLike) {
return prefix;
Expand All @@ -291,6 +312,10 @@ namespace tests {
private:
const types::StructInfo structInfo;
std::optional<std::string> entryValue;

bool anonymous;
bool dirtyInit;
size_t fieldIndexToInitUnion;
};

struct InitReference {
Expand Down Expand Up @@ -679,6 +704,7 @@ namespace tests {
size_t offsetInBits,
types::PointerUsage usage,
const std::optional<const Tests::MethodDescription> &testingMethod,
const bool anonymous,
const std::string &name,
const MapAddressName &fromAddressToName,
std::vector<InitReference> &initReferences);
Expand Down
14 changes: 8 additions & 6 deletions server/src/printers/TestsPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,12 +713,12 @@ std::string printer::MultiLinePrinter::print(TestsPrinter *printer,
const tests::StructValueView *view) {
auto subViews = view->getSubViews();
std::stringstream structuredValuesWithPrefixes;
structuredValuesWithPrefixes << "{" << NL;

structuredValuesWithPrefixes << (view->isAnonymous() ? "/* { */" : "{") << NL;
++printer->tabsDepth;

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

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

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

--printer->tabsDepth;
structuredValuesWithPrefixes << NL << printer->LINE_INDENT() << "}";
structuredValuesWithPrefixes << NL
<< printer->LINE_INDENT()
<< (view->isAnonymous() ? "/* } */" : "}");

return structuredValuesWithPrefixes.str();
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/types/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ namespace types {

struct Field {
types::Type type;
bool anonymous;
std::string name;
/// size in @b bits
size_t size;
Expand Down Expand Up @@ -291,7 +292,6 @@ namespace types {

struct StructInfo: TypeInfo {
std::vector<Field> fields{};
size_t longestFieldIndexForUnionInit;
FPointerMap functionFields{};
bool hasAnonymousStructOrUnion;
bool isCLike;
Expand Down
12 changes: 3 additions & 9 deletions server/src/types/TypesResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,15 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin
<< "\tFile path: " << structInfo.filePath.string() << "";
std::vector<types::Field> fields;

structInfo.longestFieldIndexForUnionInit = SIZE_MAX;
size_t i = 0;
size_t maxFieldSize = 0;
for (const clang::FieldDecl *F : D->fields()) {
if (F->isUnnamedBitfield()) {
continue;
}
structInfo.hasAnonymousStructOrUnion |= F->isAnonymousStructOrUnion();
types::Field field;
field.anonymous = F->isAnonymousStructOrUnion();
field.name = F->getNameAsString();
structInfo.hasAnonymousStructOrUnion |= field.anonymous;

const clang::QualType paramType = F->getType().getCanonicalType();
field.type = types::Type(paramType, paramType.getAsString(), sourceManager);
if (field.type.isPointerToFunction()) {
Expand Down Expand Up @@ -175,11 +174,6 @@ void TypesResolver::resolveStructEx(const clang::RecordDecl *D, const std::strin
field.accessSpecifier = types::Field::AS_pubic;
}
fields.push_back(field);
if (subType == types::SubType::Union && maxFieldSize < field.size) {
structInfo.longestFieldIndexForUnionInit = i;
maxFieldSize = field.size;
}
++i;
}
structInfo.fields = fields;
structInfo.size = getRecordSize(D);
Expand Down
Loading