blob: 7e28f6d46c724933f4a3d4505bdcd993efb7cc94 [file] [log] [blame]
// 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/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> mem(this, kAllocSize);
ASSERT_TRUE(mem.get() != NULL);
SetCallBackFunction(&AsanErrorCallback);
EXPECT_EQ(mem.get(), memsetFunction(mem.GetAs<void*>(), 0xAA, kAllocSize));
for (size_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> 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 size_t kAllocSize = 13;
ScopedASanAlloc<uint8> mem_src(this, kAllocSize);
ASSERT_TRUE(mem_src.get() != NULL);
// Fill the array with value going from 0 to kAllocSize;
for (size_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 (size_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 size_t kAllocSize = 13;
ScopedASanAlloc<uint8> mem_src(this, kAllocSize);
ASSERT_TRUE(mem_src.get() != NULL);
ScopedASanAlloc<uint8> mem_dst(this, kAllocSize);
ASSERT_TRUE(mem_dst.get() != NULL);
// Fill the array with value going from 0 to kAllocSize;
for (size_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 (size_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, 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, 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> 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