Skip to content

Commit

Permalink
[BUG] Anonymous union initialized using 'from_bytes' in some cases #447
Browse files Browse the repository at this point in the history
… (#467)
  • Loading branch information
alexey-utkin authored Sep 19, 2022
1 parent d98a8b1 commit 7787136
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 58 deletions.
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

0 comments on commit 7787136

Please sign in to comment.