Skip to content

Commit 6287cf4

Browse files
AnHeuermannclaude
andauthored
Re-enabling ASAN tests (#1617)
* SystemSC: size the per-FMU flag arrays in initialize(), not before fmus is filled, where they were always zero length (heap-buffer-overflow) * AlgLoop: own the KinsolSolver, it was never deleted * Enable ASAN for Clang, error out on compilers that cannot sanitize * Add testsuite/lsan-suppressions.txt for wrapper, CPython and lxml leaks * Mark 23 tests asan: no - they leak inside third-party FMU binaries Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent ef2cb7a commit 6287cf4

118 files changed

Lines changed: 267 additions & 78 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/coverage.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ jobs:
2525
submodules: recursive
2626

2727
# Everything else OMSimulator needs is vendored in 3rdParty/. flex is for
28-
# omc-diff, which the testsuite compares result files with, and numpy for
29-
# the CompositeModels tests that import it.
28+
# omc-diff, which the testsuite compares result files with.
3029
- name: Install dependencies
3130
run: |
3231
sudo apt-get update
33-
sudo apt-get install -y cmake g++ flex python3 python3-numpy gcovr ccache
32+
sudo apt-get install -y cmake g++ flex python3 gcovr ccache
3433
3534
# Only 3rdParty benefits much: the instrumented sources are rebuilt at -O0
3635
# anyway. It still takes the bulk of a cold build off the critical path.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ imgui.ini
1515
/OMSimulator.creator.user
1616
/OMSimulator.files
1717
/OMSimulator.includes
18+
19+
# Python files
20+
__pycache__/

Jenkinsfile

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pipeline {
88
booleanParam(name: 'MACOS_ARM64', defaultValue: false, description: 'Build with macOS-arm64 (M1 mac)')
99
booleanParam(name: 'SUBMODULE_UPDATE', defaultValue: false, description: 'Allow pull request to update submodules (disabled by default due to common user errors)')
1010
booleanParam(name: 'UPLOAD_BUILD_OPENMODELICA', defaultValue: false, description: 'Upload install artifacts to build.openmodelica.org/omsimulator. Activates MINGW_UCRT64 as well.')
11-
booleanParam(name: 'ASAN', defaultValue: false, description: 'Build and test with AddressSanitizer (disabled by default while the reported leaks are unfixed)')
1211
string(name: 'CTEST_FLAGS', defaultValue: '', description: 'Extra flags passed to ctest, e.g. -R api')
1312
}
1413
stages {
@@ -48,12 +47,6 @@ pipeline {
4847
stage('build-in-parallel') {
4948
parallel {
5049
stage('linux64-resolute-asan') {
51-
// Disabled until the leaks AddressSanitizer reports have been fixed.
52-
// Set the ASAN build parameter to run it in the meantime.
53-
when {
54-
expression { return params.ASAN }
55-
beforeAgent true
56-
}
5750
stages {
5851
stage('build-asan') {
5952
agent {

README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
# OMSimulator [![License: OSMC-PL](https://img.shields.io/badge/license-OSMC--PL-lightgrey.svg)](OSMC-License.txt)
1+
# OMSimulator
2+
3+
[![License: OSMC-PL](https://img.shields.io/badge/license-OSMC--PL-lightgrey.svg)](OSMC-License.txt)
4+
[![codecov](https://codecov.io/gh/OpenModelica/OMSimulator/branch/master/graph/badge.svg?token=HmYNZuQtNA)](https://codecov.io/gh/OpenModelica/OMSimulator)
25

36
The OpenModelica FMI & SSP-based co-simulation environment.
47

5-
### Branch Overview
8+
## Branch Overview
69

710
- **`master`**: Development branch for active, unstable updates.
811
- **`maintenance/v2.1`**: Stable branch for version 2.1 with patch updates (latest release: **v2.1.3**).
912
- **`maintenance/v2.0`**: Stable branch for version 2.0 with patch updates (latest release: **v2.0.1**).
1013

11-
### Overview
14+
## Overview
1215

1316
OMSimulator can be used as:
1417

@@ -18,18 +21,18 @@ OMSimulator can be used as:
1821

1922
OMSimulator is also included with the OpenModelica installer, which includes OMEdit, a graphical editor.
2023

21-
* [OpenModelica](https://openmodelica.org/)
22-
* [Standalone package](https://build.openmodelica.org/omsimulator/)
24+
- [OpenModelica](https://openmodelica.org/)
25+
- [Standalone package](https://build.openmodelica.org/omsimulator/)
2326

24-
### Documentation
27+
## Documentation
2528

2629
Latest documentation:
2730

2831
- [User Guide PDF](https://openmodelica.org/doc/OMSimulator/master/OMSimulator/UsersGuide/pdf/OMSimulator.pdf)
2932
- [User Guide HTML](https://openmodelica.org/doc/OMSimulator/master/OMSimulator/UsersGuide/html/)
3033
- [OMSimulatorLib Doxygen](https://openmodelica.org/doc/OMSimulator/master/OMSimulator/doxygen/html/index.html)
3134

32-
### Testing and Coverage
35+
## Testing and Coverage
3336

3437
OMSimulator's coverage tests provide insights across a wide range of publicly available libraries. For an overview of all tested libraries and their detailed reports, visit the [Coverage Test Overview](https://libraries.openmodelica.org/branches/master-fmi/).
3538

@@ -38,10 +41,10 @@ OMSimulator's coverage tests provide insights across a wide range of publicly av
3841

3942
> **Note**: Issues may be attributed to OMSimulator itself or the FMI export in OpenModelica.
4043
41-
### Build Instructions
44+
## Build Instructions
4245

4346
To compile the project, please follow the steps outlined in the [BUILD.md](BUILD.md) file.
4447

45-
### License
48+
## License
4649

4750
This project is licensed under the OSMC Public License. See [OSMC-License.txt](OSMC-License.txt) for details.

src/OMSimulatorLib/AlgLoop.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons
300300
{
301301
int flag;
302302
int printLevel;
303-
KinsolSolver* kinsolSolver = new KinsolSolver();
303+
/* Held by a unique_ptr until it is complete, so that the error returns below
304+
* do not leak the object and the SUNDIALS memory it allocated so far. */
305+
std::unique_ptr<KinsolSolver> kinsolSolver(new KinsolSolver());
304306

305307
logDebug("Create new KinsolSolver object for algebraic loop number " + std::to_string(algLoopNum));
306308

@@ -392,7 +394,7 @@ oms::KinsolSolver* oms::KinsolSolver::NewKinsolSolver(const int algLoopNum, cons
392394
fScaleData[i] = 1.0;
393395
}
394396

395-
return kinsolSolver;
397+
return kinsolSolver.release();
396398
}
397399

398400
/**
@@ -488,8 +490,8 @@ oms::AlgLoop::AlgLoop(oms_alg_solver_enu_t method, double relativeTolerance, scc
488490

489491
if (method == oms_alg_solver_kinsol)
490492
{
491-
kinsolData = KinsolSolver::NewKinsolSolver(systNumber, SCC.connections.size(), relativeTolerance, useDirectionalDerivative);
492-
if (kinsolData==NULL)
493+
kinsolData.reset(KinsolSolver::NewKinsolSolver(systNumber, SCC.connections.size(), relativeTolerance, useDirectionalDerivative));
494+
if (!kinsolData)
493495
{
494496
logError("NewKinsolSolver() failed. Aborting!");
495497
throw("AlgLoop() failed!");

src/OMSimulatorLib/AlgLoop.h

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#ifndef _OMS_ALGLOOP_H_
3737
#define _OMS_ALGLOOP_H_
3838

39+
#include <memory>
3940
#include <string>
4041
#include <vector>
4142
#include "OMSimulator/Types.h"
@@ -65,24 +66,27 @@ namespace oms
6566
oms_status_enu_t kinsolSolve(System& syst, DirectedGraph& graph);
6667

6768
private:
69+
/* All members start out empty, so that the destructor can be run on an
70+
* object NewKinsolSolver() gave up on half way through. */
71+
6872
/* tolerances */
69-
double fnormtol; /* function tolerance */
73+
double fnormtol = 0.0; /* function tolerance */
7074

7175
/* work arrays */
72-
N_Vector initialGuess;
73-
N_Vector uScale; /* Scaling vector for u */
74-
N_Vector fScale; /* Scaling vector for f(u) */
75-
N_Vector fTmp; /* Vector used for tmp computations */
76+
N_Vector initialGuess = nullptr;
77+
N_Vector uScale = nullptr; /* Scaling vector for u */
78+
N_Vector fScale = nullptr; /* Scaling vector for f(u) */
79+
N_Vector fTmp = nullptr; /* Vector used for tmp computations */
7680

7781
/* kinsol internal data */
78-
void* kinsolMemory;
79-
void* user_data;
80-
int size;
82+
void* kinsolMemory = nullptr;
83+
void* user_data = nullptr;
84+
int size = 0;
8185

8286
/* linear solver data */
83-
SUNLinearSolver linSol; /* Linear solver object used by KINSOL */
84-
N_Vector y; /* Template for cloning vectors needed inside linear solver */
85-
SUNMatrix J; /* (Non-)Sparse matrix template for cloning matrices needed within linear solver */
87+
SUNLinearSolver linSol = nullptr; /* Linear solver object used by KINSOL */
88+
N_Vector y = nullptr; /* Template for cloning vectors needed inside linear solver */
89+
SUNMatrix J = nullptr; /* (Non-)Sparse matrix template for cloning matrices needed within linear solver */
8690

8791
/* member function */
8892
static int nlsKinsolJac(N_Vector u, N_Vector fu, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2);
@@ -105,7 +109,9 @@ namespace oms
105109
oms_alg_solver_enu_t algSolverMethod;
106110
oms_status_enu_t fixPointIteration(System& syst, DirectedGraph& graph);
107111

108-
KinsolSolver* kinsolData;
112+
/* Owns the solver: an AlgLoop is created as a temporary and moved into
113+
* System::algLoops, so the copy constructor has to stay deleted. */
114+
std::unique_ptr<KinsolSolver> kinsolData;
109115

110116
/* Loop data */
111117
const scc_t SCC; ///< Strong connected components

src/OMSimulatorLib/CMakeLists.txt

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,45 @@ project(OMSimulatorLib)
22

33
find_package(Threads)
44

5+
# GCC and Clang both work, but they are slightly different
56
if (ASAN)
67
set(CMAKE_BUILD_TYPE Debug)
7-
ENDIF ()
88

9-
IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
9+
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) OR
10+
(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.1))
11+
message(FATAL_ERROR
12+
"ASAN=ON needs GCC 5.1 or newer, or Clang 3.1 or newer, but the selected "
13+
"C++ compiler is ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} "
14+
"(${CMAKE_CXX_COMPILER}). Configure with a supported compiler, e.g. "
15+
"-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, or build without -DASAN=ON.")
16+
endif ()
17+
18+
message(STATUS "AddressSanitizer enabled")
19+
add_compile_options("-fsanitize=address" "-fno-omit-frame-pointer")
20+
link_libraries("-fsanitize=address")
21+
endif ()
22+
23+
# Be pedantic about potential memory issues
24+
if (MSVC)
25+
# MSVC doesn't know -Werror=<name>, its warnings only have numbers.
26+
set(OMS_MSVC_MEMORY_WARNINGS
27+
4700 # uninitialized local variable used (-Wuninitialized)
28+
4701 # potentially uninitialized local variable used (-Wmaybe-uninitialized)
29+
4703 # potentially uninitialized local pointer used (-Wmaybe-uninitialized)
30+
4715 # not all control paths return a value (-Wreturn-type)
31+
4716 # must return a value (-Wreturn-type)
32+
4172 # returning address of local variable or temporary (-Wreturn-local-addr)
33+
)
34+
foreach (warning IN LISTS OMS_MSVC_MEMORY_WARNINGS)
35+
# /w1<n> reports the warning at the default warning level (C4701 and C4703
36+
# are level 4 and would stay silent otherwise), /we<n> turns it into an error.
37+
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /w1${warning} /we${warning}")
38+
endforeach ()
39+
else ()
1040
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=uninitialized")
11-
ELSEIF (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.1)
12-
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=uninitialized")
13-
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-local-addr")
1441
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-type")
15-
if (ASAN)
16-
message(STATUS "AddressSanitizer enabled")
17-
add_compile_options("-fsanitize=address" "-fno-omit-frame-pointer")
18-
link_libraries("-fsanitize=address")
19-
ENDIF ()
20-
ENDIF ()
42+
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror=return-local-addr")
43+
endif ()
2144

2245
set(OMSIMULATORLIB_SOURCES
2346
AlgLoop.cpp

src/OMSimulatorLib/SystemSC.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,11 @@ oms_status_enu_t oms::SystemSC::initialize()
319319
event_indicators_prev.push_back((double*)calloc(nEventIndicators.back(), sizeof(double)));
320320
}
321321

322+
// Now that fmus is filled, the per-FMU flags the integrator steps write to
323+
// can be sized. make_unique value-initializes them, i.e. all false.
324+
callEventUpdate = std::make_unique<bool[]>(fmus.size());
325+
terminateSimulation = std::make_unique<bool[]>(fmus.size());
326+
322327
if (n_states == 0)
323328
logInfo("model doesn't contain any continuous state");
324329

@@ -511,8 +516,8 @@ oms_status_enu_t oms::SystemSC::terminate()
511516
}
512517

513518
fmus.clear();
514-
delete[] callEventUpdate;
515-
delete[] terminateSimulation;
519+
callEventUpdate.reset();
520+
terminateSimulation.reset();
516521
nStates.clear();
517522
nEventIndicators.clear();
518523
states.clear();
@@ -579,6 +584,8 @@ oms_status_enu_t oms::SystemSC::reset()
579584
for (double* ptr : event_indicators) free(ptr);
580585
for (double* ptr : event_indicators_prev) free(ptr);
581586
fmus.clear();
587+
callEventUpdate.reset();
588+
terminateSimulation.reset();
582589
nStates.clear();
583590
nEventIndicators.clear();
584591
states.clear();

src/OMSimulatorLib/SystemSC.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
#include "System.h"
4141
#include "OMSimulator/Types.h"
4242

43+
#include <memory>
44+
4345
#include <cvode/cvode.h> /* prototypes for CVODE fcts., consts. */
4446
#include <nvector/nvector_serial.h> /* serial N_Vector types, fcts., macros */
4547
#include <sunlinsol/sunlinsol_dense.h> /* Default dense linear solver */
@@ -90,8 +92,9 @@ namespace oms
9092
private:
9193
std::vector<Component*> fmus; // use Component Base class to support FMI 2 ME and FMI 3 ME
9294

93-
bool* callEventUpdate = new bool[fmus.size()](); //initialize with false
94-
bool* terminateSimulation = new bool[fmus.size()](); //initialize with false
95+
// One entry per FMU, allocated by initialize() once fmus is filled.
96+
std::unique_ptr<bool[]> callEventUpdate; //initialized with false
97+
std::unique_ptr<bool[]> terminateSimulation; //initialized with false
9598

9699
std::vector<size_t> nStates;
97100
std::vector<size_t> nEventIndicators;

testsuite/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ Special directives in the header of a test file:
6161
* `asan: yes`
6262

6363
Also run the test case in an AddressSanitizer build (`-DOMS_TESTSUITE_ASAN=ON`), where all other
64-
tests are skipped.
64+
tests are skipped. Such a run leak checks the `OMSimulatorPython3` wrapper and the interpreter
65+
as well; the leaks those report on their own are ignored through `lsan-suppressions.txt`.
6566
* `env: KEY=VALUE ...`
6667

6768
Extra environment variables for the test case.

0 commit comments

Comments
 (0)