Skip to content

Commit 688ffff

Browse files
committed
Fix segfault: replace QUANTIZE macro with static inline function
The QUANTIZE macro introduced in the rainflow.c code review expanded its second argument three times. Passing `*dh_istream++` as the argument caused the pointer to advance by 3 on every loop iteration instead of 1, quickly running past the end of the input array and producing a segfault inside spread_damage() (both RFC_SD_TRANSIENT_23 and RFC_SD_TRANSIENT_23c cases). Fix: convert QUANTIZE to a `static inline` function so the value argument is evaluated exactly once. The safety logic (bounds check for v < class_offset and overflow guard against UINT_MAX) is preserved. Applied to both src/lib/rainflow.c and src/python/lib/rainflow.c. Additional changes in this session: - CMakeLists.txt: extend debug-flag condition to cover RelWithDebInfo builds in addition to Debug, so -DDEBUG/-D_DEBUG are set for both. - src/python/cmake/rfcnt_target_functions.cmake: add per-config output directory properties (RUNTIME_OUTPUT_DIRECTORY_<CONFIG>) so that Visual Studio's multi-config generator does not append a config subfolder (e.g. Debug/, RelWithDebInfo/) to the output path. The .pyd now always lands in src/python/ regardless of configuration, making it importable without changing PYTHONPATH. - src/python/tests/test_rfcnt_npz.py: added test script loading rfcnt_example.npz, for debugging issues.
1 parent 01d90a2 commit 688ffff

8 files changed

Lines changed: 47 additions & 14 deletions

File tree

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ src/python/venv/
88
src/python/dist/
99
src/python/numpy/
1010
src/python/MANIFEST
11-
cmake-build-debug/
12-
cmake-build-release/
11+
cmake-build*
1312
rfcnt.egg-info
1413
.idea/
1514
.vscode/

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
2929
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE)
3030
endif ()
3131

32-
if (CMAKE_BUILD_TYPE MATCHES Debug)
33-
message(STATUS "Rainflow: Debug build")
32+
if (CMAKE_BUILD_TYPE MATCHES "Debug|RelWithDebInfo")
33+
message(STATUS "Rainflow: Adding debug flags for build type ${CMAKE_BUILD_TYPE}")
3434
add_definitions(-DDEBUG -D_DEBUG)
3535
endif ()
3636

src/lib/rainflow.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,13 @@ static bool error_raise ( rfc_ctx_s *,
207207
static rfc_value_t value_delta ( rfc_ctx_s *, const rfc_value_tuple_s* pt_from, const rfc_value_tuple_s* pt_to, int *sign_ptr );
208208

209209

210-
#define QUANTIZE( r, v ) ( (r)->class_count && (v) >= (r)->class_offset ? \
211-
( ((v) - (r)->class_offset) / (r)->class_width >= (double)UINT_MAX ? UINT_MAX : \
212-
(unsigned)( ((v) - (r)->class_offset) / (r)->class_width ) ) : 0 )
210+
static inline unsigned QUANTIZE( const rfc_ctx_s *r, rfc_value_t v )
211+
{
212+
double q;
213+
if( !r->class_count || v < r->class_offset ) return 0;
214+
q = ( v - r->class_offset ) / r->class_width;
215+
return ( q >= (double)UINT_MAX ) ? UINT_MAX : (unsigned)q;
216+
}
213217
#define AMPLITUDE( r, i ) ( (r)->class_count ? ( (double)(r)->class_width * (i) / 2 ) : 0.0 )
214218
#define CLASS_MEAN( r, c ) ( (r)->class_count ? ( (double)(r)->class_width * (0.5 + (c)) + (r)->class_offset ) : 0.0 )
215219
#define CLASS_UPPER( r, c ) ( (r)->class_count ? ( (double)(r)->class_width * (1.0 + (c)) + (r)->class_offset ) : 0.0 )

