<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>Fuzz Testing of Multithreaded Applications Based on Waiting</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>Oleg Doronin</string-name>
          <email>dorooleg@niuitmo.ru</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Karina Dergun</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Andrey Dergachev</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Aglaya Ilina</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Saint Petersburg National Research University of Information Technologies</institution>
          ,
          <addr-line>Mechanics and Optics, Russian Federation</addr-line>
        </aff>
      </contrib-group>
      <abstract>
        <p>In our days, it is hard to imagine big software products written without the use of multithreading. However, they not only use multithreading, but are also complicated by being distributed. On one hand, it gives performance advantages, but on the other hand, it becomes much more di cult to nd bugs and test such applications. When developing programs that use multithreading, we can nd the following types of errors: priority inversions, deadlock, livelock, ABA problem, and others. Such errors can lead to large nancial losses, for example, in the banking infrastructure, or losses of human lives in aircraft engineering, civil engineering, medical devices and other areas. Special tools such as Valgrind, Google TSAN and others are used to nd such bugs. Until recently, such tools were not able to fuzzing testing multithreaded applications, but now Google TSAN has a special module. The main limitation of the testing fuzzing module is that it is not able to handle waiting on nonatomic variables. The results presented in this paper allow us to carry fuzzing testing of threads and at the same time correctly handle the situation with waiting on variables that are not atomic, as well as examples on which the improved algorithm successfully copes with handling such waiting.</p>
      </abstract>
      <kwd-group>
        <kwd>multithreading</kwd>
        <kwd>data races</kwd>
        <kwd>deadlock</kwd>
        <kwd>bug- nding tools fuzzing testing</kwd>
      </kwd-group>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>-</title>
      <p>
        Fuzz testing or Fuzzing [
        <xref ref-type="bibr" rid="ref6">6</xref>
        ] allows to produce a new test coverage by trying out
various variants of the program's execution in automated fashion. The simplest
way is a brute force style of enumerating every execution variant. This approach
is not e ective, because computing power is not enough to go through every
combination of inputs for most programs. Therefore, in fuzzing testing, statistics
are collected and analyzed to reduce the required number of inputs. One option
Copyright c 2019 for this paper by its authors. Use permitted under Creative
Commons License Attribution 4.0 International (CC BY 4.0)
is to analyze the code coverage and choose di erent error- nding strategies, with
the subsequent build of assumptions related to the probability of error.
      </p>
      <p>As for the fuzz testing of multithreaded applications, instead of trying out
variants of the input arguments, the search goes through the combinations of
thread execution sequences. And in this case there are di erent strategies for
performing the search of execution sequences.</p>
      <p>
        At the moment, fuzz testing is being implemented into the main
development branch of the ThreadSanitizer (TSAN) [
        <xref ref-type="bibr" rid="ref3">3</xref>
        ] [
        <xref ref-type="bibr" rid="ref4">4</xref>
        ] tool. The following articles
describe previous work done on TSAN:
1. [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] An architecture for fuzz testing of multithreaded application, with various
thread execution planning strategies implemented to nd errors in threaded
code.
2. [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] Further work expanded the coverage of the code that can be tested using
the developed module; added support for working with lock-free algorithms
and atomic variables.
3. [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] However, there are still limitations to the current version, one of which
is the lack of support for mutexes [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ], so the present work removes this
shortcoming.
      </p>
      <p>1. Comparison of existing solutions</p>
      <p>
        RRD Lfouczkz-ifnrgee FuzzLinogckwith
uDsoeerscno'tderequire changes in - + +
sSyunpcphorrotnsiztahteiobnloaclkgionrgithms +/- -
+/tShurpepaodrtloscwalorsktoinraggwe ith - + +
Snounp-paotrotmsiwcovrakriinagblwesith - -
iImtdpoleems ennottatrieoqnuiorfe etahcehinbdloivckidinugalalgorithm - X
Here's a comparison of the various solutions that exist at the moment. The
comparison is made on 5 criteria:
1. Doesn't require changes in user code - this means that the user can
use the tool to phase out thread testing for the source code in the c++
language without making any changes to it. For example, Relacy Race
Detector (RRD) [
        <xref ref-type="bibr" rid="ref10">10</xref>
        ] in some cases requires changes in the code, which is a big
