Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
65dc21f
fix(svdconv): guard bit masks and bound dimension expansion
thorstendb-ARM Oct 15, 2026
68e527e
fix(svdconv): guard missing register ancestors in field validation
thorstendb-ARM Oct 15, 2026
b53d44f
test(svdconv): preserve fixture bytes on Windows
thorstendb-ARM Oct 15, 2026
6032d90
test(svdconv): cover expansion and mask rejection boundaries
thorstendb-ARM Oct 15, 2026
93c33b3
test(svdconv): own detached expression register
thorstendb-ARM Oct 15, 2026
188c069
Merge branch 'main' into fix/svdconv-input-bounds
thorstendb-ARM Oct 20, 2026
924e91d
Merge branch 'main' into fix/svdconv-input-bounds
thorstendb-ARM Oct 20, 2026
2ddda56
fix(svdconv): reject invalid SFD conditions and output failures
thorstendb-ARM Oct 20, 2026
39cea83
Merge branch 'main' into fix/svdconv-input-bounds
thorstendb-ARM Oct 20, 2026
c4255c6
fix(svdconv): keep generated masks within 32 bits
thorstendb-ARM Oct 20, 2026
1705825
fix(svdconv): guard empty containers during symbol lookup
thorstendb-ARM Oct 20, 2026
081191e
Merge branch 'main' into fix/svdconv-input-bounds
thorstendb-ARM Oct 20, 2026
6554ca9
fix(svdconv): validate field positions before narrowing
thorstendb-ARM Oct 20, 2026
5f5c7b8
Merge remote-tracking branch 'origin/fix/svdconv-input-bounds' into f…
thorstendb-ARM Oct 20, 2026
b0f0f7a
Potential fix for pull request finding 'CodeQL / Inconsistent nullnes…
thorstendb-ARM Oct 20, 2026
a3bb15d
refactor(svdconv): preserve wide field values through generation
thorstendb-ARM Oct 20, 2026
5795769
refactor(svdconv): keep checked field generation within 32 bits
thorstendb-ARM Oct 20, 2026
9e5e7ed
refactor(svdconv): reuse SFD result to guard SFR generation
thorstendb-ARM Oct 20, 2026
408afdf
docs(svdconv): explain the checked bit-range boundary
thorstendb-ARM Oct 20, 2026
e363337
docs(svdconv): track field-range validation follow-up
thorstendb-ARM Oct 20, 2026
566a603
refactor(svdconv): move SFD validation helpers into SfdData
thorstendb-ARM Oct 20, 2026
8b88163
refactor(svdconv): simplify unsigned shift literal
thorstendb-ARM Oct 20, 2026
484e3a4
fix(svdconv): bound enum lists and calculate counts in 64 bits
thorstendb-ARM Oct 20, 2026
38fa786
refactor(svdconv): use 32-bit counts after enum width validation
thorstendb-ARM Oct 20, 2026
7c42097
style(svdconv): expand newly added control-flow blocks
thorstendb-ARM Oct 20, 2026
2f189e0
refactor(svdconv): reuse enum width limit in field diagnostics
thorstendb-ARM Oct 20, 2026
e422da5
refactor(svdconv): assign validated bit ranges without explicit casts
thorstendb-ARM Oct 20, 2026
633bdd4
test(svdconv): cover input-bound failure paths
thorstendb-ARM Oct 20, 2026
50a6cba
fix(svdconv): allow one million expansion operations
thorstendb-ARM Oct 20, 2026
35b0cac
fix(svdconv): validate field width before advancing padding
thorstendb-ARM Oct 20, 2026
43c3f60
refactor(svdconv): return a 32-bit register access mask
thorstendb-ARM Oct 21, 2026
6e032f4
Merge branch 'main' into fix/svdconv-input-bounds
thorstendb-ARM Oct 21, 2026
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
10 changes: 10 additions & 0 deletions tools/svdconv/SVDConv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ due to backward compatibility reasons. All newly developed/updated SVD files sho
in the same directory as `svdconv.exe`. On Linux and macOS, SVDConv reports error M133 and
returns exit code 2. The intermediate `.sfd` file may still be generated.
Use `--generate=sfd` to generate SFD output on any supported platform.
SVDConv supports Cortex-M devices with register and field ranges within 32 bits. Wider intermediate arithmetic
prevents overflow; it does not add support for 64-bit input or output. SFD/SFR rejects wider ranges with M226 before
SFD file creation. SFR compilation is skipped when SFD generation fails.

