head	1.4;
access;
symbols
	netbsd-11-0-RC4:1.3
	netbsd-11-0-RC3:1.3
	netbsd-11-0-RC2:1.3
	netbsd-11-0-RC1:1.3
	gcc-14-3-0:1.1.1.3
	perseant-exfatfs-base-20250801:1.3
	netbsd-11:1.3.0.4
	netbsd-11-base:1.3
	gcc-12-5-0:1.1.1.2
	perseant-exfatfs-base-20240630:1.3
	gcc-12-4-0:1.1.1.2
	perseant-exfatfs:1.3.0.2
	perseant-exfatfs-base:1.3
	gcc-12-3-0:1.1.1.2
	gcc-10-5-0:1.1.1.1
	gcc-10-4-0:1.1.1.1
	cjep_sun2x:1.2.0.4
	cjep_sun2x-base:1.2
	cjep_staticlib_x-base1:1.2
	cjep_staticlib_x:1.2.0.2
	cjep_staticlib_x-base:1.2
	gcc-10-3-0:1.1.1.1
	FSF:1.1.1;
locks; strict;
comment	@// @;


1.4
date	2025.09.14.00.08.58;	author mrg;	state Exp;
branches;
next	1.3;
commitid	x9D5QEnvbeMI4CaG;

1.3
date	2023.07.31.01.44.57;	author mrg;	state Exp;
branches;
next	1.2;
commitid	q79F5Opf0FLsyTyE;

1.2
date	2021.04.11.23.54.28;	author mrg;	state dead;
branches;
next	1.1;
commitid	wJn7ggfUTEMOWVOC;

1.1
date	2021.04.10.22.09.22;	author mrg;	state Exp;
branches
	1.1.1.1;
next	;
commitid	eC4g0MRpqTvEkNOC;

1.1.1.1
date	2021.04.10.22.09.22;	author mrg;	state Exp;
branches;
next	1.1.1.2;
commitid	eC4g0MRpqTvEkNOC;

1.1.1.2
date	2023.07.30.05.20.41;	author mrg;	state Exp;
branches;
next	1.1.1.3;
commitid	tk6nV4mbc9nVEMyE;

1.1.1.3
date	2025.09.13.23.45.04;	author mrg;	state Exp;
branches;
next	;
commitid	KwhwN4krNWa6XBaG;


desc
@@


1.4
log
@merge GCC 14.3.0.
@
text
@//===-- tsan_fd.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//

#include "tsan_fd.h"

#include <sanitizer_common/sanitizer_atomic.h>

#include "tsan_interceptors.h"
#include "tsan_rtl.h"

namespace __tsan {

const int kTableSizeL1 = 1024;
const int kTableSizeL2 = 1024;
const int kTableSize = kTableSizeL1 * kTableSizeL2;

struct FdSync {
  atomic_uint64_t rc;
};

struct FdDesc {
  FdSync *sync;
  // This is used to establish write -> epoll_wait synchronization
  // where epoll_wait receives notification about the write.
  atomic_uintptr_t aux_sync;  // FdSync*
  Tid creation_tid;
  StackID creation_stack;
  bool closed;
};

struct FdContext {
  atomic_uintptr_t tab[kTableSizeL1];
  // Addresses used for synchronization.
  FdSync globsync;
  FdSync filesync;
  FdSync socksync;
  u64 connectsync;
};

static FdContext fdctx;

static bool bogusfd(int fd) {
  // Apparently a bogus fd value.
  return fd < 0 || fd >= kTableSize;
}

static FdSync *allocsync(ThreadState *thr, uptr pc) {
  FdSync *s = (FdSync*)user_alloc_internal(thr, pc, sizeof(FdSync),
      kDefaultAlignment, false);
  atomic_store(&s->rc, 1, memory_order_relaxed);
  return s;
}

static FdSync *ref(FdSync *s) {
  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
    atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
  return s;
}

static void unref(ThreadState *thr, uptr pc, FdSync *s) {
  if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
    if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
      CHECK_NE(s, &fdctx.globsync);
      CHECK_NE(s, &fdctx.filesync);
      CHECK_NE(s, &fdctx.socksync);
      user_free(thr, pc, s, false);
    }
  }
}