drawback. As a result, the code-changing approach can be more di cult.
It's very expensive for large enterprise applications to make changes to the
source code, as well as to maintain and accompany two versions. The second
problem is that such changes can lead to new errors, making it di cult to
test and develop software. As a result, the following decisions win in this
case: Lock-free fuzzing [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] and Fuzzing with Lock [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ]
2. Supports the blocking synchronization algorithms - the above tools
are mainly focused on working with atomic variables. When it comes to the
primitive synchronization (mutex, shared mutex, conditional variable), then
not all tools are able to work with them. For example, RRD and Fuzzing
with Lock can only work with some synchronization primitives, and
Lockfree fuzzing can't work with them.
3. Supports working with thread local storage - many advanced
algorithms use thread local storage. It's hard to imagine a memory allocator
that doesn't use TLS [
        <xref ref-type="bibr" rid="ref7">7</xref>
        ]. Also, many lock-free algorithms use such memory
for optimization or for storage of states. These algorithms include Hazard
Pointers [
        <xref ref-type="bibr" rid="ref8">8</xref>
        ]. RRD doesn't know how to work properly with TLS. The reason
for this behavior is the substitution of real threads for bers
4. Supports working with non-atomic variables - this wording is very
subtle and means that the algorithm is able to work with expectations on
non-atomic variables. An example of this case is described below. It turns out
that not one of the existing solutions does not know how to work correctly
with such variables
5. It does not require the individual implementation of each blocking
algorithm - the downside of RRD and Fuzzing with Lock is that they only
support a limited number of synchronization primitives. When you add new
synchronization primitives, you have to wait for a new implementation.
      </p>
      <p>
        This work improves the fuzz testing module described in [
        <xref ref-type="bibr" rid="ref1">1</xref>
        ] and [
        <xref ref-type="bibr" rid="ref2">2</xref>
        ] by
supporting the correct handling of locks/waiting on non-atomic variables and
eliminates the requirement to implement all synchronization algorithms.
2
      </p>
    </sec>
    <sec id="sec-2">
      <title>Waiting-based algorithm</title>
      <p>Let's start by describing the fuzz testing algorithm for threads based on waiting,
with an example that causes previous approaches to fuzz testing to hang:</p>
      <p>Example barrier
1 volatile std::byte barrier = 0;
2 void thread1() {
3 while (!barrier);
4 }
5</p>
      <sec id="sec-2-1">
        <title>6 void thread2() {</title>
        <p>7 barrier = true;
8 //...
9 }</p>
        <p>This example uses a simple barrier that suspends thread 1.</p>
        <p>The code is fully valid up to the CPU memory model, as the minimum
addressed memory unit is a byte.</p>
        <p>The main assumption on which we base the developed algorithm is that errors
in multithreaded applications appear only at synchronization points. Therefore,
thread switching points are selected among operations: reading/writing into an
atomic variable, capturing/releasing a mutex, waiting/noti cation on a
conditional variable, and others.</p>
        <p>Based on the above, we design the interface for thread scheduling fuzzing in
the simplest form of a single SynchronizationPoint method.</p>
      </sec>
      <sec id="sec-2-2">
        <title>1 class IScheduler { 2 virtual void SynchronizationPoint() = 0; 3 };</title>
      </sec>
      <sec id="sec-2-3">
        <title>Interface for schedulers</title>
        <p>To improve the thread scheduler fuzzing module, we now need to solve two
problems:
1. Implement planning algorithms for the presented interface
2. Introduce the developed interface into the TSAN architecture
Let's start with the second problem and describe how TSAN works:</p>
        <p>The image above shows a simpli ed layout of the program's compilation in
