Skip to content

Commit 3dcc63e

Browse files
committed
Setup a sanitized build for CSP
Signed-off-by: Adam Glustein <[email protected]>
1 parent 53bc788 commit 3dcc63e

File tree

10 files changed

+63
-7
lines changed

10 files changed

+63
-7
lines changed

CMakeLists.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ option(CSP_MANYLINUX "Build for python's manylinux setup" OFF)
6767
option(CSP_USE_VCPKG "Build with vcpkg dependencies" ON)
6868
option(CSP_USE_CCACHE "Build with ccache caching" OFF)
6969
option(CSP_USE_LD_CLASSIC_MAC "On macOS, link with ld_classic" OFF)
70+
option(CSP_ENABLE_ASAN "Build with address sanitizer" OFF)
71+
option(CSP_ENABLE_UBSAN "Build with undefined behavior sanitizer" OFF)
7072

7173
# Extension options
7274
option(CSP_BUILD_KAFKA_ADAPTER "Build kafka adapter" ON)
@@ -213,6 +215,18 @@ else()
213215
endif()
214216
endif()
215217

218+
if(CSP_ENABLE_ASAN)
219+
message(STATUS "Enabling Address Sanitizer")
220+
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
221+
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
222+
endif()
223+
224+
if(CSP_ENABLE_UBSAN)
225+
message(STATUS "Enabling Undefined Behavior Sanitizer")
226+
add_compile_options(-fsanitize=undefined -fno-omit-frame-pointer)
227+
add_link_options(-fsanitize=undefined -fno-omit-frame-pointer)
228+
endif()
229+
216230

217231
###################################################################################################################################################
218232
# Messages #

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ develop: requirements ## install dependencies and build library
2020
build: ## build the library
2121
python setup.py build build_ext --inplace
2222

23+
build-sanitizer: ## build the library with ASAN and UBSAN enabled
24+
CSP_ENABLE_ASAN=1 CSP_ENABLE_UBSAN=1 python setup.py build build_ext --inplace
25+
2326
build-debug: ## build the library ( DEBUG ) - May need a make clean when switching from regular build to build-debug and vice versa
2427
SKBUILD_CONFIGURE_OPTIONS="" DEBUG=1 python setup.py build build_ext --inplace
2528

2629
build-conda: ## build the library in Conda
2730
python setup.py build build_ext --csp-no-vcpkg --inplace
2831

32+
build-conda-sanitizer: ## build the library in Conda with ASAN and UBSAN enabled
33+
CSP_ENABLE_ASAN=1 CSP_ENABLE_UBSAN=1 python setup.py build build_ext --csp-no-vcpkg --inplace
34+
2935
install: ## install library
3036
python -m pip install .
3137

@@ -84,6 +90,10 @@ TEST_ARGS :=
8490
test-py: ## Clean and Make unit tests
8591
python -m pytest -v csp/tests --junitxml=junit.xml $(TEST_ARGS)
8692