static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
  CHECK_GE(fd, 0);
  CHECK_LT(fd, kTableSize);
  atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
  uptr l1 = atomic_load(pl1, memory_order_consume);
  if (l1 == 0) {
    uptr size = kTableSizeL2 * sizeof(FdDesc);
    // We need this to reside in user memory to properly catch races on it.
    void *p = user_alloc_internal(thr, pc, size, kDefaultAlignment, false);
    internal_memset(p, 0, size);
    MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
    if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
      l1 = (uptr)p;
    else
      user_free(thr, pc, p, false);
  }
  FdDesc *fds = reinterpret_cast<FdDesc *>(l1);
  return &fds[fd % kTableSizeL2];
}

// pd must be already ref'ed.
static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,
    bool write = true) {
  FdDesc *d = fddesc(thr, pc, fd);
  // As a matter of fact, we don't intercept all close calls.
  // See e.g. libc __res_iclose().
  if (d->sync) {
    unref(thr, pc, d->sync);
    d->sync = 0;
  }
  unref(thr, pc,
        reinterpret_cast<FdSync *>(
            atomic_load(&d->aux_sync, memory_order_relaxed)));
  atomic_store(&d->aux_sync, 0, memory_order_relaxed);
  if (flags()->io_sync == 0) {
    unref(thr, pc, s);
  } else if (flags()->io_sync == 1) {
    d->sync = s;
  } else if (flags()->io_sync == 2) {
    unref(thr, pc, s);
    d->sync = &fdctx.globsync;
  }
  d->creation_tid = thr->tid;
  d->creation_stack = CurrentStackId(thr, pc);
  d->closed = false;
  // This prevents false positives on fd_close_norace3.cpp test.
  // The mechanics of the false positive are not completely clear,
  // but it happens only if global reset is enabled (flush_memory_ms=1)
  // and may be related to lost writes during asynchronous MADV_DONTNEED.
  SlotLocker locker(thr);
  if (write) {
    // To catch races between fd usage and open.
    MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
  } else {
    // See the dup-related comment in FdClose.
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead | kAccessSlotLocked);
  }
}

void FdInit() {
  atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
  atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
  atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
}

void FdOnFork(ThreadState *thr, uptr pc) {
  // On fork() we need to reset all fd's, because the child is going
  // close all them, and that will cause races between previous read/write
  // and the close.
  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
    if (tab == 0)
      break;
    for (int l2 = 0; l2 < kTableSizeL2; l2++) {
      FdDesc *d = &tab[l2];
      MemoryResetRange(thr, pc, (uptr)d, 8);
    }
  }
}

bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack, bool *closed) {
  for (int l1 = 0; l1 < kTableSizeL1; l1++) {
    FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
    if (tab == 0)
      break;
    if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
      int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
      FdDesc *d = &tab[l2];
      *fd = l1 * kTableSizeL1 + l2;
      *tid = d->creation_tid;
      *stack = d->creation_stack;
      *closed = d->closed;
      return true;
    }
  }
  return false;
}

void FdAcquire(ThreadState *thr, uptr pc, int fd) {
  if (bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  FdSync *s = d->sync;
  DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
  if (s)
    Acquire(thr, pc, (uptr)s);
}

void FdRelease(ThreadState *thr, uptr pc, int fd) {
  if (bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  FdSync *s = d->sync;
  DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
  if (s)
    Release(thr, pc, (uptr)s);
  if (uptr aux_sync = atomic_load(&d->aux_sync, memory_order_acquire))
    Release(thr, pc, aux_sync);
}

void FdAccess(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
}

void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {
  DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  {
    // Need to lock the slot to make MemoryAccess and MemoryResetRange atomic
    // with respect to global reset. See the comment in MemoryRangeFreed.
    SlotLocker locker(thr);
    if (!MustIgnoreInterceptor(thr)) {
      if (write) {
        // To catch races between fd usage and close.
        MemoryAccess(thr, pc, (uptr)d, 8,
                     kAccessWrite | kAccessCheckOnly | kAccessSlotLocked);
      } else {
        // This path is used only by dup2/dup3 calls.
        // We do read instead of write because there is a number of legitimate
        // cases where write would lead to false positives:
        // 1. Some software dups a closed pipe in place of a socket before
        // closing
        //    the socket (to prevent races actually).
        // 2. Some daemons dup /dev/null in place of stdin/stdout.
        // On the other hand we have not seen cases when write here catches real
        // bugs.
        MemoryAccess(thr, pc, (uptr)d, 8,
                     kAccessRead | kAccessCheckOnly | kAccessSlotLocked);
      }
    }
    // We need to clear it, because if we do not intercept any call out there
    // that creates fd, we will hit false postives.
    MemoryResetRange(thr, pc, (uptr)d, 8);
  }
  unref(thr, pc, d->sync);
  d->sync = 0;
  unref(thr, pc,
        reinterpret_cast<FdSync *>(
            atomic_load(&d->aux_sync, memory_order_relaxed)));
  atomic_store(&d->aux_sync, 0, memory_order_relaxed);
  d->closed = true;
  d->creation_tid = thr->tid;
  d->creation_stack = CurrentStackId(thr, pc);
}

void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, &fdctx.filesync);
}