the C language. It embeds TSAN in the source code during the compilation
phase. As a result, we get the source code with replaced functions to work with
mutexes, conditional variables, instrumented reading and writing operations into
variables, and other intercepted functions.</p>
        <p>This approach allows to replace certain functionality transparently and to
create algorithms based on it to nd errors in the code. From the user's point
of view, it su ces to compile the source code with special compiler options,
and algorithms for nding errors will work. A downside of this approach is that
additional compilation time is required, and even with separate compilation the
linking stage can take considerable amount of time. However, for well-structured
programs this shortcoming is negligible.</p>
        <p>Let's look at what the described architecture looks like from a code
perspective. Suppose the user code gains ownership of the mutex:</p>
      </sec>
      <sec id="sec-2-4">
        <title>Mutex Example</title>
      </sec>
      <sec id="sec-2-5">
        <title>1 pthread_mutex_t mutex;</title>
        <p>2 //...</p>
      </sec>
      <sec id="sec-2-6">
        <title>3 pthread_mutex_lock(&amp;mutex);</title>
        <p>4 // application logic</p>
      </sec>
      <sec id="sec-2-7">
        <title>5 pthread_mutex_unlock(&amp;mutex);</title>
        <p>In fact, this code is converted to the following:</p>
      </sec>
      <sec id="sec-2-8">
        <title>1 pthread_mutex_t mutex;</title>
        <p>2 // ...</p>
      </sec>
      <sec id="sec-2-9">
        <title>3 tsan_mutex_lock(&amp;mutex);</title>
        <p>4 // ...</p>
      </sec>
      <sec id="sec-2-10">
        <title>5 tsan_mutex_unlock(&amp;mutex);</title>
      </sec>
      <sec id="sec-2-11">
        <title>Converted code</title>
        <p>The implementation of tsan mutex lock/unlock is taken care of by the
developers of algorithms for nding errors. For SynchronizationPoint, for example,
the use of tsan mutex lock will look like this:</p>
        <p>Converted code
1 int tsan_mutex_lock(void* mutex) {</p>
      </sec>
      <sec id="sec-2-12">
        <title>2 IScheduler::SyncronizationPoint();</title>
        <p>3 //...</p>
      </sec>
      <sec id="sec-2-13">
        <title>4 pthread_mutex_lock(mutex);</title>
        <p>5 //...</p>
      </sec>
      <sec id="sec-2-14">
        <title>6 IScheduler::SynchronizationPoint();</title>
        <p>7 }</p>
        <p>In example above we made two points of synchronisation: before taking the
mutex and after it. This is how IScheduler is embedded into TSAN. We now
progress to description of our algorithm for fuzz testing of multithreaded
applications. This algorithm should allow to deal with cases of thread hangs such as
described above. The states each thread in IScheduler can be in are:
1. UNKNOWN - the thread is in this state until it reaches the rst
synchronization point.
2. RUNNING - marks the main execution thread. Only one thread in the
program can have such this state at every moment in time.
3. WAIT - a thread in this state is waiting for its turn for execution.
4. OUT TIME - this state happens if a thread has exhausted its execution
quant but has not reached the next SynchronizationPoint. One reason for this
state can be the hanging of the thread on the waiting event, just as described
in the example with the barrier. Here, the thread remains on execution, and
there may be several threads in OUT TIME state. When these threads reach
SynchronizationPoint, they go into WAIT.</p>
        <p>
          Figure 2 shows the graph [
          <xref ref-type="bibr" rid="ref9">9</xref>
          ] states in which the process and transitions
