Fixed SIGSEGV

This commit is contained in:
deeaitch committed 2026-10-23 21:04:43 -04:00
1 parent f03882d3f4
commit 5b0019d50d
50 files changed
+1565 -821

No files matched your search

+25 -2
View File
@@ -40,14 +40,15 @@ make
make check
```
`parser_tests.pri` and `decoder_tests.pri` include the original test sources;
`parser_tests.pri` includes `dbc_parser_test.cpp` and `dbc_statement_test.cpp`;
`decoder_tests.pri` includes the decoder test source;
`tests.pro` also includes `dbc_coverage_test.cpp`, `dbc_lifetime_test.cpp`,
`dbc_validation_test.cpp`, and `dbc_pipeline_test.cpp`.
`../core/core.pri` supplies the library include path and linkage.
## Local regression checks
`run_core_ci.sh` builds the core archive, runs all six QtTest suites and the
`run_core_ci.sh` builds the core archive, runs all seven QtTest suites and the
qmake tree target with `make check`, then compiles and runs the standalone C++17
tree/builder test without Qt. A failed command returns a nonzero status. The
`sanitize` mode applies ASan/UBSan flags to core, tests, and the standalone
@@ -295,6 +296,28 @@ Attribute changes were verified with `qmake6 ../dbc.pro`, `make -j4`, and
passed. The standalone C++17 tree test also passed. The existing named
comma-decimal C locale test skipped because the locale is not installed.
## Long value descriptions and multiplex ranges
`dbc_statement_test.cpp` adds a separate `StatementParserTest` suite to the
existing parser qmake source list and the main test runner. Both parser APIs are
checked with 50,000-character descriptions (including quotes, backslashes,
semicolons, and spaces), 10,000 `VAL_` pairs, 10,000 multiplex ranges, and long
numeric bounds and whitespace. Valid metadata must survive parsing without
truncation, support value-description lookup after decoding, and activate or
skip multiplex branches correctly. Statements may precede or follow definitions.
Malformed long statements cover incomplete quotes/escapes, invalid escapes,
missing separators and semicolons, duplicate keys, numeric overflow, empty and
reversed ranges, trailing text, and unresolved long names. The diagnostic API
must return an empty database with the correct category, path, and source line;
the throwing API must report that same location.
Verified with Qt 6.8.2 in separate Release and ASan/UBSan builds with
`CONFIG+=dbc_qt`: each passed 2,449 core QtTest checks (including 29 checks in the
new suite), seven model checks, and the qmake tree test. One named-locale case
skipped because the locale is unavailable. Sanitizer errors were fatal; local
leak checking used `detect_leaks=0` because of the documented ptrace restriction.
## Unsupported signal value types
`unsupportedSignalValueTypes` verifies that both parser APIs reject `SIG_VALTYPE_`
+194
View File
@@ -0,0 +1,194 @@
#include "dbc_decode_builder.h"
#include "dbc_decoder.h"
#include "dbc_parser.h"
#include <QTemporaryFile>
#include <QtTest>
#include <iomanip>
#include <sstream>
#include <stdexcept>
namespace {
std::string WriteFixture(QTemporaryFile &file, const std::string &text) {
if (!file.open() || file.write(text.data(), static_cast<qint64>(text.size())) != static_cast<qint64>(text.size()) || !file.flush())
throw std::runtime_error("Cannot write statement fixture");
return file.fileName().toStdString();
}
std::string Quote(const std::string &text) {
std::ostringstream stream;
stream << std::quoted(text);
return stream.str();
}
std::string RepeatedRanges() {
std::string ranges = "1-1";
for (unsigned index = 1U; index < 10000U; ++index)
ranges += ",1-1";
return ranges;
}
const std::string definitions =
"BO_ 1 Frame: 2 ECU\n"
"SG_ Mode M : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ Value m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
}// namespace
class StatementParserTest : public QObject {
Q_OBJECT
private Q_SLOTS:
void longValueDescriptions_data();
void longValueDescriptions();
void longMultiplexRanges_data();
void longMultiplexRanges();
void invalidLongStatements_data();
void invalidLongStatements();
};
void StatementParserTest::longValueDescriptions_data() {
QTest::addColumn<std::string>("description");
QTest::addColumn<unsigned>("count");
QTest::addColumn<bool>("beforeDefinitions");
QTest::newRow("50000-character-description") << std::string(50000U, 'x') << 1U << false;
std::string escaped;
for (unsigned index = 0U; index < 12500U; ++index)
escaped += "\"\\; ";
QTest::newRow("50000-escaped-characters") << escaped << 1U << true;
QTest::newRow("10000-pairs") << std::string("Description") << 10000U << true;
QTest::newRow("50000-spaces") << std::string(50000U, ' ') << 1U << false;
}
void StatementParserTest::longValueDescriptions() {
QFETCH(std::string, description);
QFETCH(unsigned, count);
QFETCH(bool, beforeDefinitions);
std::string statement = "VAL_ 1 Value";
for (unsigned index = 0U; index < count; ++index)
statement += " " + std::to_string(index) + " " + Quote(description);
statement += ";\n";
const std::string frame = "BO_ 1 Frame: 4 ECU\nSG_ Value : 0|32@1+ (1,0) [0|1e10] \"\" ECU\n";
QTemporaryFile file;
const std::string path = WriteFixture(file, beforeDefinitions ? statement + frame : frame + statement);
const DbcParser parser;
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
QVERIFY(!result.HasErrors());
QVERIFY(result.diagnostics.empty());
const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
for (const DbcDatabase &database: databases) {
const ValueDescriptions &values = database.frames.at(0).signals.at(0).valueDescriptions;
QCOMPARE(values.size(), static_cast<std::size_t>(count));
for (unsigned index = 0U; index < count; ++index)
QCOMPARE(values.at(std::uint64_t(index)), description);
const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
RawCanFrame raw;
raw.canId = 1U;
const unsigned last = count - 1U;
raw.data = {static_cast<std::uint8_t>(last), static_cast<std::uint8_t>(last >> 8U), 0U, 0U};
const DecodedFrameValue decoded = DbcDecoder().Decode(runtime, raw);
QCOMPARE(decoded.status, FrameDecodeStatus::Valid);
const std::string *label = decoded.signals.at(0).GetValueDescription();
QVERIFY(label);
QCOMPARE(*label, description);
}
}
void StatementParserTest::longMultiplexRanges_data() {
QTest::addColumn<std::string>("ranges");
QTest::addColumn<unsigned>("count");
QTest::addColumn<bool>("beforeDefinitions");
QTest::newRow("10000-ranges") << RepeatedRanges() << 10000U << false;
const std::string paddedOne = std::string(50000U, '0') + "1";
QTest::newRow("long-bounds") << paddedOne + "-" + paddedOne << 1U << true;
QTest::newRow("long-whitespace") << std::string(50000U, ' ') + "1\t-\t1" + std::string(50000U, ' ') << 1U << false;
}
void StatementParserTest::longMultiplexRanges() {
QFETCH(std::string, ranges);
QFETCH(unsigned, count);
QFETCH(bool, beforeDefinitions);
const std::string statement = "SG_MUL_VAL_ 1 Value Mode " + ranges + ";\n";
QTemporaryFile file;
const std::string path = WriteFixture(file, beforeDefinitions ? statement + definitions : definitions + statement);
const DbcParser parser;
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
QVERIFY(!result.HasErrors());
QVERIFY(result.diagnostics.empty());
const std::vector<DbcDatabase> databases = {result.database, parser.ParseFile(path)};
for (const DbcDatabase &database: databases) {
const Multiplexing &mux = database.frames.at(0).signals.at(1).multiplexing;
QCOMPARE(mux.selector, std::string("Mode"));
QCOMPARE(mux.ranges.size(), static_cast<std::size_t>(count));
for (const MultiplexRange &range: mux.ranges) {
QCOMPARE(range.minimum, std::uint64_t(1));
QCOMPARE(range.maximum, std::uint64_t(1));
}
const DecodeDatabase runtime = DbcDecodeBuilder().Build(database);
RawCanFrame raw;
raw.canId = 1U;
raw.data = {1U, 42U};
const DecodedFrameValue active = DbcDecoder().Decode(runtime, raw);
QCOMPARE(active.status, FrameDecodeStatus::Valid);
QVERIFY(active.signals.at(1).valid);
QCOMPARE(std::get<std::uint64_t>(active.signals[1].rawValue), std::uint64_t(42));
raw.data[0] = 2U;
const DecodedFrameValue inactive = DbcDecoder().Decode(runtime, raw);
QCOMPARE(inactive.signals.at(1).status, SignalDecodeStatus::Inactive);
}
}
void StatementParserTest::invalidLongStatements_data() {
QTest::addColumn<std::string>("statement");
QTest::addColumn<DbcDiagnosticCode>("code");
const std::string text(50000U, 'x');
const std::string value = "VAL_ 1 Value 0 \"" + text;
QTest::newRow("val-missing-quote") << value + ";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-dangling-escape") << value + "\\" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-invalid-escape") << value + "\\q\";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-missing-semicolon") << value + "\"" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-trailing-text") << value + "\"; junk" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-missing-pair-separator") << value + "\"1 \"On\";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-missing-number-separator") << "VAL_ 1 Value 0\"" + text + "\";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-duplicate") << value + "\" 00 \"Duplicate\";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-overflow") << "VAL_ 1 Value " + std::string(50000U, '9') + " \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
QTest::newRow("val-long-missing-reference") << "VAL_ 1 " + text + " 0 \"Bad\";" << DbcDiagnosticCode::InvalidValueDescription;
const std::string mux = "SG_MUL_VAL_ 1 Value Mode " + RepeatedRanges();
QTest::newRow("mux-missing-semicolon") << mux << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-trailing-comma") << mux + ",;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-empty-range") << mux + ",,1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-reversed-range") << mux + ",2-1;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-signed-bound") << mux + ",+1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-suffix") << mux + "x;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-overflow") << mux + ",0-18446744073709551616;" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-long-overflow") << "SG_MUL_VAL_ 1 Value Mode 0-" + std::string(50000U, '9') + ";" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-trailing-text") << mux + "; junk" << DbcDiagnosticCode::InvalidMultiplexing;
QTest::newRow("mux-long-missing-reference") << "SG_MUL_VAL_ 1 " + text + " Mode 1-1;" << DbcDiagnosticCode::InvalidMultiplexing;
}
void StatementParserTest::invalidLongStatements() {
QFETCH(std::string, statement);
QFETCH(DbcDiagnosticCode, code);
QTemporaryFile file;
const std::string path = WriteFixture(file, "\n" + statement + "\n" + definitions);
const DbcParser parser;
const DbcParseResult result = parser.ParseFileWithDiagnostics(path);
QVERIFY(result.HasErrors());
QVERIFY(result.database.frames.empty());
QCOMPARE(result.diagnostics.size(), std::size_t(1));
const DbcDiagnostic &error = result.diagnostics[0];
QCOMPARE(error.severity, DbcDiagnosticSeverity::Error);
QCOMPARE(error.code, code);
QCOMPARE(error.filePath, path);
QCOMPARE(error.lineNumber, std::size_t(2));
try {
parser.ParseFile(path);
QFAIL("Malformed statements must throw through ParseFile");
} catch (const std::runtime_error &exception) {
QCOMPARE(std::string(exception.what()), path + ":2: " + error.message);
}
}
int RunStatementParserTests(int argc, char **argv) {
StatementParserTest tests;
return QTest::qExec(&tests, argc, argv);
}
#include "dbc_statement_test.moc"
+3 -1
View File
@@ -2,6 +2,7 @@
#include <QtTest>
int RunParserTests(int argc, char **argv);
int RunStatementParserTests(int argc, char **argv);
int RunDecoderTests(int argc, char **argv);
int RunCoverageTests(int argc, char **argv);
@@ -14,10 +15,11 @@ int RunPipelineTests(int argc, char **argv);
int main(int argc, char **argv) {
QCoreApplication application(argc, argv);
const int parserResult = RunParserTests(argc, argv);
const int statementResult = RunStatementParserTests(argc, argv);
const int decoderResult = RunDecoderTests(argc, argv);
const int coverageResult = RunCoverageTests(argc, argv);
const int lifetimeResult = RunLifetimeTests(argc, argv);
const int validationResult = RunValidationTests(argc, argv);
const int pipelineResult = RunPipelineTests(argc, argv);
return pipelineResult | validationResult | parserResult | decoderResult | coverageResult | lifetimeResult;
return statementResult | pipelineResult | validationResult | parserResult | decoderResult | coverageResult | lifetimeResult;
}
+1 -1
View File
@@ -1 +1 @@
SOURCES += $$PWD/dbc_parser_test.cpp
SOURCES += $$PWD/dbc_parser_test.cpp $$PWD/dbc_statement_test.cpp