1 /*
2   This file is part of drd, a thread error detector.
3 
4   Copyright (C) 2006-2017 Bart Van Assche <bvanassche@acm.org>.
5 
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   02111-1307, USA.
20 
21   The GNU General Public License is contained in the file COPYING.
22 */
23 
24 
25 #include "drd_clientobj.h"
26 #include "drd_cond.h"
27 #include "drd_error.h"
28 #include "drd_mutex.h"
29 #include "pub_tool_errormgr.h"    /* VG_(maybe_record_error)() */
30 #include "pub_tool_libcassert.h"  /* tl_assert()               */
31 #include "pub_tool_libcbase.h"    /* VG_(memcmp)()             */
32 #include "pub_tool_libcprint.h"   /* VG_(printf)()             */
33 #include "pub_tool_machine.h"     /* VG_(get_IP)()             */
34 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)()    */
35 
36 
37 /* Local functions. */
38 
39 static void DRD_(cond_cleanup)(struct cond_info* p);
40 
41 
42 /* Local variables. */
43 
44 static Bool DRD_(s_report_signal_unlocked) = True;
45 static Bool DRD_(s_trace_cond);
46 
47 
48 /* Function definitions. */
49 
DRD_(cond_set_report_signal_unlocked)50 void DRD_(cond_set_report_signal_unlocked)(const Bool r)
51 {
52    DRD_(s_report_signal_unlocked) = r;
53 }
54 
DRD_(cond_set_trace)55 void DRD_(cond_set_trace)(const Bool trace_cond)
56 {
57    DRD_(s_trace_cond) = trace_cond;
58 }
59 
60 static
DRD_(cond_initialize)61 void DRD_(cond_initialize)(struct cond_info* const p, const Addr cond)
62 {
63    tl_assert(cond != 0);
64    tl_assert(p->a1   == cond);
65    tl_assert(p->type == ClientCondvar);
66 
67    p->cleanup       = (void(*)(DrdClientobj*))(DRD_(cond_cleanup));
68    p->delete_thread = 0;
69    p->waiter_count  = 0;
70    p->mutex         = 0;
71 }
72 
73 /**
74  * Free the memory that was allocated by cond_initialize(). Called by
75  * DRD_(clientobj_remove)().
76  */
DRD_(cond_cleanup)77 static void DRD_(cond_cleanup)(struct cond_info* p)
78 {
79    tl_assert(p);
80    if (p->mutex)
81    {
82       struct mutex_info* q;
83       q = &(DRD_(clientobj_get)(p->mutex, ClientMutex)->mutex);
84       {
85          CondDestrErrInfo cde = {
86 	    DRD_(thread_get_running_tid)(),
87 	    p->a1,
88 	    q ? q->a1 : 0,
89 	    q ? q->owner : DRD_INVALID_THREADID
90 	 };
91          VG_(maybe_record_error)(VG_(get_running_tid)(),
92                                  CondDestrErr,
93                                  VG_(get_IP)(VG_(get_running_tid)()),
94                                  "Destroying condition variable that is being"
95                                  " waited upon",
96                                  &cde);
97       }
98    }
99 }
100 
101 /**
102  * Report that the synchronization object at address 'addr' is of the
103  * wrong type.
104  */
wrong_type(const Addr addr)105 static void wrong_type(const Addr addr)
106 {
107    GenericErrInfo gei = {
108       .tid  = DRD_(thread_get_running_tid)(),
109       .addr = addr,
110    };
111    VG_(maybe_record_error)(VG_(get_running_tid)(),
112                            GenericErr,
113                            VG_(get_IP)(VG_(get_running_tid)()),
114                            "wrong type of synchronization object",
115                            &gei);
116 }
117 
cond_get_or_allocate(const Addr cond)118 static struct cond_info* cond_get_or_allocate(const Addr cond)
119 {
120    struct cond_info *p;
121 
122    tl_assert(offsetof(DrdClientobj, cond) == 0);
123    p = &(DRD_(clientobj_get)(cond, ClientCondvar)->cond);
124    if (p)
125       return p;
126 
127    if (DRD_(clientobj_present)(cond, cond + 1))
128    {
129       wrong_type(cond);
130       return 0;
131    }
132 
133    p = &(DRD_(clientobj_add)(cond, ClientCondvar)->cond);
134    DRD_(cond_initialize)(p, cond);
135    return p;
136 }
137 
DRD_(cond_get)138 struct cond_info* DRD_(cond_get)(const Addr cond)
139 {
140    tl_assert(offsetof(DrdClientobj, cond) == 0);
141    return &(DRD_(clientobj_get)(cond, ClientCondvar)->cond);
142 }
143 
144 /** Called before pthread_cond_init(). */
DRD_(cond_pre_init)145 void DRD_(cond_pre_init)(const Addr cond)
146 {
147    struct cond_info* p;
148 
149    if (DRD_(s_trace_cond))
150       DRD_(trace_msg)("[%u] cond_init       cond 0x%lx",
151                       DRD_(thread_get_running_tid)(), cond);
152 
153    p = DRD_(cond_get)(cond);
154 
155    if (p) {
156       CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
157       VG_(maybe_record_error)(VG_(get_running_tid)(),
158                               CondErr,
159                               VG_(get_IP)(VG_(get_running_tid)()),
160                               "initialized twice",
161                               &cei);
162    }
163 
164    cond_get_or_allocate(cond);
165 }
166 
167 /** Called after pthread_cond_destroy(). */
DRD_(cond_post_destroy)168 void DRD_(cond_post_destroy)(const Addr cond, const Bool destroy_succeeded)
169 {
170    struct cond_info* p;
171 
172    if (DRD_(s_trace_cond))
173       DRD_(trace_msg)("[%u] cond_destroy    cond 0x%lx",
174                       DRD_(thread_get_running_tid)(), cond);
175 
176    p = DRD_(cond_get)(cond);
177    if (p == 0)
178    {
179       CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
180       VG_(maybe_record_error)(VG_(get_running_tid)(),
181                               CondErr,
182                               VG_(get_IP)(VG_(get_running_tid)()),
183                               "not a condition variable",
184                               &cei);
185       return;
186    }
187 
188    if (p->waiter_count != 0)
189    {
190       CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
191       VG_(maybe_record_error)(VG_(get_running_tid)(),
192                               CondErr,
193                               VG_(get_IP)(VG_(get_running_tid)()),
194                               "destruction of condition variable being waited"
195                               " upon",
196                               &cei);
197    }
198 
199    if (destroy_succeeded)
200       DRD_(clientobj_remove)(p->a1, ClientCondvar);
201 }
202 
203 /**
204  * Called before pthread_cond_wait(). Note: before this function is called,
205  * mutex_unlock() has already been called from drd_clientreq.c.
206  */
DRD_(cond_pre_wait)207 void DRD_(cond_pre_wait)(const Addr cond, const Addr mutex)
208 {
209    struct cond_info* p;
210    struct mutex_info* q;
211 
212    if (DRD_(s_trace_cond))
213       DRD_(trace_msg)("[%u] cond_pre_wait   cond 0x%lx",
214                       DRD_(thread_get_running_tid)(), cond);
215 
216    p = cond_get_or_allocate(cond);
217    if (!p)
218    {
219       CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
220       VG_(maybe_record_error)(VG_(get_running_tid)(),
221                               CondErr,
222                               VG_(get_IP)(VG_(get_running_tid)()),
223                               "not a condition variable",
224                               &cei);
225       return;
226    }
227 
228    if (p->waiter_count == 0)
229    {
230       p->mutex = mutex;
231    }
232    else if (p->mutex != mutex)
233    {
234       CondWaitErrInfo cwei
235          = { .tid = DRD_(thread_get_running_tid)(),
236              .cond = cond, .mutex1 = p->mutex, .mutex2 = mutex };
237       VG_(maybe_record_error)(VG_(get_running_tid)(),
238                               CondWaitErr,
239                               VG_(get_IP)(VG_(get_running_tid)()),
240                               "Inconsistent association of condition variable"
241                               " and mutex",
242                               &cwei);
243    }
244    tl_assert(p->mutex);
245    q = DRD_(mutex_get)(p->mutex);
246    if (q
247        && q->owner == DRD_(thread_get_running_tid)() && q->recursion_count > 0)
248    {
249       const ThreadId vg_tid = VG_(get_running_tid)();
250       MutexErrInfo MEI = { DRD_(thread_get_running_tid)(),
251                            q->a1, q->recursion_count, q->owner };
252       VG_(maybe_record_error)(vg_tid,
253                               MutexErr,
254                               VG_(get_IP)(vg_tid),
255                               "Mutex locked recursively",
256                               &MEI);
257    }
258    else if (q == 0)
259    {
260       DRD_(not_a_mutex)(p->mutex);
261    }
262 
263    ++p->waiter_count;
264 }
265 
266 /**
267  * Called after pthread_cond_wait().
268  */
DRD_(cond_post_wait)269 void DRD_(cond_post_wait)(const Addr cond)
270 {
271    struct cond_info* p;
272 
273    if (DRD_(s_trace_cond))
274       DRD_(trace_msg)("[%u] cond_post_wait  cond 0x%lx",
275                       DRD_(thread_get_running_tid)(), cond);
276 
277    p = DRD_(cond_get)(cond);
278    if (!p)
279    {
280       CondDestrErrInfo cde = {
281          DRD_(thread_get_running_tid)(), cond, 0, DRD_INVALID_THREADID
282       };
283       VG_(maybe_record_error)(VG_(get_running_tid)(),
284                               CondDestrErr,
285                               VG_(get_IP)(VG_(get_running_tid)()),
286                               "condition variable has been destroyed while"
287                               " being waited upon",
288                               &cde);
289       return;
290    }
291 
292    if (p->waiter_count > 0)
293    {
294       --p->waiter_count;
295       if (p->waiter_count == 0)
296       {
297 	 p->mutex = 0;
298       }
299    }
300 }
301 
cond_signal(const DrdThreadId tid,struct cond_info * const cond_p)302 static void cond_signal(const DrdThreadId tid, struct cond_info* const cond_p)
303 {
304    const ThreadId vg_tid = VG_(get_running_tid)();
305    const DrdThreadId drd_tid = DRD_(VgThreadIdToDrdThreadId)(vg_tid);
306 
307    tl_assert(cond_p);
308 
309    if (cond_p->waiter_count > 0)
310    {
311       if (DRD_(s_report_signal_unlocked)
312 	  && ! DRD_(mutex_is_locked_by)(cond_p->mutex, drd_tid))
313       {
314 	 /*
315 	  * A signal is sent while the associated mutex has not been locked.
316 	  * This can indicate but is not necessarily a race condition.
317 	  */
318 	 CondRaceErrInfo cei = { .tid = DRD_(thread_get_running_tid)(),
319 				 .cond  = cond_p->a1,
320 				 .mutex = cond_p->mutex,
321 	 };
322 	 VG_(maybe_record_error)(vg_tid,
323 				 CondRaceErr,
324 				 VG_(get_IP)(vg_tid),
325 				 "CondErr",
326 				 &cei);
327       }
328    }
329    else
330    {
331       /*
332        * No other thread is waiting for the signal, hence the signal will
333        * be lost. This is normal in a POSIX threads application.
334        */
335    }
336 }
337 
not_initialized(Addr const cond)338 static void not_initialized(Addr const cond)
339 {
340    CondErrInfo cei = { .tid = DRD_(thread_get_running_tid)(), .cond = cond };
341    VG_(maybe_record_error)(VG_(get_running_tid)(),
342                            CondErr,
343                            VG_(get_IP)(VG_(get_running_tid)()),
344                            "condition variable has not been initialized",
345                            &cei);
346 }
347 
348 /** Called before pthread_cond_signal(). */
DRD_(cond_pre_signal)349 void DRD_(cond_pre_signal)(Addr const cond)
350 {
351    struct cond_info* p;
352 
353    p = DRD_(cond_get)(cond);
354    if (DRD_(s_trace_cond))
355       DRD_(trace_msg)("[%u] cond_signal     cond 0x%lx",
356                       DRD_(thread_get_running_tid)(), cond);
357 
358    tl_assert(DRD_(pthread_cond_initializer));
359    if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer),
360                          DRD_(pthread_cond_initializer_size)) != 0)
361    {
362       not_initialized(cond);
363       return;
364    }
365 
366    if (!p)
367       p = cond_get_or_allocate(cond);
368 
369    cond_signal(DRD_(thread_get_running_tid)(), p);
370 }
371 
372 /** Called before pthread_cond_broadcast(). */
DRD_(cond_pre_broadcast)373 void DRD_(cond_pre_broadcast)(Addr const cond)
374 {
375    struct cond_info* p;
376 
377    if (DRD_(s_trace_cond))
378       DRD_(trace_msg)("[%u] cond_broadcast  cond 0x%lx",
379                       DRD_(thread_get_running_tid)(), cond);
380 
381    p = DRD_(cond_get)(cond);
382    tl_assert(DRD_(pthread_cond_initializer));
383    if (!p && VG_(memcmp)((void*)cond, (void*)DRD_(pthread_cond_initializer),
384                          DRD_(pthread_cond_initializer_size)) != 0)
385    {
386       not_initialized(cond);
387       return;
388    }
389 
390    if (!p)
391       p = cond_get_or_allocate(cond);
392 
393    cond_signal(DRD_(thread_get_running_tid)(), p);
394 }
395