between states may be located. Let's describe an example on two threads in
which threads will move between states on the graph:
1. Let two T1 (UNKNOWN) and T2 (UNKNOWN) threads be created in the
system. Since these threads have not met the synchronization point, they are
at the UNKNOWN point and the scheduling algorithm believes that such
threads do not exist in the system.
2. Suppose the T1 thread read the atomic variable or captured the
synchronization primitive, then it immediately goes into t1 (WAIT). The transition
from WAIT to RUNNING can happen instantly if the scheduling algorithm
decides so.
3. Let's say T1 (WAIT) stayed that state, but now the T2 thread has recorded
into an atomic variable, and it's gone into T2 (WAIT) and instantly switched
to T2 (RUNNING).
4. While the T2 thread was running, its time quant could run out and the
WatchDog thread decided to mark it OUT TIME and run the T1(RUNNING)
thread. When the T2 thread reaches the next synchronization point, it will
go into T2 (WAIT) and wait for the T1 thread to reach the next
synchronization.
        </p>
        <p>The example above describes a typical example of a thread wandering through
such a graph. It is worth noting that WatchDog constantly monitors all the
threads running in the system at some interval and can change these states for
arbitrary threads.</p>
        <p>The problem of thread hang-ups on a normal variable is solved by a state of
OUT TIME, when physically several threads can be executed. This algorithm
imposes restrictions on the data structures used in IScheduler: they must be
thread-safe. But what we get is the bene ts of no hang-ups in these algorithms
for di erent thread planning strategies; it works for any production-ready
applications.</p>
        <p>Let's look now at how to manage thread states. WatchDog is used to manage
the OUT TIME states. The schema shows the application architecture:</p>
        <p>The system has N threads, where N can increase or decrease. WatchDog
selects execution threads through SynchronizationPoint, except for the OUT TIME
state processing. A special WatchDog thread monitors all states and durations
of threads' work. It sets OUT TIME state if the thread has been running longer
than a de ned period of time. Depending on the choice of the length of this time
period, we can balance the quality and time of work.</p>
        <p>The pseudo-code for the SynchronizationPoint method implementation is:
tsan mutex lock
1 int tsan_mutex_lock(void* mutex) {</p>
      </sec>
      <sec id="sec-2-15">
        <title>2 IScheduler::SyncronizationPoint();</title>
        <p>3 //...</p>
      </sec>
      <sec id="sec-2-16">
        <title>4 pthread_mutex_lock(mutex);</title>
        <p>5 //...</p>
      </sec>
      <sec id="sec-2-17">
        <title>6 IScheduler::SynchronizationPoint();</title>
        <p>7 }</p>
        <p>In the example above, we do two synchronization points: before the mutex
capture and after the capture. This is how IScheduler is introduced into TSAN.</p>
        <p>Now let's go to the description of how to build a fuzzing algorithm for
testing multithreaded applications, which would bypass the thread sagging cases
described above.</p>
        <p>Let's start by describing the states in which each thread can be inside
IScheduler:</p>
        <p>SynchronizationPoint pseudo-code</p>
      </sec>
      <sec id="sec-2-18">
        <title>1 SynchronizationPoint():</title>
        <p>2 tid = GetTid();
3 oldState = state[tid];
4 state[tid] = WAIT;
5 if (oldState = RUNNING) {
6
7
8
9
nextTid = GetNextTid();
state[nextTid] = nextTid;
}
While (state[tid] == Wait) Yield();</p>
        <p>The pseudo-code above sets the (initial) state for each thread to WAIT unless
it was in OUT TIME state; and then the next thread is selected for execution,
so there can only be one RUNNING thread on the execution. The rest of the
work for OUT TIME state processing and preserving the invariant of just one
RUNNING thread takes place in the WatchDog thread.
3</p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>Conclusion</title>
      <p>
        This work improved the module for fuzz testing of multithreaded applications in
Google TSAN. We added support for the correct processing of application
hangup on non-atomic variables. This result allows TSAN to test any multithreaded
algorithms. For example, if we want to improve the quality of lock-free algorithm
testing, such as the libcds [
        <xref ref-type="bibr" rid="ref5">5</xref>
        ] library, it su ces to set an in nite time for the