src/python/cmake/rfcnt_target_functions.cmake

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,23 @@ function (rfcnt_library OUTPUT_NAME OUTPUT_DIRECTORY rfcnt_target)
8989
# Windows python modules have suffix ".pyd"
9090

9191
if (OUTPUT_DIRECTORY)
92+
# Set per-config output dirs so multi-config generators (VS, Xcode) don't
93+
# append a config subfolder (Debug/, Release/, …). Without these, the .pyd
94+
# lands in e.g. src/python/Debug/ and Python can't find it.
95+
set(_out_dir "${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_DIRECTORY}")
9296
set_target_properties(
9397
${rfcnt_target}
9498
PROPERTIES
95-
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_DIRECTORY} # Output directory
96-
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_DIRECTORY} # Output directory
99+
RUNTIME_OUTPUT_DIRECTORY "${_out_dir}"
100+
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${_out_dir}"
101+
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${_out_dir}"
102+
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${_out_dir}"
103+
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${_out_dir}"
104+
LIBRARY_OUTPUT_DIRECTORY "${_out_dir}"
105+
LIBRARY_OUTPUT_DIRECTORY_DEBUG "${_out_dir}"
106+
LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${_out_dir}"
107+
LIBRARY_OUTPUT_DIRECTORY_RELEASE "${_out_dir}"
108+
LIBRARY_OUTPUT_DIRECTORY_MINSIZEREL "${_out_dir}"
97109
)
98110
install(
99111
TARGETS ${rfcnt_target}

src/python/lib/rainflow.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,13 @@ static bool error_raise ( rfc_ctx_s *,
207207
static rfc_value_t value_delta ( rfc_ctx_s *, const rfc_value_tuple_s* pt_from, const rfc_value_tuple_s* pt_to, int *sign_ptr );
208208

209209

210-
#define QUANTIZE( r, v ) ( (r)->class_count && (v) >= (r)->class_offset ? \
211-
( ((v) - (r)->class_offset) / (r)->class_width >= (double)UINT_MAX ? UINT_MAX : \
212-
(unsigned)( ((v) - (r)->class_offset) / (r)->class_width ) ) : 0 )
210+
static inline unsigned QUANTIZE( const rfc_ctx_s *r, rfc_value_t v )
211+
{
212+
double q;
213+
if( !r->class_count || v < r->class_offset ) return 0;
214+
q = ( v - r->class_offset ) / r->class_width;
215+
return ( q >= (double)UINT_MAX ) ? UINT_MAX : (unsigned)q;
216+
}
213217
#define AMPLITUDE( r, i ) ( (r)->class_count ? ( (double)(r)->class_width * (i) / 2 ) : 0.0 )
214218
#define CLASS_MEAN( r, c ) ( (r)->class_count ? ( (double)(r)->class_width * (0.5 + (c)) + (r)->class_offset ) : 0.0 )
215219
#define CLASS_UPPER( r, c ) ( (r)->class_count ? ( (double)(r)->class_width * (1.0 + (c)) + (r)->class_offset ) : 0.0 )

src/python/tests/rfcnt_example.npz

8.25 KB
Binary file not shown.

src/python/tests/test_rfcnt_npz.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import numpy as np
2+
import rfcnt
3+
4+
print(f"Using rfcnt from {rfcnt.__file__}")
5+
6+
file = "rfcnt_example.npz"
7+
print(f"Loading data from {file}...")
8+
arr = np.load(file)["arr"]
9+
10+
# input("Press Enter")
11+
12+
cw = np.ptp(arr) / 99
13+
result = rfcnt.rfc(arr, class_width=cw, class_offset=arr.min() - cw/2)
14+
print(result)

src/python/version.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
_version = (0, 5, 2, "")
1+
_version = (0, 5, 2, ".post2")
22
__version__ = "%d.%d.%d%s" % _version
3-
__author__ = "Andreas Martin"
3+
__author__ = "Andreas Martin"

0 commit comments

Comments
 (0)