Dimension expansion has a cumulative limit of 1,000,000 generated indices and model copies per input file.
Nested arrays, inherited content, and repeated expansion share this limit. Exceeding it reports M249 and stops
processing that input before any output generator runs. This bounds expansion work, not the XML file size or total
process memory. Invalid dimension index ranges and overflowing expansion addresses report M250.

## Return Codes

Expand Down Expand Up @@ -287,6 +295,8 @@ The following table shows the errors and warnings issued by svdconv.
| M241 | ERROR | Parse Error: 'VALUE' invalid for Array generation | Correct SVD.|
| M242 | WARNING | 'LEVEL' 'NAME' `<dimArrayIndex>` found, but no `<dim>` | Correct SVD.|
| M243 | WARNING | 'LEVEL' 'NAME' `<dimArrayIndex>` found, but `<dim>` does not describe an array | Correct SVD.|
| M249 | ERROR | Dimension expansion exceeds the per-input limit of 'NUM' generated indices and model copies. | Reduce the total expanded model size.|
| M250 | ERROR | Dimension 'NAME' has an invalid or overflowing index/address range. | Correct the dimension range or increment.|

### Data Check Errors

Expand Down
10 changes: 9 additions & 1 deletion tools/svdconv/SVDConv/src/SVDConv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,13 @@ SVD_ERR SvdConv::CheckSvdFile()
if(success) { LogMsg("M040", NAME("Deleting XML Tree"), TIME(t2)); }
else { LogMsg("M111", NAME("Deleting XML Tree")); }

// A partial model from an exhausted expansion budget must never reach a generator.
if(m_svdModel->HasExpansionError()) {
delete m_svdModel;
m_svdModel = nullptr;
return SVD_ERR_VERIFY;
}