void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {
  DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
  if (bogusfd(oldfd) || bogusfd(newfd))
    return;
  // Ignore the case when user dups not yet connected socket.
  FdDesc *od = fddesc(thr, pc, oldfd);
  MemoryAccess(thr, pc, (uptr)od, 8, kAccessRead);
  FdClose(thr, pc, newfd, write);
  init(thr, pc, newfd, ref(od->sync), write);
}

void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
  DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
  FdSync *s = allocsync(thr, pc);
  init(thr, pc, rfd, ref(s));
  init(thr, pc, wfd, ref(s));
  unref(thr, pc, s);
}

void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, allocsync(thr, pc));
}

void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, 0);
}

void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, 0);
}

void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, allocsync(thr, pc));
}

void FdPollAdd(ThreadState *thr, uptr pc, int epfd, int fd) {
  DPrintf("#%d: FdPollAdd(%d, %d)\n", thr->tid, epfd, fd);
  if (bogusfd(epfd) || bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  // Associate fd with epoll fd only once.
  // While an fd can be associated with multiple epolls at the same time,
  // or with different epolls during different phases of lifetime,
  // synchronization semantics (and examples) of this are unclear.
  // So we don't support this for now.
  // If we change the association, it will also create lifetime management
  // problem for FdRelease which accesses the aux_sync.
  if (atomic_load(&d->aux_sync, memory_order_relaxed))
    return;
  FdDesc *epd = fddesc(thr, pc, epfd);
  FdSync *s = epd->sync;
  if (!s)
    return;
  uptr cmp = 0;
  if (atomic_compare_exchange_strong(
          &d->aux_sync, &cmp, reinterpret_cast<uptr>(s), memory_order_release))
    ref(s);
}

void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  // It can be a UDP socket.
  init(thr, pc, fd, &fdctx.socksync);
}

void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
  DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
  if (bogusfd(fd))
    return;
  // Synchronize connect->accept.
  Acquire(thr, pc, (uptr)&fdctx.connectsync);
  init(thr, pc, newfd, &fdctx.socksync);
}

void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  // Synchronize connect->accept.
  Release(thr, pc, (uptr)&fdctx.connectsync);
}

void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
  DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
  if (bogusfd(fd))
    return;
  init(thr, pc, fd, &fdctx.socksync);
}

uptr File2addr(const char *path) {
  (void)path;
  static u64 addr;
  return (uptr)&addr;
}

uptr Dir2addr(const char *path) {
  (void)path;
  static u64 addr;
  return (uptr)&addr;
}

}  //  namespace __tsan
@


1.3
log
@make this actually be GCC 12.3.0's libsanitizer.

