diff --git a/CMakePresets.json b/CMakePresets.json index 9e68e113f..a4e01a49e 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -109,7 +109,7 @@ { "name": "unix-sanitizer", "environment": { - "SANITIZER_FLAGS": "-fsanitize=address" + "SANITIZER_FLAGS": "-fstack-protector -fsanitize=address,pointer-compare,undefined" }, "inherits": "unix-base", "hidden": true @@ -137,6 +137,9 @@ { "name": "msvc-debug", "displayName": "Debug (MSVC)", + "environment": { + "SANITIZER_FLAGS": "/RTC1" + }, "inherits": [ "msvc-base", "debug" diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index 3108f6307..970dc5b6c 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -97,14 +97,14 @@ class ColumnarAttributeRange : public std::ranges::view_interface { - AttributeType const* buffer_ptr = - reinterpret_cast(attribute_buffer.data) + idx_; - auto& attribute_ref = - meta_attribute.template get_attribute(reinterpret_cast(&result)); - attribute_ref = *buffer_ptr; - }); + ctype_func_selector(meta_attribute.ctype, [&result, &attribute_buffer, &meta_attribute, + idx = idx_] { + AttributeType const* buffer_ptr = + reinterpret_cast(attribute_buffer.data) + idx; + AttributeType& attribute_ref = + meta_attribute.template get_attribute(reinterpret_cast(&result)); + attribute_ref = *buffer_ptr; + }); } return result; } diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp index 9f76876fb..33fe9e200 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp @@ -289,6 +289,9 @@ template class YBus { std::shared_ptr const> const& param, std::shared_ptr const& y_bus_struct = {}) : math_topology_{topo_ptr} { + assert(math_topology_ != nullptr); + assert(param != nullptr); + // use existing struct or make new struct if (y_bus_struct) { y_bus_struct_ = y_bus_struct; @@ -447,29 +450,32 @@ template class YBus { template requires std::same_as> || std::same_as> std::vector calculate_branch_flow(ComplexValueVector const& u) const { - std::vector branch_flow(math_topology_->branch_bus_idx.size()); - std::transform(math_topology_->branch_bus_idx.cbegin(), math_topology_->branch_bus_idx.cend(), - math_model_param_->branch_param.cbegin(), branch_flow.begin(), - [&u](BranchIdx branch_idx, BranchCalcParam const& param) { - auto const [f, t] = branch_idx; - // if one side is disconnected, use zero voltage at that side - ComplexValue const uf = f != -1 ? u[f] : ComplexValue{0.0}; - ComplexValue const ut = t != -1 ? u[t] : ComplexValue{0.0}; - T output; - - // See "Branch Flow Calculation" in "State Estimation Alliander" - output.i_f = dot(param.yff(), uf) + dot(param.yft(), ut); - output.i_t = dot(param.ytf(), uf) + dot(param.ytt(), ut); - - if constexpr (std::same_as>) { - // See "Shunt Injection Flow Calculation" in "State Estimation Alliander" - output.s_f = uf * conj(output.i_f); - output.s_t = ut * conj(output.i_t); - } - - return output; - }); - return branch_flow; + assert(math_topology_ != nullptr); + + return std::views::zip(std::as_const(math_topology_->branch_bus_idx), + std::as_const(math_model_param_->branch_param)) | + std::views::transform([&u](auto const& branch_idx_params) -> T { + auto const& [branch_idx, param] = branch_idx_params; + + auto const [f, t] = branch_idx; + // if one side is disconnected, use zero voltage at that side + ComplexValue const uf = f != -1 ? u[f] : ComplexValue{0.0}; + ComplexValue const ut = t != -1 ? u[t] : ComplexValue{0.0}; + T output; + + // See "Branch Flow Calculation" in "State Estimation Alliander" + output.i_f = dot(param.yff(), uf) + dot(param.yft(), ut); + output.i_t = dot(param.ytf(), uf) + dot(param.ytt(), ut); + + if constexpr (std::same_as>) { + // See "Shunt Injection Flow Calculation" in "State Estimation Alliander" + output.s_f = uf * conj(output.i_f); + output.s_t = ut * conj(output.i_t); + } + + return output; + }) | + std::ranges::to>(); } // calculate shunt flow based on voltage, injection direction @@ -477,6 +483,8 @@ template class YBus { requires std::same_as> || std::same_as> std::vector calculate_shunt_flow(ComplexValueVector const& u) const { + assert(math_topology_ != nullptr); + std::vector shunt_flow(math_topology_->n_shunt()); for (auto const [bus, shunts] : enumerated_zip_sequence(math_topology_->shunts_per_bus)) { for (Idx const shunt : shunts) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp index 367792666..817d9342f 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/topology.hpp @@ -353,8 +353,9 @@ class Topology { }; // k as branch number for 2-way branch for (auto const& [idx, branch_node_idx, branch_connected] : - std::views::zip(std::views::iota(0), comp_topo_.branch_node_idx, comp_conn_.branch_connected)) { - assert(std::ssize(branch_connected) == 2); // NOSONAR(R354) + std::views::zip(std::views::iota(0), std::as_const(comp_topo_.branch_node_idx), + std::as_const(comp_conn_.branch_connected))) { + assert(std::ssize(branch_connected) == 2); auto const [i, j] = branch_node_idx; IntS const i_status = branch_connected[0]; @@ -387,8 +388,9 @@ class Topology { } // k as branch number for 3-way branch for (auto const& [idx, i, i_status, j_math] : - std::views::zip(std::views::iota(0), comp_topo_.branch3_node_idx, comp_conn_.branch3_connected, - std::views::drop(comp_coup_.node, comp_topo_.n_node))) { + std::views::zip(std::views::iota(0), std::as_const(comp_topo_.branch3_node_idx), + std::as_const(comp_conn_.branch3_connected), + std::views::drop(std::as_const(comp_coup_.node), comp_topo_.n_node))) { std::array const i_math{ comp_coup_.node[i[0]], comp_coup_.node[i[1]], @@ -553,7 +555,8 @@ class Topology { std::ranges::for_each(math_topology_, [](MathModelTopology& topo) { topo.load_gen_type.resize(topo.n_load_gen()); }); // assign load type - for (auto const& [idx_math, load_gen_type] : std::views::zip(comp_coup_.load_gen, comp_topo_.load_gen_type)) { + for (auto const& [idx_math, load_gen_type] : + std::views::zip(std::as_const(comp_coup_.load_gen), std::as_const(comp_topo_.load_gen_type))) { if (idx_math.group == -1) { continue; } diff --git a/power_grid_model_c/power_grid_model_c/CMakeLists.txt b/power_grid_model_c/power_grid_model_c/CMakeLists.txt index 14a8ad986..e74c5d5b5 100644 --- a/power_grid_model_c/power_grid_model_c/CMakeLists.txt +++ b/power_grid_model_c/power_grid_model_c/CMakeLists.txt @@ -49,6 +49,32 @@ set_target_properties( INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE ) +# Always-on hardening +target_compile_definitions( + power_grid_model_c + PRIVATE + "$<$,$>:_FORTIFY_SOURCE=2>" + "$<$:_ITERATOR_DEBUG_LEVEL=$,2,1>>" +) +target_compile_options( + power_grid_model_c + BEFORE + PRIVATE + "$<$,$>:-fstack-protector;-fsanitize=undefined>" + "$<$:-static-libubsan>" + "$<$:-lubsan>" + "$<$,$,$>>>:-fsanitize-minimal-runtime>" +) +target_link_options( + power_grid_model_c + BEFORE + PRIVATE + "$<$,$>:-fstack-protector;-fsanitize=undefined>" + "$<$:-static-libubsan>" + "$<$:-lubsan>" + "$<$,$>>:-fsanitize-minimal-runtime>" +) + install( TARGETS power_grid_model_c EXPORT power_grid_modelTargets diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index e58c80b72..c8f998db9 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -25,4 +25,9 @@ target_compile_definitions( PRIVATE PGM_ENABLE_EXPERIMENTAL ) +set_target_properties( + power_grid_model_api_tests + PROPERTIES BUILD_RPATH $ +) + doctest_discover_tests(power_grid_model_api_tests)