// ---------------------- Calculate Model ----------------------
t1 = CrossPlatformUtils::ClockInMsec();
success = m_svdModel->CalculateModel();
Expand Down Expand Up @@ -334,6 +341,7 @@ SVD_ERR SvdConv::CheckSvdFile()
// ---------------------- Generate SFD File ----------------------
if(m_svdOptions.IsGenerateSfd()) {
t1 = CrossPlatformUtils::ClockInMsec();
success = false;
if(device) {
generator->SetSvdFileName(path);
generator->SetProgramInfo(version, descr, copyright);
Expand All @@ -346,7 +354,7 @@ SVD_ERR SvdConv::CheckSvdFile()
}

// ---------------------- Generate SFR File ----------------------
if(m_svdOptions.IsGenerateSfr()) {
if(m_svdOptions.IsGenerateSfr() && success) {
t1 = CrossPlatformUtils::ClockInMsec();
if(device) {
generator->SetSvdFileName(path);
Expand Down
3 changes: 3 additions & 0 deletions tools/svdconv/SVDConv/src/SVDConv_Msgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ const MsgTable SvdConv::msgTable = {
{ "M247", { MsgLevel::LEVEL_ERROR, CRLF_B, "C Expression: Error occurred during generation!" } },
{ "M248", { MsgLevel::LEVEL_ERROR, CRLF_B, "C Expression: Referenced Object must be Register or Field!" } },

{ "M249", { MsgLevel::LEVEL_ERROR, CRLF_B, "Dimension expansion exceeds the per-input limit of %NUM% generated indices and model copies." } },
{ "M250", { MsgLevel::LEVEL_ERROR, CRLF_B, "Dimension '%NAME%' has an invalid or overflowing index/address range." } },


// 300... Data Check Errors
{ "M300", { MsgLevel::LEVEL_ERROR, CRLF_B, "" } },
Expand Down
3 changes: 3 additions & 0 deletions tools/svdconv/SVDGenerator/include/SfdData.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class SfdData {
bool IsValid (SvdItem* item);

private:
static bool CheckBitRange (SvdItem* item);
static bool CheckBitRanges (SvdItem* item);

const SvdOptions& m_options;
FileIo* m_fileIo;
SfdGenerator* m_gen;
Expand Down
55 changes: 32 additions & 23 deletions tools/svdconv/SVDGenerator/src/HeaderData_Field.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ bool HeaderData::CreateFields(SvdRegister* reg, string structName)
FIELDMAPLIST sortedFields;
AddFields(fieldCont, sortedFields);

CreateSortedFields(sortedFields, regSize, structName);
if(!CreateSortedFields(sortedFields, regSize, structName)) {
return false;
}
}

return true;
Expand Down Expand Up @@ -74,10 +76,11 @@ bool HeaderData::AddFields(SvdItem* container, FIELDMAPLIST& sortedFields)
}
}

const auto name = field->GetNameCalculated();
uint32_t offs = (uint32_t) field->GetOffset();
uint32_t width = (uint32_t) field->GetBitWidth();
uint32_t mask = ((1<<width)-1) << offs;
uint32_t offset, bitWidth;
if(!SvdUtils::GetBitRange32(field->GetOffset(), field->GetBitWidth(), offset, bitWidth)) {
continue;
}
const auto mask = SvdUtils::CreateBitMask32(bitWidth, offset);

ONESTRUCT* oneStruct = nullptr;
for(auto& oneStr : sortedFields) {
Expand All @@ -93,8 +96,8 @@ bool HeaderData::AddFields(SvdItem* container, FIELDMAPLIST& sortedFields)
oneStruct = &*sortedFields.rbegin();
}

oneStruct->fields.emplace(offset, field);
oneStruct->mask |= mask;
oneStruct->fields[offs] = field;
}

return true;
Expand All @@ -104,20 +107,30 @@ bool HeaderData::AddFields(SvdItem* container, FIELDMAPLIST& sortedFields)
bool HeaderData::CreateSortedFields(const FIELDMAPLIST& sortedFields, uint32_t regSize, string structName)
{
m_reservedFieldCnt = 0;
if(regSize > sizeof(uint32_t)) {
m_gen->Generate<C_ERROR>("Register bitfields exceed 32 bits", -1);
return false;
}
const auto registerBits = regSize * 8;

for(const auto& oneStruct : sortedFields) {
uint32_t offsCnt = 0;
m_gen->Generate<STRUCT|BEGIN>("");

for(const auto& [newOffs, field] : oneStruct.fields) {
int32_t res = newOffs - offsCnt;

GenerateReservedField(res, regSize);
offsCnt += res;
offsCnt += CreateField(field, regSize, offsCnt);
if(newOffs < offsCnt || newOffs > registerBits) {
m_gen->Generate<C_ERROR>("Reserved bits calculation error", -1);
return false;
}
GenerateReservedField(newOffs - offsCnt, regSize);
const auto fieldWidth = CreateField(field, regSize, newOffs);
if(fieldWidth > registerBits - newOffs) {
m_gen->Generate<C_ERROR>("Reserved bits calculation error", -1);
return false;
}
offsCnt = newOffs + fieldWidth;
}

GenerateReservedField((regSize * 8) - offsCnt, regSize); // fill up (pad) bitfields
GenerateReservedField(registerBits - offsCnt, regSize); // fill up (pad) bitfields

m_gen->Generate<STRUCT|END>("%s", structName.c_str());
}
Expand All @@ -131,27 +144,23 @@ uint32_t HeaderData::CreateField(SvdField* field, uint32_t regSize, uint32_t off
return false;
}

uint32_t sizeNeeded = 0;

const auto& dataType = SvdUtils::GetDataTypeString(regSize);
const auto name = field->GetNameCalculated();
const auto descr = field->GetDescriptionCalculated();
const auto accType = field->GetEffectiveAccess();
uint32_t fieldWidth = field->GetBitWidth();
uint32_t fieldPos = (uint32_t)field->GetOffset();
uint32_t fieldPos, fieldWidth;
if(!SvdUtils::GetBitRange32(field->GetOffset(), field->GetBitWidth(), fieldPos, fieldWidth)) {
return 0;
}

m_gen->Generate<MAKE|MK_FIELD_STRUCT>("%s", accType, dataType.c_str(), regSize, fieldWidth, name.c_str());
uint32_t lastBit = fieldPos + fieldWidth -1;
const auto lastBit = fieldPos + fieldWidth -1;
m_gen->Generate<MAKE|MK_DOXY_COMMENT_BITFIELD>("%s", fieldPos, lastBit, !descr.empty()? descr.c_str() : name.c_str());

if(sizeNeeded < fieldWidth) {
sizeNeeded = fieldWidth;
}

if(offsCnt != fieldPos) {
m_gen->Generate<C_ERROR>("Reserved bits calculation error", -1);
}

return sizeNeeded;
return fieldWidth;
}

8 changes: 5 additions & 3 deletions tools/svdconv/SVDGenerator/src/HeaderData_PosMask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,11 @@ bool HeaderData::CreateFieldPosMask(SvdField* field, PosMaskNames *posMaskNames)
const auto& alternateGroup = posMaskNames->alternate;
const auto name = field->GetNameCalculated();
const auto fieldName = field->GetHierarchicalNameResulting();
uint32_t firstBit = (uint32_t)field->GetOffset();
uint32_t bitWidth = field->GetEffectiveBitWidth();
uint32_t bitMaxNum = (uint32_t) ((((uint64_t)(1) << bitWidth) -1));
uint32_t firstBit, bitWidth;
if(!SvdUtils::GetBitRange32(field->GetOffset(), field->GetEffectiveBitWidth(), firstBit, bitWidth)) {
return false;
}
const auto bitMaxNum = SvdUtils::CreateBitMask32(bitWidth);

if(!alternateGroup.empty())
m_gen->Generate<MAKE|MK_FIELD_POSMASK3 >("%s_%s", name.c_str(), firstBit, bitMaxNum, fieldName.c_str(), alternateGroup.c_str());
Expand Down
73 changes: 70 additions & 3 deletions tools/svdconv/SVDGenerator/src/SfdData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,65 @@
#include "SvdItem.h"
#include "FileIo.h"
#include "SvdDevice.h"
#include "SvdDimension.h"
#include "SvdField.h"
#include "SvdPeripheral.h"
#include "SvdRegister.h"
#include "SvdUtils.h"
#include "ErrLog.h"

using namespace std;

bool SfdData::CheckBitRange(SvdItem* item)
{
const auto reg = dynamic_cast<SvdRegister*>(item);
const auto field = dynamic_cast<SvdField*>(item);
if(reg || field) {
uint32_t offset, width;
if(!SvdUtils::GetBitRange32(field ? field->GetOffset() : 0, item->GetEffectiveBitWidth(), offset, width)) {
LogMsg("M226", MSG("SFD supports only bit ranges within 32 bits: '" + item->GetNameCalculated() + "'"), item->GetLineNumber());
return false;
}
}
return true;
}

bool SfdData::CheckBitRanges(SvdItem* item)
{
if(!item->IsValid()) {
return true;
}
if(!CheckBitRange(item)) {
return false;
}

const auto peripheral = dynamic_cast<SvdPeripheral*>(item);
const auto condition = peripheral ? peripheral->GetDisableCondition() : nullptr;
if(condition && condition->IsValid()) {
for(const auto& symbol : condition->GetSymbolsList()) {
if(symbol.token.type != SvdCExpressionParser::xme_identi) {
continue;
}
const auto referenced = symbol.svdItem;
if(!referenced || !referenced->IsValid()) {
LogMsg("M226", MSG("Invalid item referenced by an SFD expression: '" + condition->GetText() + "'"), condition->GetLineNumber());
return false;
}
if(!CheckBitRange(referenced)) {
return false;
}
}
}

for(const auto child : item->GetChildren()) {
if(!CheckBitRanges(child)) {
return false;
}
}
const auto dim = item->GetDimension();
return !dim || CheckBitRanges(dim);
}


SfdData::SfdData(const FileHeaderInfo &fileHeaderInfo, const SvdOptions& options):
m_options(options),
Expand Down Expand Up @@ -45,10 +101,21 @@ bool SfdData::Create(SvdItem *item, const string &fileName)
return false;
}

m_fileIo->Create(fileName);

const auto device = dynamic_cast<SvdDevice*>(item);
if(!device) {
if(!device || !CheckBitRanges(device)) {
return false;
}
for(const auto& [name, reg] : device->GetExpressionRegistersList()) {
if(!reg || !reg->IsValid()) {
LogMsg("M226", MSG("Invalid register referenced by an SFD expression: '" + name + "'"));
return false;
}
if(!CheckBitRanges(reg)) {
return false;
}
}

if(!m_fileIo->Create(fileName)) {
return false;
}

Expand Down
22 changes: 15 additions & 7 deletions tools/svdconv/SVDGenerator/src/SfdData_SingleItems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ bool SfdData::CreateRegisterItem(SvdRegister* reg)
if(cont && cont->IsValid() && cont->GetChildCount()) {
list<SvdItem*> fieldsList;
CreateFields(cont, fieldsList);
const auto accMaskReadRtree = (uint32_t) reg->GetAccessMask();
const auto accMaskReadRtree = reg->GetAccessMask();

CreateItemDescription(reg, "RTree");
m_gen->Generate<RTREE|BEGIN >("SFDITEM_REG__%s", itemName.c_str());
Expand Down Expand Up @@ -873,10 +873,14 @@ bool SfdData::CreateFieldItem(SvdField* field)
const auto dispName = field->GetDisplayNameCalculated();
const auto fieldDescr = SvdUtils::CheckTextGeneric_SfrCC2(field->GetDescriptionCalculated());
const auto regName = field->GetParentRegisterNameHierarchical();
const auto firstBit = (uint32_t)field->GetOffset();
const auto bitWidth = (uint32_t)field->GetEffectiveBitWidth();
const auto accessType = field->GetEffectiveAccess();
const auto regAddress = (uint32_t)field->GetAbsoluteAddress();
uint32_t firstBit = 0;
uint32_t bitWidth = 0;
if(!SvdUtils::GetBitRange32(field->GetOffset(), field->GetEffectiveBitWidth(), firstBit, bitWidth)) {
LogMsg("M226", MSG("SFD supports only bit ranges within 32 bits: '" + fieldName + "'"), field->GetLineNumber());
return false;
}

uint32_t regBitWidth = 32;
const auto parent = field->GetParent();
Expand All @@ -894,8 +898,8 @@ bool SfdData::CreateFieldItem(SvdField* field)
field->GetValuesDescriptionString(valuesDescr);
valuesDescr = SvdUtils::CheckTextGeneric_SfrCC2(valuesDescr);

uint32_t lastBit = firstBit + bitWidth -1;
uint32_t bitMaxNum = (uint32_t) (((uint64_t)(1) << bitWidth) -1);
const auto lastBit = firstBit + bitWidth -1;
const auto bitMaxNum = SvdUtils::CreateBitMask32(bitWidth);

CreateItemDescription(field, "Item");
m_gen->Generate<ITEM|BEGIN>("SFDITEM_FIELD__%s", itemName.c_str());
Expand Down Expand Up @@ -1011,6 +1015,11 @@ bool SfdData::CreateEnumValueSet(SvdEnumContainer *cont, SvdField* field)
return true;
}

const auto bitWidth = field->GetEffectiveBitWidth();
if(bitWidth == 0 || bitWidth > MAX_BITWIDTH_FOR_COMBO) {
return false;
}

const auto defaultValue = cont->GetDefaultValue();
map<uint32_t, SvdEnum*> enumValues;

Expand All @@ -1028,8 +1037,7 @@ bool SfdData::CreateEnumValueSet(SvdEnumContainer *cont, SvdField* field)
enumValues[val.u32] = enu;
}

const auto bitWidth = field->GetEffectiveBitWidth();
const auto bitMaxNum = (uint32_t)((1UL << (uint64_t)bitWidth));
const uint32_t bitMaxNum = 1U << bitWidth;

for(uint32_t bitNum=0; bitNum<bitMaxNum; bitNum++) {
auto element = enumValues.find(bitNum);
Expand Down
4 changes: 2 additions & 2 deletions tools/svdconv/SVDGenerator/src/SvdGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ bool SvdGenerator::SfdFile(SvdDevice *device, const string &path)
SetFileHeader(fileHeaderInfo, device);

const auto sfdData = new SfdData(fileHeaderInfo, m_options);
sfdData->Create(device, fileName);
const bool success = sfdData->Create(device, fileName);

delete sfdData;

return true;
return success;
}

bool SvdGenerator::SfrFile(SvdDevice *device, const string &path)
Expand Down
5 changes: 3 additions & 2 deletions tools/svdconv/SVDModel/include/SvdDimension.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ class SvdDimension : public SvdItem
bool SetDimIndexList (const std::list<std::string>& dimIndexList) { m_dimIndexList = dimIndexList; return true; }
bool SetDimName (const std::string& dimName ) { m_dimName = dimName ; return true; }

int32_t GetAddressBitsUnits ();
int32_t CalcAddressIncrement ();
uint32_t GetAddressBitsUnits ();
uint64_t CalcAddressIncrement ();
bool CheckExpansionRange (uint64_t start, uint64_t increment);
bool IsTagAllowed (const std::string& tag);
bool InitAllowedTags ();

Expand Down
Loading
Loading