the libsanitizer we used with GCC 9 and GCC 10 was significantly
ahead of the GCC 9 and GCC 10 provided versions.
@
text
@d14 4
a18 1
#include <sanitizer_common/sanitizer_atomic.h>
d32 3
d37 1
d110 4
d124 6
d135 1
a135 1
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d160 1
a160 1
bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack) {
d171 1
d198 2
d215 26
a240 13
  if (write) {
    // To catch races between fd usage and close.
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessWrite);
  } else {
    // This path is used only by dup2/dup3 calls.
    // We do read instead of write because there is a number of legitimate
    // cases where write would lead to false positives:
    // 1. Some software dups a closed pipe in place of a socket before closing
    //    the socket (to prevent races actually).
    // 2. Some daemons dup /dev/null in place of stdin/stdout.
    // On the other hand we have not seen cases when write here catches real
    // bugs.
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
a241 3
  // We need to clear it, because if we do not intercept any call out there
  // that creates fd, we will hit false postives.
  MemoryResetRange(thr, pc, (uptr)d, 8);
d244 7
a250 2
  d->creation_tid = kInvalidTid;
  d->creation_stack = kInvalidStackID;
d307 24
@


1.2
log
@revert sanitizer back to the version we were using with GCC 9, since
that one was already newer than the GCC 10 version.
@
text
@d29 2
a30 2
  int creation_tid;
  u32 creation_stack;
d118 1
a118 1
    MemoryRead(thr, pc, (uptr)d, kSizeLog8);
d143 1
a143 1
bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
d166 1
a166 1
  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
d177 1
a177 1
  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
d187 1
a187 1
  MemoryRead(thr, pc, (uptr)d, kSizeLog8);
d197 1
a197 1
    MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
d207 1
a207 1
    MemoryRead(thr, pc, (uptr)d, kSizeLog8);
d214 2
a215 2
  d->creation_tid = 0;
  d->creation_stack = 0;
d231 1
a231 1
  MemoryRead(thr, pc, (uptr)od, kSizeLog8);
@


1.1
log
@Initial revision
@
text
@@


1.1.1.1
log
@initial import of GCC 10.3.0.  main changes include:

caveats:
- ABI issue between c++14 and c++17 fixed
- profile mode is removed from libstdc++
- -fno-common is now the default

new features:
- new flags -fallocation-dce, -fprofile-partial-training,
  -fprofile-reproducible, -fprofile-prefix-path, and -fanalyzer
- many new compile and link time optimisations
- enhanced drive optimisations
- openacc 2.6 support
- openmp 5.0 features
- new warnings: -Wstring-compare and -Wzero-length-bounds
- extended warnings: -Warray-bounds, -Wformat-overflow,
  -Wrestrict, -Wreturn-local-addr, -Wstringop-overflow,
  -Warith-conversion, -Wmismatched-tags, and -Wredundant-tags
- some likely C2X features implemented
- more C++20 implemented
- many new arm & intel CPUs known

hundreds of reported bugs are fixed.  full list of changes
can be found at:

   https://gcc.gnu.org/gcc-10/changes.html
@
text
@@


1.1.1.2
log
@initial import of GCC 12.3.0.

major changes in GCC 11 included:

- The default mode for C++ is now -std=gnu++17 instead of -std=gnu++14.
- When building GCC itself, the host compiler must now support C++11,
  rather than C++98.
- Some short options of the gcov tool have been renamed: -i to -j and
  -j to -H.
- ThreadSanitizer improvements.
- Introduce Hardware-assisted AddressSanitizer support.
- For targets that produce DWARF debugging information GCC now defaults
  to DWARF version 5. This can produce up to 25% more compact debug
  information compared to earlier versions.
- Many optimisations.
- The existing malloc attribute has been extended so that it can be
  used to identify allocator/deallocator API pairs. A pair of new
  -Wmismatched-dealloc and -Wmismatched-new-delete warnings are added.
- Other new warnings:
  -Wsizeof-array-div, enabled by -Wall, warns about divisions of two
    sizeof operators when the first one is applied to an array and the
    divisor does not equal the size of the array element.
  -Wstringop-overread, enabled by default, warns about calls to string
    functions reading past the end of the arrays passed to them as
    arguments.
  -Wtsan, enabled by default, warns about unsupported features in
    ThreadSanitizer (currently std::atomic_thread_fence).
- Enchanced warnings:
  -Wfree-nonheap-object detects many more instances of calls to
    deallocation functions with pointers not returned from a dynamic
    memory allocation function.
  -Wmaybe-uninitialized diagnoses passing pointers or references to
    uninitialized memory to functions taking const-qualified arguments.
  -Wuninitialized detects reads from uninitialized dynamically
    allocated memory.
  -Warray-parameter warns about functions with inconsistent array forms.
  -Wvla-parameter warns about functions with inconsistent VLA forms.
- Several new features from the upcoming C2X revision of the ISO C
  standard are supported with -std=c2x and -std=gnu2x.
- Several C++20 features have been implemented.
- The C++ front end has experimental support for some of the upcoming
  C++23 draft.
- Several new C++ warnings.
- Enhanced Arm, AArch64, x86, and RISC-V CPU support.
- The implementation of how program state is tracked within
  -fanalyzer has been completely rewritten with many enhancements.

see https://gcc.gnu.org/gcc-11/changes.html for a full list.

major changes in GCC 12 include:

- An ABI incompatibility between C and C++ when passing or returning
  by value certain aggregates containing zero width bit-fields has
  been discovered on various targets. x86-64, ARM and AArch64
  will always ignore them (so there is a C ABI incompatibility
  between GCC 11 and earlier with GCC 12 or later), PowerPC64 ELFv2
  always take them into account (so there is a C++ ABI
  incompatibility, GCC 4.4 and earlier compatible with GCC 12 or
  later, incompatible with GCC 4.5 through GCC 11). RISC-V has
  changed the handling of these already starting with GCC 10. As
  the ABI requires, MIPS takes them into account handling function
  return values so there is a C++ ABI incompatibility with GCC 4.5
  through 11.
- STABS: Support for emitting the STABS debugging format is
  deprecated and will be removed in the next release. All ports now
  default to emit DWARF (version 2 or later) debugging info or are
  obsoleted.
- Vectorization is enabled at -O2 which is now equivalent to the
  original -O2 -ftree-vectorize -fvect-cost-model=very-cheap.
- GCC now supports the ShadowCallStack sanitizer.
- Support for __builtin_shufflevector compatible with the clang
  language extension was added.
- Support for attribute unavailable was added.
- Support for __builtin_dynamic_object_size compatible with the
  clang language extension was added.
- New warnings:
  -Wbidi-chars warns about potentially misleading UTF-8
    bidirectional control characters.
  -Warray-compare warns about comparisons between two operands of
    array type.
- Some new features from the upcoming C2X revision of the ISO C
  standard are supported with -std=c2x and -std=gnu2x.
- Several C++23 features have been implemented.
- Many C++ enhancements across warnings and -f options.

see https://gcc.gnu.org/gcc-12/changes.html for a full list.
@
text
@d29 2
a30 2
  Tid creation_tid;
  StackID creation_stack;
d118 1
a118 1
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d143 1
a143 1
bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack) {
d166 1
a166 1
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d177 1
a177 1
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d187 1
a187 1
  MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d197 1
a197 1
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessWrite);
d207 1
a207 1
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead);
d214 2
a215 2
  d->creation_tid = kInvalidTid;
  d->creation_stack = kInvalidStackID;
