Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ASan-reported heap-buffer-overflow in GIUH tests #539

Closed
PhilMiller opened this issue Jun 21, 2023 · 3 comments · Fixed by #553
Closed

Fix ASan-reported heap-buffer-overflow in GIUH tests #539

PhilMiller opened this issue Jun 21, 2023 · 3 comments · Fixed by #553
Assignees

Comments

@PhilMiller
Copy link
Contributor

Identified in #536

$ ./test/test_giuh --gtest_filter=GIUH_Test.TestInit0

Running main() from /Users/phil/Code/noaa/ngen/test/googletest/googletest/src/gtest_main.cc
Note: Google Test filter = GIUH_Test.TestInit0
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from GIUH_Test
[ RUN      ] GIUH_Test.TestInit0
=================================================================
==72208==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x000109d001f8 at pc 0x00010249fd50 bp 0x00016dac8b10 sp 0x00016dac8b08
READ of size 8 at 0x000109d001f8 thread T0
    #0 0x10249fd4c in giuh::giuh_kernel_impl::interpolate_regularized_cdf() GIUH.cpp:134
    #1 0x102364074 in giuh::giuh_kernel_impl::giuh_kernel_impl(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, unsigned int) GIUH.hpp:102
    #2 0x102340028 in giuh::giuh_kernel_impl::giuh_kernel_impl(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, unsigned int) GIUH.hpp:98
    #3 0x10233c190 in giuh::giuh_kernel_impl::giuh_kernel_impl(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >) GIUH.hpp:124
    #4 0x1024abab0 in giuh::GiuhJsonReader::build_giuh_kernel(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, boost::property_tree::basic_ptree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >) GiuhJsonReader.cpp:16
    #5 0x1024ae4ec in giuh::GiuhJsonReader::get_giuh_kernel_for_id(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) GiuhJsonReader.cpp:78
    #6 0x102338dc4 in GIUH_Test_TestInit0_Test::TestBody() GIUH_Test.cpp:71
    #7 0x10247d810 in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) gtest.cc:2599
    #8 0x1023e0d90 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) gtest.cc:2635
    #9 0x1023e0960 in testing::Test::Run() gtest.cc:2674
    #10 0x1023e3200 in testing::TestInfo::Run() gtest.cc:2853
    #11 0x1023e6194 in testing::TestSuite::Run() gtest.cc:3012
    #12 0x10240a418 in testing::internal::UnitTestImpl::RunAllTests() gtest.cc:5870
    #13 0x10248b224 in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) gtest.cc:2599
    #14 0x1024094d8 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) gtest.cc:2635
    #15 0x102408fe8 in testing::UnitTest::Run() gtest.cc:5444
    #16 0x10249e23c in RUN_ALL_TESTS() gtest.h:2293
    #17 0x10249e198 in main gtest_main.cc:51
    #18 0x1029b1088 in start+0x204 (dyld:arm64e+0x5088)

0x000109d001f8 is located 8 bytes to the left of 22256-byte region [0x000109d00200,0x000109d058f0)
allocated by thread T0 here:
    #0 0x102dddef0 in wrap__Znwm+0x74 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x4def0)
    #1 0x1023490bc in void* std::__1::__libcpp_operator_new<unsigned long>(unsigned long) new:235
    #2 0x102348fe0 in std::__1::__libcpp_allocate(unsigned long, unsigned long) new:261
    #3 0x1023669bc in std::__1::allocator<double>::allocate(unsigned long) allocator.h:108
    #4 0x102366804 in std::__1::allocator_traits<std::__1::allocator<double> >::allocate(std::__1::allocator<double>&, unsigned long) allocator_traits.h:262
    #5 0x102365e48 in std::__1::vector<double, std::__1::allocator<double> >::__vallocate(unsigned long) vector:1015
    #6 0x10236a060 in std::__1::vector<double, std::__1::allocator<double> >::vector(std::__1::vector<double, std::__1::allocator<double> > const&) vector:1280
    #7 0x10233bf98 in std::__1::vector<double, std::__1::allocator<double> >::vector(std::__1::vector<double, std::__1::allocator<double> > const&) vector:1273
    #8 0x1024aba84 in giuh::GiuhJsonReader::build_giuh_kernel(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, boost::property_tree::basic_ptree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >) GiuhJsonReader.cpp:16
    #9 0x1024ae4ec in giuh::GiuhJsonReader::get_giuh_kernel_for_id(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) GiuhJsonReader.cpp:78
    #10 0x102338dc4 in GIUH_Test_TestInit0_Test::TestBody() GIUH_Test.cpp:71
    #11 0x10247d810 in void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) gtest.cc:2599
    #12 0x1023e0d90 in void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) gtest.cc:2635
    #13 0x1023e0960 in testing::Test::Run() gtest.cc:2674
    #14 0x1023e3200 in testing::TestInfo::Run() gtest.cc:2853
    #15 0x1023e6194 in testing::TestSuite::Run() gtest.cc:3012
    #16 0x10240a418 in testing::internal::UnitTestImpl::RunAllTests() gtest.cc:5870
    #17 0x10248b224 in bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) gtest.cc:2599
    #18 0x1024094d8 in bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) gtest.cc:2635
    #19 0x102408fe8 in testing::UnitTest::Run() gtest.cc:5444
    #20 0x10249e23c in RUN_ALL_TESTS() gtest.h:2293
    #21 0x10249e198 in main gtest_main.cc:51
    #22 0x1029b1088 in start+0x204 (dyld:arm64e+0x5088)

