text: // Copyright 2014 Google Inc. All Rights Reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "syzygy/agent/asan/crt_interceptors.h"
  
  #include <windows.h>
  
  #include "base/bind.h"
  #include "gtest/gtest.h"
  #include "syzygy/agent/asan/unittest_util.h"
  
  namespace agent {
  namespace asan {
  
  namespace {
  
  using testing::ScopedAsanAlloc;
  
  typedef testing::TestAsanRtl CrtInterceptorsTest;
  
  // An arbitrary size for the buffer we allocate in the different unittests.
  const size_t kAllocSize = 13;
  
  void AsanErrorCallback(AsanErrorInfo* error_info) {
    // Our tests should clean up after themselves and not leave any blocks
    // corrupt.
    ASSERT_NE(CORRUPT_BLOCK, error_info->error_type);
  
    // Raise an exception to prevent the intercepted function from corrupting
    // the block. If this error is not handled then this will cause the unittest
    // to fail.
    ::RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, 0);
  }
  
  }  // namespace
  
  TEST_F(CrtInterceptorsTest, AsanCheckMemset) {
    const size_t kAllocSize = 13;
    ScopedAsanAlloc<uint8_t> mem(this, kAllocSize);
    ASSERT_TRUE(mem.get() != NULL);
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(mem.get(), memsetFunction(mem.GetAs<void*>(), 0xAA, kAllocSize));
    for (uint32_t i = 0; i < kAllocSize; ++i)
      EXPECT_EQ(0xAA, mem[i]);
  
    memsetFunctionFailing(mem.get() - 1, 0xBB, kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    memsetFunctionFailing(mem.get(), 0xCC, kAllocSize + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckMemchr) {
    const size_t kAllocSize = 13;
    ScopedAsanAlloc<uint8_t> mem(this, kAllocSize);
    ASSERT_TRUE(mem.get() != NULL);
    ::memset(mem.get(), 0, kAllocSize);
    mem[4] = 0xAA;
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(mem.get() + 4, memchrFunction(mem.get(), mem[4], kAllocSize));
    EXPECT_EQ(NULL, memchrFunction(mem.get(), mem[4] + 1, kAllocSize));
  
    memchrFunctionFailing(mem.get() - 1, mem[4], kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    memchrFunctionFailing(mem.get() + 1, mem[4], kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckMemmove) {
    const uint8_t kAllocSize = 13;
    ScopedAsanAlloc<uint8_t> mem_src(this, kAllocSize);
    ASSERT_TRUE(mem_src.get() != NULL);
    // Fill the array with value going from 0 to kAllocSize;
    for (uint8_t i = 0; i < kAllocSize; ++i)
      mem_src[i] = i;
  
    SetCallBackFunction(&AsanErrorCallback);
    // Shift all the value from one index to the right.
    EXPECT_EQ(mem_src.get() + 1,
              memmoveFunction(mem_src.get() + 1, mem_src.get(), kAllocSize - 1));
    EXPECT_EQ(0, mem_src[0]);
    for (uint8_t i = 1; i < kAllocSize; ++i)
      EXPECT_EQ(i - 1, mem_src[i]);
  
    // Re-shift them to the left.
    memmoveFunctionFailing(mem_src.get(), mem_src.get() + 1, kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  
    memmoveFunctionFailing(mem_src.get() - 1, mem_src.get(), kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckMemcpy) {
    const uint8_t kAllocSize = 13;
    ScopedAsanAlloc<uint8_t> mem_src(this, kAllocSize);
    ASSERT_TRUE(mem_src.get() != NULL);
    ScopedAsanAlloc<uint8_t> mem_dst(this, kAllocSize);
    ASSERT_TRUE(mem_dst.get() != NULL);
    // Fill the array with value going from 0 to kAllocSize;
    for (uint8_t i = 0; i < kAllocSize; ++i) {
      mem_src[i] = i;
      mem_dst[i] = ~i;
    }
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(mem_dst.get(),
              memcpyFunction(mem_dst.get(), mem_src.get(), kAllocSize));
    for (uint32_t i = 0; i < kAllocSize; ++i)
      EXPECT_EQ(mem_dst[i], mem_src[i]);
  
    memcpyFunctionFailing(mem_dst.get(), mem_src.get(), kAllocSize + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  
    memcpyFunctionFailing(mem_dst.get(), mem_src.get() - 1, kAllocSize);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanCheckStrcspn) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* str_value = "abc1";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str.get() != NULL);
  
    const char* str_value_2 = "abc";
    ScopedAsanAlloc<char> str2(this, ::strlen(str_value_2) + 1, str_value_2);
    ASSERT_TRUE(str2.get() != NULL);
  
    // This should contain at least one value present in |str| but none present
    // in |str2|.
    const char* keys_value = "12";
    EXPECT_NE(::strlen(str_value), ::strcspn(str_value, keys_value));
    EXPECT_EQ(::strlen(str_value_2), ::strcspn(str_value_2, keys_value));
    ScopedAsanAlloc<char> keys(this, ::strlen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strcspn(str.get(), keys.get()),
              strcspnFunction(str.get(), keys.get()));
  
    strcspnFunctionFailing(str.get() - 1, keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    // The key set should be null terminated, otherwise an overflow should be
    // detected.
    size_t keys_len = ::strlen(keys.get());
    keys[keys_len] = 'a';
    strcspnFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[keys_len] = 0;
    ResetLog();
  
    // The implementation allows a non null terminated input string if it contains
    // at least one of the keys, otherwise it'll overflow.
  
    size_t str_len = ::strlen(str.get());
    str[str_len] = 'a';
    EXPECT_EQ(::strcspn(str.get(), keys.get()),
              strcspnFunction(str.get(), keys.get()));
    str[str_len] = 0;
    ResetLog();
  
    size_t str2_len = ::strlen(str2.get());
    str2[str2_len] = 'a';
    strcspnFunctionFailing(str2.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    str2[str2_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanStrcspnImpl) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    EXPECT_EQ(5, asan_strcspn("abcde", "fgh"));
    EXPECT_EQ(5, asan_strcspn("abcde", ""));
    EXPECT_EQ(0, asan_strcspn("abcde", "abcde"));
    EXPECT_EQ(0U, asan_strcspn("abcde", "edcba"));
    EXPECT_EQ(3, asan_strcspn("abcde", "ed"));
    EXPECT_EQ(2, asan_strcspn("abcde", "c"));
    EXPECT_EQ(0U, asan_strcspn("", ""));
    EXPECT_EQ(0U, asan_strcspn("", "abcde"));
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckStrlen) {
    const char* str_value = "test_strlen";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strlen(str.get()), strlenFunction(str.get()));
  
    strlenFunctionFailing(str.get() - 1);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t str_len = ::strlen(str.get());
    str[str_len] = 'a';
    strlenFunctionFailing(str.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckStrnlen) {
    const char str_value[] = "test_strnlen";
    const size_t str_len = arraysize(str_value);
    ScopedAsanAlloc<char> str(this, str_len, str_value);
    ASSERT_TRUE(str != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strnlen(str.get(), str_len), strnlenFunction(str.get(), str_len));
  
    strnlenFunctionFailing(str.get() - 1, str_len);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t n = ::strlen(str.get());
    str[n] = 'a';
    strnlenFunctionFailing(str.get(), str_len + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckStrrchr) {
    const char* str_value = "test_strrchr";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strrchr(str.get(), 'c'), strrchrFunction(str.get(), 'c'));
    EXPECT_EQ(::strrchr(str.get(), 'z'), strrchrFunction(str.get(), 'z'));
  
    strrchrFunctionFailing(str.get() - 1, 'c');
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t str_len = ::strlen(str.get());
    str[str_len] = 'a';
    strrchrFunctionFailing(str.get(), 'c');
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckWcsnlen) {
    const wchar_t str_value[] = L"test_wcsnlen";
    const size_t str_len = arraysize(str_value);
    ScopedAsanAlloc<wchar_t> str(this, str_len, str_value);
    ASSERT_TRUE(str != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::wcsnlen(str.get(), str_len),
              wcsnlenFunction(str.get(), str_len));
  
    wcsnlenFunctionFailing(str.get() - 1, str_len);
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t n = ::wcslen(str.get());
    str[n] = 'a';
    wcsnlenFunctionFailing(str.get(), str_len + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckWcsrchr) {
    const wchar_t* wstr_value = L"test_wcsrchr";
    ScopedAsanAlloc<wchar_t> wstr(this, ::wcslen(wstr_value) + 1);
    ASSERT_TRUE(wstr != NULL);
    wcscpy(wstr.get(), wstr_value);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::wcsrchr(wstr.get(), L'c'), wcsrchrFunction(wstr.get(), L'c'));
    EXPECT_EQ(::wcsrchr(wstr.get(), 'z'), wcsrchrFunction(wstr.get(), 'z'));
  
    wcsrchrFunctionFailing(wstr.get() - 1, L'c');
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t str_len = ::wcslen(wstr_value);
    wstr[str_len] = L'a';
    wcsrchrFunctionFailing(wstr.get(), L'c');
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckWcschr) {
    const wchar_t* wstr_value = L"test_wcschr";
    ScopedAsanAlloc<wchar_t> wstr(this, ::wcslen(wstr_value) + 1);
    ASSERT_TRUE(wstr != NULL);
    wcscpy(wstr.get(), wstr_value);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::wcschr(wstr.get(), L'c'), wcschrFunction(wstr.get(), L'c'));
    EXPECT_EQ(::wcschr(wstr.get(), 'z'), wcschrFunction(wstr.get(), 'z'));
  
    wcschrFunctionFailing(wstr.get() - 1, L'c');
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t str_len = ::wcslen(wstr_value);
    wstr[str_len] = L'a';
    wcschrFunctionFailing(wstr.get(), L'z');
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanCheckStrcmp) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* str_value = "test_strcmp";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str.get() != NULL);
  
    const char* keys_value = "strcmp";
    ScopedAsanAlloc<char> keys(this, ::strlen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strcmp(str.get(), keys.get()),
              strcmpFunction(str.get(), keys.get()));
  
    strcmpFunctionFailing(str.get() - 1, keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t keys_len = ::strlen(keys.get());
    keys[keys_len] = 'a';
    strcmpFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[keys_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanCheckStrpbrk) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* str_value = "abc1";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str.get() != NULL);
  
    const char* str_value_2 = "abc";
    ScopedAsanAlloc<char> str2(this, ::strlen(str_value_2) + 1, str_value_2);
    ASSERT_TRUE(str2.get() != NULL);
  
    // This should contain at least one value present in |str| but none present
    // in |str2|.
    const char* keys_value = "12";
    EXPECT_NE(::strlen(str_value), ::strcspn(str_value, keys_value));
    EXPECT_EQ(::strlen(str_value_2), ::strcspn(str_value_2, keys_value));
    ScopedAsanAlloc<char> keys(this, ::strlen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strpbrk(str.get(), keys.get()),
              strpbrkFunction(str.get(), keys.get()));
  
    strpbrkFunctionFailing(str.get() - 1, keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t keys_len = ::strlen(keys.get());
    keys[keys_len] = 'a';
    strpbrkFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[keys_len] = 0;
    ResetLog();
  
    // The implementation allows a non null terminated input string if it contains
    // at least one of the keys, otherwise it'll overflow.
  
    size_t str_len = ::strlen(str.get());
    str[str_len] = 'a';
    EXPECT_EQ(::strpbrk(str.get(), keys.get()),
              strpbrkFunction(str.get(), keys.get()));
    str[str_len] = 0;
    ResetLog();
  
    size_t str2_len = ::strlen(str2.get());
    str2[str2_len] = 'a';
    strpbrkFunctionFailing(str2.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    str2[str2_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanStrpbrkImpl) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* abcde = "abcde";
    const char* empty_string = "";
    EXPECT_EQ(strpbrk(abcde, abcde), asan_strpbrk(abcde, abcde));
    EXPECT_EQ(strpbrk(abcde, "fgh"), asan_strpbrk(abcde, "fgh"));
    EXPECT_EQ(strpbrk(abcde, ""), asan_strpbrk(abcde, ""));
    EXPECT_EQ(strpbrk(abcde, "edcba"), asan_strpbrk(abcde, "edcba"));
    EXPECT_EQ(strpbrk(abcde, "ed"), asan_strpbrk(abcde, "ed"));
    EXPECT_EQ(strpbrk(abcde, "c"), asan_strpbrk(abcde, "c"));
    EXPECT_EQ(strpbrk(empty_string, ""), asan_strpbrk(empty_string, ""));
    EXPECT_EQ(strpbrk(empty_string, abcde), asan_strpbrk(empty_string, abcde));
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanCheckStrstr) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* str_value = "test_strstr";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str != NULL);
  
    const char* keys_value = "strstr";
    ScopedAsanAlloc<char> keys(this, ::strlen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strstr(str.get(), keys.get()),
              strstrFunction(str.get(), keys.get()));
  
    strstrFunctionFailing(str.get() - 1, keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t keys_len = ::strlen(keys.get());
    keys[keys_len] = 'a';
    strstrFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[keys_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckWcsstr) {
    const wchar_t* str_value = L"test_wcsstr";
    ScopedAsanAlloc<wchar_t> str(this, ::wcslen(str_value) + 1, str_value);
    ASSERT_TRUE(str != NULL);
  
    const wchar_t* keys_value = L"wcsstr";
    ScopedAsanAlloc<wchar_t> keys(this, ::wcslen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    EXPECT_EQ(::wcsstr(str.get(), keys.get()),
              wcsstrFunction(str.get(), keys.get()));
  
    EXPECT_EQ(NULL, wcsstrFunction(str.get(), L"abcd"));
  
    SetCallBackFunction(&AsanErrorCallback);
    keys[::wcslen(keys_value)] = L'a';
    wcsstrFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[::wcslen(keys_value)] = 0;
  
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanCheckStrspn) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    const char* str_value = "123_abc";
    ScopedAsanAlloc<char> str(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(str.get() != NULL);
  
    const char* keys_value = "123";
    EXPECT_EQ(::strlen(keys_value), ::strspn(str_value, keys_value));
    ScopedAsanAlloc<char> keys(this, ::strlen(keys_value) + 1, keys_value);
    ASSERT_TRUE(keys.get() != NULL);
  
    // The second test string should only contains values present in the keys.
    const char* str_value_2 = "12321";
    ScopedAsanAlloc<char> str2(this, ::strlen(str_value_2) + 1, str_value_2);
    EXPECT_EQ(::strlen(str_value_2), ::strspn(str_value_2, keys_value));
    ASSERT_TRUE(str2.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(::strspn(str.get(), keys.get()),
              strspnFunction(str.get(), keys.get()));
  
    strspnFunctionFailing(str.get() - 1, keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    size_t keys_len = ::strlen(keys.get());
    keys[keys_len] = 'a';
    strspnFunctionFailing(str.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    keys[keys_len] = 0;
    ResetLog();
  
    // The implementation allows a non null terminated input string if it doesn't
    // start with a value contained in the keys, otherwise it'll overflow.
  
    size_t str_len = ::strlen(str.get());
    str[str_len] = 'a';
    EXPECT_EQ(::strspn(str.get(), keys.get()),
              strspnFunction(str.get(), keys.get()));
    str[str_len] = 0;
    ResetLog();
  
    size_t str2_len = ::strlen(str2.get());
    str2[str2_len] = keys[0];
    strspnFunctionFailing(str2.get(), keys.get());
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    str2[str2_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, DISABLED_AsanStrspnImpl) {
    // TODO(sebmarchand): Reactivate this unittest once the implementation of
    //     this interceptor has been fixed.
    EXPECT_EQ(5, asan_strspn("abcde", "abcde"));
    EXPECT_EQ(5, asan_strspn("abcde", "edcba"));
    EXPECT_EQ(0U, asan_strspn("abcde", ""));
    EXPECT_EQ(2, asan_strspn("abcde", "ab"));
    EXPECT_EQ(4, asan_strspn("abccde", "abc"));
    EXPECT_EQ(2, asan_strspn("abcde", "fghab"));
    EXPECT_EQ(3, asan_strspn("abcde", "fagbhc"));
    EXPECT_EQ(1, asan_strspn("abcde", "aaa"));
    EXPECT_EQ(0U, asan_strspn("abcde", "fgh"));
    EXPECT_EQ(0U, asan_strspn("", ""));
    EXPECT_EQ(0U, asan_strspn("", "abcde"));
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckStrncpy) {
    const char* str_value = "test_strncpy";
    ScopedAsanAlloc<char> source(this, ::strlen(str_value) + 1, str_value);
    ASSERT_TRUE(source != NULL);
  
    const char* long_str_value = "test_strncpy_long_source";
    ScopedAsanAlloc<char> long_source(this, ::strlen(long_str_value) + 1,
        long_str_value);
    ASSERT_TRUE(long_source.get() != NULL);
  
    ScopedAsanAlloc<char> destination(this, ::strlen(str_value) + 1);
    ASSERT_TRUE(destination != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(destination.get(),
              strncpyFunction(destination.get(),
                              source.get(),
                              ::strlen(str_value)));
  
    // Test an underflow on the source.
    strncpyFunctionFailing(destination.get(),
                           source.get() - 1,
                           ::strlen(str_value));
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    // Test an underflow on the destination.
    strncpyFunctionFailing(destination.get() - 1,
                           source.get(),
                           ::strlen(str_value));
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    // Test an overflow on the destination.
    std::vector<uint8_t> original_data(::strlen(long_str_value));
    memcpy(&original_data[0], destination.get(), ::strlen(long_str_value));
    strncpyFunctionFailing(destination.get(),
                           long_source.get(),
                           ::strlen(long_str_value));
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  
    // Another overflow on the destination.
    strncpyFunctionFailing(destination.get(),
                           source.get(),
                           ::strlen(str_value) + 2);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    ResetLog();
  
    // Test an overflow on the source.
    size_t source_len = ::strlen(source.get());
    source[source_len] = 'a';
    strncpyFunctionFailing(destination.get(),
                           source.get(),
                           ::strlen(source.get()) + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    source[source_len] = 0;
    ResetLog();
  }
  
  TEST_F(CrtInterceptorsTest, AsanCheckStrncat) {
    const char* prefix_value = "test_";
    const char* suffix_value = "strncat";
    char buffer[64];
  
    ScopedAsanAlloc<char> mem(this,
        ::strlen(prefix_value) + ::strlen(suffix_value) + 1, prefix_value);
    ASSERT_TRUE(mem != NULL);
    ::strcpy(buffer, prefix_value);
  
    ScopedAsanAlloc<char> suffix(this, ::strlen(suffix_value) + 1, suffix_value);
    ASSERT_TRUE(mem.get() != NULL);
  
    SetCallBackFunction(&AsanErrorCallback);
    EXPECT_EQ(mem.get(),
        strncatFunction(mem.get(), suffix.get(), ::strlen(suffix_value)));
    EXPECT_STRCASEEQ(
        ::strncat(buffer, suffix.get(), ::strlen(suffix_value)), mem.get());
  
    // Test an underflow on the suffix.
    ::strcpy(mem.get(), prefix_value);
    ::strcpy(buffer, prefix_value);
    strncatFunctionFailing(mem.get(), suffix.get() - 1, ::strlen(suffix_value));
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    // Test an underflow on the destination.
    ::strcpy(mem.get(), prefix_value);
    ::strcpy(buffer, prefix_value);
    strncatFunctionFailing(mem.get() - 1, suffix.get(), ::strlen(suffix_value));
    EXPECT_TRUE(LogContains(kHeapBufferUnderFlow));
    ResetLog();
  
    // Test an overflow on the suffix.
    size_t suffix_len = ::strlen(suffix.get());
    suffix[suffix_len] = 'a';
    ::strcpy(mem.get(), prefix_value);
    ::strcpy(buffer, prefix_value);
    strncatFunctionFailing(mem.get(), suffix.get(), ::strlen(suffix.get()) + 1);
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    suffix[suffix_len] = 0;
    ResetLog();
  
    // Test an overflow on the destination.
    ::strcpy(mem.get(), prefix_value);
    ::strcpy(buffer, prefix_value);
    size_t prefix_len = ::strlen(prefix_value);
    mem[prefix_len] = 'a';
    buffer[prefix_len] = 'a';
    strncatFunctionFailing(mem.get(), suffix.get(), ::strlen(suffix.get()));
    EXPECT_TRUE(LogContains(kHeapBufferOverFlow));
    mem[prefix_len] = 0;
    buffer[prefix_len] = 0;
    ResetLog();
  }
  
  }  // namespace asan
  }  // namespace agent
  