d231 1
a231 1
  MemoryAccess(thr, pc, (uptr)od, 8, kAccessRead);
@


1.1.1.3
log
@initial import of GCC 14.3.0.

major changes in GCC 13:
- improved sanitizer
- zstd debug info compression
- LTO improvements
- SARIF based diagnostic support
- new warnings: -Wxor-used-as-pow, -Wenum-int-mismatch, -Wself-move,
  -Wdangling-reference
- many new -Wanalyzer* specific warnings
- enhanced warnings: -Wpessimizing-move, -Wredundant-move
- new attributes to mark file descriptors, c++23 "assume"
- several C23 features added
- several C++23 features added
- many new features for Arm, x86, RISC-V

major changes in GCC 14:
- more strict C99 or newer support
- ia64* marked deprecated (but seemingly still in GCC 15.)
- several new hardening features
- support for "hardbool", which can have user supplied values of true/false
- explicit support for stack scrubbing upon function exit
- better auto-vectorisation support
- added clang-compatible __has_feature and __has_extension
- more C23, including -std=c23
- several C++26 features added
- better diagnostics in C++ templates
- new warnings: -Wnrvo, Welaborated-enum-base
- many new features for Arm, x86, RISC-V
- possible ABI breaking change for SPARC64 and small structures with arrays
  of floats.
