Skip to content

Commit

Permalink
[sanitizer] Add fgets, fputs and puts into sanitizer_common
Browse files Browse the repository at this point in the history
Summary:
Add fgets, fputs and puts to sanitizer_common. This adds ASAN coverage
for these functions, extends MSAN support from fgets to fputs/puts and
extends TSAN support from puts to fputs.

Fixes: google/sanitizers#952

Reviewed By: vitalybuka

Differential Revision: https://reviews.llvm.org/D46545

llvm-svn: 334450
  • Loading branch information
Lekensteyn committed Jun 11, 2018
1 parent 61db138 commit 1c05c95
Show file tree
Hide file tree
Showing 10 changed files with 210 additions and 29 deletions.
10 changes: 0 additions & 10 deletions compiler-rt/lib/esan/esan_interceptors.cpp
Expand Up @@ -316,13 +316,6 @@ INTERCEPTOR(int, unlink, char *path) {
return REAL(unlink)(path);
}

INTERCEPTOR(int, puts, const char *s) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, internal_strlen(s));
return REAL(puts)(s);
}

INTERCEPTOR(int, rmdir, char *path) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, rmdir, path);
Expand Down Expand Up @@ -493,9 +486,6 @@ void initializeInterceptors() {
INTERCEPT_FUNCTION(creat);
ESAN_MAYBE_INTERCEPT_CREAT64;
INTERCEPT_FUNCTION(unlink);
INTERCEPT_FUNCTION(fread);
INTERCEPT_FUNCTION(fwrite);
INTERCEPT_FUNCTION(puts);
INTERCEPT_FUNCTION(rmdir);

ESAN_MAYBE_INTERCEPT_SIGNAL;
Expand Down
10 changes: 0 additions & 10 deletions compiler-rt/lib/msan/msan_interceptors.cc
Expand Up @@ -748,15 +748,6 @@ INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int sv[2]) {
return res;
}

INTERCEPTOR(char *, fgets, char *s, int size, void *stream) {
ENSURE_MSAN_INITED();
InterceptorScope interceptor_scope;
char *res = REAL(fgets)(s, size, stream);
if (res)
__msan_unpoison(s, REAL(strlen)(s) + 1);
return res;
}

#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) {
ENSURE_MSAN_INITED();
Expand Down Expand Up @@ -1609,7 +1600,6 @@ void InitializeInterceptors() {
INTERCEPT_FUNCTION(pipe);
INTERCEPT_FUNCTION(pipe2);
INTERCEPT_FUNCTION(socketpair);
INTERCEPT_FUNCTION(fgets);
MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED;
INTERCEPT_FUNCTION(getrlimit);
MSAN_MAYBE_INTERCEPT_GETRLIMIT64;
Expand Down
47 changes: 47 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
Expand Up @@ -1189,6 +1189,50 @@ INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt,
#define INIT_PWRITEV64
#endif

#if SANITIZER_INTERCEPT_FGETS
INTERCEPTOR(char *, fgets, char *s, SIZE_T size, void *file) {
// libc file streams can call user-supplied functions, see fopencookie.
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fgets, s, size, file);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
char *res = REAL(fgets)(s, size, file);
if (res)
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1);
return res;
}
#define INIT_FGETS COMMON_INTERCEPT_FUNCTION(fgets)
#else
#define INIT_FGETS
#endif

#if SANITIZER_INTERCEPT_FPUTS
INTERCEPTOR(int, fputs, char *s, void *file) {
// libc file streams can call user-supplied functions, see fopencookie.
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fputs, s, file);
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
return REAL(fputs)(s, file);
}
#define INIT_FPUTS COMMON_INTERCEPT_FUNCTION(fputs)
#else
#define INIT_FPUTS
#endif

#if SANITIZER_INTERCEPT_PUTS
INTERCEPTOR(int, puts, char *s) {
// libc file streams can call user-supplied functions, see fopencookie.
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
return REAL(puts)(s);
}
#define INIT_PUTS COMMON_INTERCEPT_FUNCTION(puts)
#else
#define INIT_PUTS
#endif

