/* Copyright (c) 2008-2010, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file is part of ThreadSanitizer, a dynamic data race detector.
// Author: Konstantin Serebryany.
//
// Some libc functions are implemented in a way unfriendly to race detectors
// and memcheck-like tools.
// E.g. strlen() may read up to 7 bytes past the allocated buffer.
// To avoid false positives in these functions, the tool needs to replace these
// funcions with simpler implementation.
//
// The includer must define these macros:
// REPORT_WRITE_RANGE, REPORT_READ_RANGE, EXTRA_REPLACE_PARAMS,
// EXTRA_REPLACE_ARGS, NOINLINE
// See ts_valgrind_intercepts.c and ts_pin.cc.
#ifndef TS_REPLACE_H_
#define TS_REPLACE_H_
static NOINLINE char *Replace_memchr(EXTRA_REPLACE_PARAMS const char *s,
int c, size_t n) {
size_t i;
char *ret = 0;
for (i = 0; i < n; i++) {
if (s[i] == (char)c) {
ret = (char*)(&s[i]);
break;
}
}
REPORT_READ_RANGE(s, ret ? i + 1 : n);
return ret;
}
static NOINLINE char *Replace_strchr(EXTRA_REPLACE_PARAMS const char *s,
int c) {
size_t i;
char *ret = 0;
for (i = 0; ; i++) {
if (s[i] == (char)c) {
ret = (char*)(&s[i]);
break;
}
if (s[i] == 0) break;
}
REPORT_READ_RANGE(s, ret ? i + 1 : i);
return ret;
}
static NOINLINE char *Replace_strrchr(EXTRA_REPLACE_PARAMS const char *s,
int c) {
char* ret = 0;
size_t i;
for (i = 0; ; i++) {
if (s[i] == (char)c) {
ret = (char*)&s[i];
}
if (s[i] == 0) break;
}
REPORT_READ_RANGE(s, i);
return ret;
}
static NOINLINE size_t Replace_strlen(EXTRA_REPLACE_PARAMS const char *s) {
size_t i = 0;
for (i = 0; s[i]; i++) {
}
REPORT_READ_RANGE(s, i);
return i;
}
static NOINLINE char *Replace_memcpy(EXTRA_REPLACE_PARAMS char *dst,
const char *src, size_t len) {
size_t i;
for (i = 0; i < len; i++) {
dst[i] = src[i];
}
REPORT_READ_RANGE(src, i);
REPORT_WRITE_RANGE(dst, i);
return dst;
}
static NOINLINE char *Replace_memmove(EXTRA_REPLACE_PARAMS char *dst,
const char *src, size_t len) {
size_t i;
if (dst < src) {
for (i = 0; i < len; i++) {
dst[i] = src[i];
}
} else {
for (i = 0; i < len; i++) {
dst[len - i - 1] = src[len - i - 1];
}
}
REPORT_READ_RANGE(src, i);
REPORT_WRITE_RANGE(dst, i);
return dst;
}
static NOINLINE int Replace_memcmp(EXTRA_REPLACE_PARAMS const unsigned char *s1,
const unsigned char *s2, size_t len) {
size_t i;
int res = 0;
for (i = 0; i < len; i++) {
if (s1[i] != s2[i]) {
res = (int)s1[i] - (int)s2[i];
i++;
break;
}
}
REPORT_READ_RANGE(s1, i);
REPORT_READ_RANGE(s2, i);
return res;
}
static NOINLINE char *Replace_strcpy(EXTRA_REPLACE_PARAMS char *dst,
const char *src) {
size_t i;
for (i = 0; src[i]; i++) {
dst[i] = src[i];
}
dst[i] = 0;
REPORT_READ_RANGE(src, i + 1);
REPORT_WRITE_RANGE(dst, i + 1);
return dst;
}
static NOINLINE char *Replace_stpcpy(EXTRA_REPLACE_PARAMS char *dst,
const char *src) {
size_t i;
for (i = 0; src[i]; i++) {
dst[i] = src[i];
}
dst[i] = 0;
REPORT_READ_RANGE(src, i + 1);
REPORT_WRITE_RANGE(dst, i + 1);
return dst + i;
}
static NOINLINE char *Replace_strncpy(EXTRA_REPLACE_PARAMS char *dst,
const char *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
dst[i] = src[i];
if (src[i] == 0) break;
}
REPORT_READ_RANGE(src, i < n ? i+1 : n);
REPORT_WRITE_RANGE(dst, i < n ? i+1 : n);
return dst;
}
static NOINLINE int Replace_strcmp(EXTRA_REPLACE_PARAMS const char *s1,
const char *s2) {
unsigned char c1;
unsigned char c2;
size_t i;
for (i = 0; ; i++) {
c1 = (unsigned char)s1[i];
c2 = (unsigned char)s2[i];
if (c1 != c2) break;
if (c1 == 0) break;
}
REPORT_READ_RANGE(s1, i+1);
REPORT_READ_RANGE(s2, i+1);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
return 0;
}
static NOINLINE int Replace_strncmp(EXTRA_REPLACE_PARAMS const char *s1,
const char *s2, size_t n) {
unsigned char c1 = 0;
unsigned char c2 = 0;
size_t i;
for (i = 0; i < n; i++) {
c1 = (unsigned char)s1[i];
c2 = (unsigned char)s2[i];
if (c1 != c2) break;
if (c1 == 0) break;
}
REPORT_READ_RANGE(s1, i < n ? i+1 : n);
REPORT_READ_RANGE(s2, i < n ? i+1 : n);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
return 0;
}
static NOINLINE char *Replace_strcat(EXTRA_REPLACE_PARAMS char *dest,
const char *src) {
size_t dest_len = Replace_strlen(EXTRA_REPLACE_ARGS dest);
Replace_strcpy(EXTRA_REPLACE_ARGS dest + dest_len, src);
return dest;
}
#if defined(TS_VALGRIND)
// Read every byte in the memory range.
static NOINLINE void ReadMemory(const void* p, size_t size) {
const volatile char* start = (const volatile char*)p;
const volatile char* end = start + size;
volatile char tmp = 0;
for (; start < end; ++start) {
// If we just read the bytes, Valgrind will optimize it out.
tmp ^= *start;
}
}
// Read every byte in the null-terminated string.
static NOINLINE void ReadString(const char* s) {
const volatile char* p = (const volatile char*)s;
volatile char tmp = 0;
char c;
for (; (c = *p); ++p) {
tmp ^= c;
}
}
#endif // TS_VALGRIND
#endif // TS_REPLACE_H_