@
text
@d14 1
a14 1

a16 3
#include "tsan_interceptors.h"
#include "tsan_rtl.h"

a28 3
  // This is used to establish write -> epoll_wait synchronization
  // where epoll_wait receives notification about the write.
  atomic_uintptr_t aux_sync;  // FdSync*
a30 1
  bool closed;
a102 4
  unref(thr, pc,
        reinterpret_cast<FdSync *>(
            atomic_load(&d->aux_sync, memory_order_relaxed)));
  atomic_store(&d->aux_sync, 0, memory_order_relaxed);
a112 6
  d->closed = false;
  // This prevents false positives on fd_close_norace3.cpp test.
  // The mechanics of the false positive are not completely clear,
  // but it happens only if global reset is enabled (flush_memory_ms=1)
  // and may be related to lost writes during asynchronous MADV_DONTNEED.
  SlotLocker locker(thr);
d118 1
a118 1
    MemoryAccess(thr, pc, (uptr)d, 8, kAccessRead | kAccessSlotLocked);
d143 1
a143 1
bool FdLocation(uptr addr, int *fd, Tid *tid, StackID *stack, bool *closed) {
a153 1
      *closed = d->closed;
a179 2
  if (uptr aux_sync = atomic_load(&d->aux_sync, memory_order_acquire))
    Release(thr, pc, aux_sync);
d195 13
a207 26
  {
    // Need to lock the slot to make MemoryAccess and MemoryResetRange atomic
    // with respect to global reset. See the comment in MemoryRangeFreed.
    SlotLocker locker(thr);
    if (!MustIgnoreInterceptor(thr)) {
      if (write) {
        // To catch races between fd usage and close.
        MemoryAccess(thr, pc, (uptr)d, 8,
                     kAccessWrite | kAccessCheckOnly | kAccessSlotLocked);
      } else {
        // This path is used only by dup2/dup3 calls.
        // We do read instead of write because there is a number of legitimate
        // cases where write would lead to false positives:
        // 1. Some software dups a closed pipe in place of a socket before
        // closing
        //    the socket (to prevent races actually).
        // 2. Some daemons dup /dev/null in place of stdin/stdout.
        // On the other hand we have not seen cases when write here catches real
        // bugs.
        MemoryAccess(thr, pc, (uptr)d, 8,
                     kAccessRead | kAccessCheckOnly | kAccessSlotLocked);
      }
    }
    // We need to clear it, because if we do not intercept any call out there
    // that creates fd, we will hit false postives.
    MemoryResetRange(thr, pc, (uptr)d, 8);
d209 3
d214 2
a215 7
  unref(thr, pc,
        reinterpret_cast<FdSync *>(
            atomic_load(&d->aux_sync, memory_order_relaxed)));
  atomic_store(&d->aux_sync, 0, memory_order_relaxed);
  d->closed = true;
  d->creation_tid = thr->tid;
  d->creation_stack = CurrentStackId(thr, pc);
a271 24
void FdPollAdd(ThreadState *thr, uptr pc, int epfd, int fd) {
  DPrintf("#%d: FdPollAdd(%d, %d)\n", thr->tid, epfd, fd);
  if (bogusfd(epfd) || bogusfd(fd))
    return;
  FdDesc *d = fddesc(thr, pc, fd);
  // Associate fd with epoll fd only once.
  // While an fd can be associated with multiple epolls at the same time,
  // or with different epolls during different phases of lifetime,
  // synchronization semantics (and examples) of this are unclear.
  // So we don't support this for now.
  // If we change the association, it will also create lifetime management
  // problem for FdRelease which accesses the aux_sync.
  if (atomic_load(&d->aux_sync, memory_order_relaxed))
    return;
  FdDesc *epd = fddesc(thr, pc, epfd);
  FdSync *s = epd->sync;
  if (!s)
    return;
  uptr cmp = 0;
  if (atomic_compare_exchange_strong(
          &d->aux_sync, &cmp, reinterpret_cast<uptr>(s), memory_order_release))
    ref(s);
}

@