#if SANITIZER_INTERCEPT_PRCTL
INTERCEPTOR(int, prctl, int option, unsigned long arg2,
unsigned long arg3, // NOLINT
Expand Down Expand Up @@ -7236,6 +7280,9 @@ static void InitializeCommonInterceptors() {
INIT_WRITEV;
INIT_PWRITEV;
INIT_PWRITEV64;
INIT_FGETS;
INIT_FPUTS;
INIT_PUTS;
INIT_PRCTL;
INIT_LOCALTIME_AND_FRIENDS;
INIT_STRPTIME;
Expand Down
Expand Up @@ -164,6 +164,9 @@

#define SANITIZER_INTERCEPT_FREAD SI_POSIX
#define SANITIZER_INTERCEPT_FWRITE SI_POSIX
#define SANITIZER_INTERCEPT_FGETS SI_POSIX
#define SANITIZER_INTERCEPT_FPUTS SI_POSIX
#define SANITIZER_INTERCEPT_PUTS SI_POSIX

#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32
Expand Down
9 changes: 0 additions & 9 deletions compiler-rt/lib/tsan/rtl/tsan_interceptors.cc
Expand Up @@ -1736,12 +1736,6 @@ TSAN_INTERCEPTOR(void, abort, int fake) {
REAL(abort)(fake);
}

TSAN_INTERCEPTOR(int, puts, const char *s) {
SCOPED_TSAN_INTERCEPTOR(puts, s);
MemoryAccessRange(thr, pc, (uptr)s, internal_strlen(s), false);
return REAL(puts)(s);
}

TSAN_INTERCEPTOR(int, rmdir, char *path) {
SCOPED_TSAN_INTERCEPTOR(rmdir, path);
Release(thr, pc, Dir2addr(path));
Expand Down Expand Up @@ -2706,10 +2700,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(unlink);
TSAN_INTERCEPT(tmpfile);
TSAN_MAYBE_INTERCEPT_TMPFILE64;
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
TSAN_INTERCEPT(abort);
TSAN_INTERCEPT(puts);
TSAN_INTERCEPT(rmdir);
TSAN_INTERCEPT(closedir);

Expand Down
46 changes: 46 additions & 0 deletions compiler-rt/test/asan/TestCases/Posix/fgets_fputs.cc
@@ -0,0 +1,46 @@
// RUN: %clangxx_asan -g %s -o %t
// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FGETS
// RUN: not %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-FPUTS
// RUN: not %run %t 3 3 2>&1 | FileCheck %s --check-prefix=CHECK-PUTS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int test_fgets() {
FILE *fp = fopen("/etc/passwd", "r");
char buf[2];
fgets(buf, sizeof(buf) + 1, fp); // BOOM
fclose(fp);
return 0;
}

int test_fputs() {
FILE *fp = fopen("/dev/null", "w");
char buf[1] = {'x'}; // Note: not nul-terminated
fputs(buf, fp); // BOOM
return fclose(fp);
}

void test_puts() {
char *p = strdup("x");
free(p);
puts(p); // BOOM
}

int main(int argc, char *argv[]) {
if (argc == 1)
test_fgets();
else if (argc == 2)
test_fputs();
else
test_puts();
return 0;
}

// CHECK-FGETS: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}}
// CHECK-FGETS: #{{.*}} in {{(wrap_|__interceptor_)?}}fgets
// CHECK-FPUTS: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}}
// CHECK-FPUTS: #{{.*}} in {{(wrap_|__interceptor_)?}}fputs
// CHECK-PUTS: {{.*ERROR: AddressSanitizer: heap-use-after-free}}
// CHECK-PUTS: #{{.*}} in {{(wrap_|__interceptor_)?}}puts
47 changes: 47 additions & 0 deletions compiler-rt/test/msan/fgets_fputs.cc
@@ -0,0 +1,47 @@
// RUN: %clangxx_msan -g %s -o %t
// RUN: %run %t
// RUN: not %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-FPUTS
// RUN: not %run %t 3 3 2>&1 | FileCheck %s --check-prefix=CHECK-PUTS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int test_fgets() {
FILE *fp = fopen("/dev/zero", "r");
char c;

if (!fgets(&c, 1, fp))
return 1;

if (c == '1') // No error
return 2;

fclose(fp);
return 0;
}

int test_fputs() {
FILE *fp = fopen("/dev/null", "w");
char buf[2];
fputs(buf, fp); // BOOM
return fclose(fp);
}

void test_puts() {
char buf[2];
puts(buf); // BOOM
}

int main(int argc, char *argv[]) {
if (argc == 1)
test_fgets();
else if (argc == 2)
test_fputs();
else
test_puts();
return 0;
}

// CHECK-FPUTS: Uninitialized bytes in __interceptor_fputs at offset 0 inside
// CHECK-PUTS: Uninitialized bytes in __interceptor_puts at offset 0 inside
20 changes: 20 additions & 0 deletions compiler-rt/test/sanitizer_common/TestCases/Posix/fgets.cc
@@ -0,0 +1,20 @@
// RUN: %clangxx -g %s -o %t && %run %t

#include <stdio.h>

int main(void) {
FILE *fp;
char buf[2];
char *s;

fp = fopen("/etc/passwd", "r");
if (!fp)
return 1;

s = fgets(buf, sizeof(buf), fp);
if (!s)
return 2;

fclose(fp);
return 0;
}
18 changes: 18 additions & 0 deletions compiler-rt/test/sanitizer_common/TestCases/Posix/fputs_puts.cc
@@ -0,0 +1,18 @@
// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
// CHECK: {{^foobar$}}

#include <stdio.h>

int main(void) {
int r;

r = fputs("foo", stdout);
if (r < 0)
return 1;

r = puts("bar");
if (r < 0)
return 1;

return 0;
}
29 changes: 29 additions & 0 deletions compiler-rt/test/tsan/race_on_fputs.cc
@@ -0,0 +1,29 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
#include "test.h"

char s[] = "abracadabra";

void *Thread0(void *p) {
fputs(s, stdout);
barrier_wait(&barrier);
return 0;
}

void *Thread1(void *p) {
barrier_wait(&barrier);
s[3] = 'z';
return 0;
}

int main() {
barrier_init(&barrier, 2);
pthread_t th[2];
pthread_create(&th[0], 0, Thread0, 0);
pthread_create(&th[1], 0, Thread1, 0);
pthread_join(th[0], 0);
pthread_join(th[1], 0);
fprintf(stderr, "DONE");
}

// CHECK: WARNING: ThreadSanitizer: data race
// CHECK: DONE

0 comments on commit 1c05c95

Please sign in to comment.