state of OUT TIME. We can see the relevance of the results through the test
cases where the use of fuzz testing infrastructure led to a hanging state, but
now it works ne. We have created a review request for integration into google
TSAN main branch: https://reviews.llvm.org/D66235
      </p>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          1.
          <string-name>
            <surname>Doronin</surname>
            <given-names>O.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Dergun</surname>
            <given-names>K.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Dergachev</surname>
            <given-names>A</given-names>
          </string-name>
          .
          <article-title>Automatic fuzzy-scheduling of threads in Google Thread Sanitizer to detect</article-title>
          errors in multithreaded code // CEUR Workshop Proceedings - 2019, Vol.
          <volume>2344</volume>
          , pp.
          <fpage>1</fpage>
          -
          <lpage>12</lpage>
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          2.
          <string-name>
            <surname>Derghun</surname>
            <given-names>K.I.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Doronin</surname>
            <given-names>O.V.</given-names>
          </string-name>
          <article-title>Fazzing testirovanie ne-grained algoritmov, Sbornik tezisov dokladov kongressa molodyh uchenyh</article-title>
          . Elektronnoe izdanie. { SPb:
          <string-name>
            <surname>Universitet</surname>
            <given-names>ITMO</given-names>
          </string-name>
          , [
          <year>2019</year>
          ]
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          3.
          <article-title>ThreadSanitizer project: documentation, source code, dynamic annotations, unit tests</article-title>
          . http://code.google.com/p/data-race-test
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          4.
          <string-name>
            <surname>Serebryany</surname>
            ,
            <given-names>K.</given-names>
          </string-name>
          ,
          <string-name>
            <surname>Iskhodzhanov</surname>
          </string-name>
          , T.:
          <article-title>ThreadSanitizer: data race detection in practice</article-title>
          .
          <source>WBIA</source>
          (
          <year>2009</year>
          )
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          5.
          <string-name>
            <given-names>M.</given-names>
            <surname>Khizhinsky</surname>
          </string-name>
          , CDS C+
          <article-title>+ library</article-title>
          , https://github.com/khizmax/libcds
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          6.
          <string-name>
            <given-names>Majkl</given-names>
            <surname>Satton</surname>
          </string-name>
          , Adam Grin,
          <source>FUZZING. Issledovanie uyazvimostej metodom gruboj sily</source>
          ,
          <year>2009</year>
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          7.
          <string-name>
            <given-names>Patrick</given-names>
            <surname>Carribault</surname>
          </string-name>
          , Marc Perache, Herve Jourdren,
          <string-name>
            <surname>Thread-Local Storage Extension to Support Thread-Based</surname>
            <given-names>MPI</given-names>
          </string-name>
          /OpenMP Applications,
          <year>2011</year>
        </mixed-citation>
      </ref>
      <ref id="ref8">
        <mixed-citation>
          8.
          <string-name>
            <surname>Maged</surname>
            <given-names>M.</given-names>
          </string-name>
          <string-name>
            <surname>Michael</surname>
            , Michael Wong,
            <given-names>Hazard</given-names>
          </string-name>
          <string-name>
            <surname>Pointers</surname>
          </string-name>
          .
          <source>Safe Resource Reclamation for Optimistic Concurrency</source>
          ,
          <year>2016</year>
        </mixed-citation>
      </ref>
      <ref id="ref9">
        <mixed-citation>
          9.
          <string-name>
            <surname>Keijo</surname>
            <given-names>Ruohonen</given-names>
          </string-name>
          ,
          <source>GRAPH THEORY</source>
          ,
          <year>2013</year>
        </mixed-citation>
      </ref>
      <ref id="ref10">
        <mixed-citation>
          10.
          <string-name>
            <surname>Dmitry</surname>
            <given-names>Vyukov</given-names>
          </string-name>
          , Relacy Race Detector, http://www.1024cores.net/home/relacyrace-detector
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>