93+
test-py-sanitizer: ## Clean and Make unit tests with sanitizers enabled
94+
ASAN_OPTIONS=detect_leaks=0,detect_stack_use_after_return=true,use_odr_indicator=1,strict_init_order=true,strict_string_checks=true LD_PRELOAD=$$(gcc -print-file-name=libasan.so) \
95+
python -m pytest -v csp/tests --junitxml=junit.xml $(TEST_ARGS)
96+
8797
test-cpp: ## Make C++ unit tests
8898
ifneq ($(OS),Windows_NT)
8999
for f in ./csp/tests/bin/*; do $$f; done || (echo "TEST FAILED" && exit 1)

cpp/cmake/modules/Findcsp_autogen.cmake

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,20 @@ function(csp_autogen MODULE_NAME DEST_FILENAME HEADER_NAME_OUTVAR SOURCE_NAME_OU
2626
set(CSP_AUTOGEN_PYTHONPATH ${PROJECT_BINARY_DIR}/lib:${CMAKE_SOURCE_DIR}:$$PYTHONPATH)
2727
endif()
2828

29+
if(CSP_ENABLE_ASAN)
30+
execute_process(
31+
COMMAND gcc -print-file-name=libasan.so
32+
OUTPUT_VARIABLE ASAN_LIB_PATH
33+
OUTPUT_STRIP_TRAILING_WHITESPACE
34+
)
35+
# Turn off leak checks as we are using PyMalloc when we run autogen
36+
set(ASAN_PRELOAD_CMD "ASAN_OPTIONS=detect_leaks=0" "LD_PRELOAD=${ASAN_LIB_PATH}")
37+
else()
38+
set(ASAN_PRELOAD_CMD "")
39+
endif()
40+
2941
add_custom_command(OUTPUT "${CSP_AUTOGEN_CPP_OUT}" "${CSP_AUTOGEN_H_OUT}"
30-
COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS}
42+
COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${ASAN_PRELOAD_CMD} ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS}
3143
COMMENT "generating csp c++ types from module ${MODULE_NAME}"
3244
DEPENDS mkdir_autogen_${MODULE_TARGETNAME}
3345
${CSP_AUTOGEN_MODULE_PATH}

cpp/csp/core/DynamicBitSet.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ class DynamicBitSet
141141
{
142142
node_type * old = m_nodes;
143143
m_nodes = new node_type[ newNodes ];
144-
memcpy( m_nodes, old, m_numNodes * sizeof( node_type ) );
144+
if( likely( m_numNodes > 0 ) )
145+
memcpy( m_nodes, old, m_numNodes * sizeof( node_type ) );
145146
memset( m_nodes + m_numNodes, 0, ( newNodes - m_numNodes ) * sizeof( node_type ) );
146147

147148
m_numNodes = newNodes;

cpp/csp/core/Exception.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static void printBacktrace( char ** messages, int size, std::ostream & dest )
3434
{
3535
char *begin_name = 0, *begin_offset = 0;
3636
char tmp[1024];
37-
strncpy( tmp, messages[i], sizeof(tmp) );
37+
strncpy( tmp, messages[i], sizeof(tmp) - 1 );
3838
tmp[ sizeof( tmp ) - 1 ] = 0;
3939

4040
// find parentheses and +address offset surrounding the mangled name:

cpp/csp/python/cspbaselibimpl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,14 @@ DECLARE_CPPNODE( exprtk_impl )
273273
csp.make_passive( inputs );
274274
}
275275

276+
virtual ~exprtk_impl()
277+
{
278+
// Need to release the expression before clearing values/symbol table
279+
// https://github.com/ArashPartow/exprtk/blob/cc1b800c2bd1ac3ac260478c915d2aec6f4eb41c/readme.txt#L909
280+
s_expr.release();
281+
s_valuesContainer.clear();
282+
}
283+
276284
INVOKE()
277285
{
278286
if( use_trigger )

cpp/csp/python/npstatsimpl.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,12 +1303,12 @@ DECLARE_CPPNODE( _np_arg_min_max )
13031303
PyArray_Descr *descr;
13041304
PyArray_DescrConverter( date_type, &descr );
13051305
Py_XDECREF( date_type );
1306-
DateTime * values = new DateTime[s_elem.size()];
1306+
1307+
PyObject * out = PyArray_NewFromDescr( &PyArray_Type, descr, s_shp.m_dims.size(), &s_shp.m_dims[0], NULL, NULL, 0, NULL );
1308+
DateTime * values = static_cast<DateTime *>( PyArray_DATA( ( PyArrayObject * )out ) );
13071309
for( size_t i = 0; i < s_elem.size(); ++i )
13081310
values[i] = s_elem[i].compute_dt();
1309-
1310-
PyObject * out = PyArray_NewFromDescr( &PyArray_Type, descr, s_shp.m_dims.size(), &s_shp.m_dims[0], NULL, values, 0, NULL );
1311-
PyArray_ENABLEFLAGS( ( PyArrayObject * ) out, NPY_ARRAY_OWNDATA );
1311+
13121312
RETURN( PyObjectPtr::own( out ) );
13131313
}
13141314
}

cpp/tests/engine/test_tick_buffer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,8 @@ TEST( TickBufferTest, test_flatten )
9898
ASSERT_EQ( values_wrap[ i ], i + 3 );
9999
ASSERT_EQ( values_nowrap[ i ], i );
100100
}
101+
102+
free( values_wrap );
103+
free( values_nowrap );
104+
free( values_single );
101105
}

csp/tests/test_engine.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import numpy as np
1616
import psutil
17+
import pytest
1718

1819
import csp
1920
from csp import PushMode, ts
@@ -1175,6 +1176,10 @@ def list_comprehension_bug_graph():
11751176
rv = csp.run(list_comprehension_bug_graph, starttime=datetime(2020, 1, 1))["Bucket"]
11761177
self.assertEqual([v[1][0] for v in rv[10:]], list(range(20)))
11771178

1179+
@unittest.skipIf(
1180+
os.environ.get("ASAN_OPTIONS") is not None,
1181+
reason="Test skipped when AddressSanitizer is enabled, RSS usage is much larger than usual",
1182+
)
11781183
def test_alarm_leak(self):
11791184
"""this was a leak in Scheduler.cpp"""
11801185

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
("CSP_BUILD_KAFKA_ADAPTER", "1"),
2424
("CSP_BUILD_PARQUET_ADAPTER", "1"),
2525
("CSP_BUILD_WS_CLIENT_ADAPTER", "1"),
26+
("CSP_ENABLE_ASAN", "0"),
27+
("CSP_ENABLE_UBSAN", "0"),
2628
# NOTE:
2729
# - omit vcpkg, need to test for presence
2830
# - omit ccache, need to test for presence

0 commit comments

Comments
 (0)