SUMMARY: AddressSanitizer: heap-buffer-overflow GIUH.cpp:134 in giuh::giuh_kernel_impl::interpolate_regularized_cdf()
Shadow bytes around the buggy address:
  0x0070213bffe0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0070213bfff0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0070213c0000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0070213c0010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0070213c0020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0070213c0030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]
  0x0070213c0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213c0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213c0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213c0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0070213c0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==72208==ABORTING
Abort trap: 6
@PhilMiller PhilMiller self-assigned this Jun 21, 2023
@PhilMiller
Copy link
Contributor Author

(lldb) f 5
frame #5: 0x0000000100c28ed8 test_all`giuh::giuh_kernel_impl::interpolate_regularized_cdf(this=0x000000016fdfd330) at GIUH.cpp:134:62
   131 	        cdf_times_index_for_iteration--;
   132 	
   133 	        // Then apply equation from spreadsheet
-> 134 	        double result = (time_for_ordinate - this->cdf_times.at(cdf_times_index_for_iteration)) /
   135 	                        (this->cdf_times[cdf_times_index_for_iteration + 1] -
   136 	                         this->cdf_times[cdf_times_index_for_iteration]) *
   137 	                        (this->cdf_cumulative_freqs[cdf_times_index_for_iteration + 1] -

(lldb) p cdf_times_index_for_iteration 
(int) $0 = -1

@robertbartel looks like the decrement leaves us with an index of -1 in some cases

@robertbartel
Copy link
Contributor

Looking quickly, I think we are prevented from coming across that case because of what the values are and how they are used. But I am pretty sure we can just do this to make the sanitizer happy:

diff --git a/src/core/catchment/giuh/GIUH.cpp b/src/core/catchment/giuh/GIUH.cpp
index c86143e09..cab60f7ed 100644
--- a/src/core/catchment/giuh/GIUH.cpp
+++ b/src/core/catchment/giuh/GIUH.cpp
@@ -123,7 +123,7 @@ void giuh_kernel_impl::interpolate_regularized_cdf()
 
         // Find index 'i' of largest CDF time less than the time for the current ordinate
         // Start by getting the index of the first time greater than time_for_ordinate
-        int cdf_times_index_for_iteration = 0;
+        int cdf_times_index_for_iteration = 1;
         while (this->cdf_times[cdf_times_index_for_iteration] < regularized_times_s.back()) {
             cdf_times_index_for_iteration++;
         }

@PhilMiller
Copy link
Contributor Author

To be clear, ASan is a dynamic runtime analysis, not a static analyzer. This isn't a false positive - the case in question actually happened in our tests, and the code actually threw the exception when I changed [] to .at()

PhilMiller added a commit that referenced this issue Jun 27, 2023
PhilMiller added a commit to PhilMiller/NOAA-OWP-ngen that referenced this issue Jun 29, 2023
PhilMiller added a commit to PhilMiller/NOAA-OWP-ngen that referenced this issue Jun 29, 2023
PhilMiller added a commit to PhilMiller/NOAA-OWP-ngen that referenced this issue Jun 29, 2023
PhilMiller added a commit to PhilMiller/NOAA-OWP-ngen that referenced this issue Jun 29, 2023
mattw-nws pushed a commit that referenced this issue Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants