Bug Summary

File:src/lib/librthread/rthread_rwlock.c
Warning:line 129, column 6
Access to field 'value' results in a dereference of a null pointer (loaded from variable 'rwlock')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name rthread_rwlock.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/lib/librthread/obj -resource-dir /usr/local/lib/clang/13.0.0 -include namespace.h -I /usr/src/lib/librthread -I /usr/src/lib/librthread/../libc/arch/amd64 -I /usr/src/lib/librthread/../libc/include -D FUTEX -D PIC -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/librthread/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/lib/librthread/rthread_rwlock.c
1/* $OpenBSD: rthread_rwlock.c,v 1.13 2019/03/03 18:39:10 visa Exp $ */
2/*
3 * Copyright (c) 2019 Martin Pieuchot <mpi@openbsd.org>
4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <stdlib.h>
20#include <unistd.h>
21#include <errno(*__errno()).h>
22
23#include <pthread.h>
24
25#include "rthread.h"
26#include "synch.h"
27
28#define UNLOCKED0 0
29#define MAXREADER0x7ffffffe 0x7ffffffe
30#define WRITER0x7fffffff 0x7fffffff
31#define WAITING0x80000000 0x80000000
32#define COUNT(v)((v) & 0x7fffffff) ((v) & WRITER0x7fffffff)
33
34#define SPIN_COUNT128 128
35#if defined(__i386__) || defined(__amd64__1)
36#define SPIN_WAIT()asm volatile("pause": : : "memory") asm volatile("pause": : : "memory")
37#else
38#define SPIN_WAIT()asm volatile("pause": : : "memory") do { } while (0)
39#endif
40
41static _atomic_lock_t rwlock_init_lock = _SPINLOCK_UNLOCKED(0);
42
43int
44pthread_rwlock_init(pthread_rwlock_t *lockp,
45 const pthread_rwlockattr_t *attrp __unused__attribute__((__unused__)))
46{
47 pthread_rwlock_t rwlock;
48
49 rwlock = calloc(1, sizeof(*rwlock));
50 if (!rwlock)
51 return (errno(*__errno()));
52
53 *lockp = rwlock;
54
55 return (0);
56}
57DEF_STD(pthread_rwlock_init)__asm__(".global " "pthread_rwlock_init" " ; " "pthread_rwlock_init"
" = " "_libpthread_pthread_rwlock_init")
;
58
59int
60pthread_rwlock_destroy(pthread_rwlock_t *lockp)
61{
62 pthread_rwlock_t rwlock;
63
64 rwlock = *lockp;
65 if (rwlock) {
66 if (rwlock->value != UNLOCKED0) {
67#define MSG "pthread_rwlock_destroy on rwlock with waiters!\n"
68 write(2, MSG, sizeof(MSG) - 1);
69#undef MSG
70 return (EBUSY16);
71 }
72 free((void *)rwlock);
73 *lockp = NULL((void *)0);
74 }
75
76 return (0);
77}
78
79static int
80_rthread_rwlock_ensure_init(pthread_rwlock_t *rwlockp)
81{
82 int ret = 0;
83
84 /*
85 * If the rwlock is statically initialized, perform the dynamic
86 * initialization.
87 */
88 if (*rwlockp == NULL((void *)0)) {
89 _spinlock(&rwlock_init_lock);
90 if (*rwlockp == NULL((void *)0))
91 ret = pthread_rwlock_init(rwlockp, NULL((void *)0));
92 _spinunlock(&rwlock_init_lock);
93 }
94 return (ret);
95}
96
97static int
98_rthread_rwlock_tryrdlock(pthread_rwlock_t rwlock)
99{
100 unsigned int val;
101
102 do {
103 val = rwlock->value;
104 if (COUNT(val)((val) & 0x7fffffff) == WRITER0x7fffffff)
105 return (EBUSY16);
106 if (COUNT(val)((val) & 0x7fffffff) == MAXREADER0x7ffffffe)
107 return (EAGAIN35);
108 } while (atomic_cas_uint(&rwlock->value, val, val + 1)_atomic_cas_uint((&rwlock->value), (val), (val + 1)) != val);
109
110 membar_enter_after_atomic()do { __asm volatile("" ::: "memory"); } while (0);
111 return (0);
112}
113
114static int
115_rthread_rwlock_timedrdlock(pthread_rwlock_t *rwlockp, int trywait,
116 const struct timespec *abs, int timed)
117{
118 pthread_t self = pthread_self();
119 pthread_rwlock_t rwlock;
120 unsigned int val, new;
121 int i, error;
122
123 if ((error = _rthread_rwlock_ensure_init(rwlockp)))
2
Assuming 'error' is 0
3
Taking false branch
124 return (error);
125
126 rwlock = *rwlockp;
4
Null pointer value stored to 'rwlock'
127 _rthread_debug(5, "%p: rwlock_%srdlock %p (%u)\n", self,
128 (timed
4.1
'timed' is 0
? "timed" : (trywait
5.1
'trywait' is 0
? "try" : "")), (void *)rwlock,
5
'?' condition is false
6
'?' condition is false
129 rwlock->value);
7
Access to field 'value' results in a dereference of a null pointer (loaded from variable 'rwlock')
130
131 error = _rthread_rwlock_tryrdlock(rwlock);
132 if (error != EBUSY16 || trywait)
133 return (error);
134
135 /* Try hard to not enter the kernel. */
136 for (i = 0; i < SPIN_COUNT128; i++) {
137 val = rwlock->value;
138 if (val == UNLOCKED0 || (val & WAITING0x80000000))
139 break;
140
141 SPIN_WAIT()asm volatile("pause": : : "memory");
142 }
143
144 while ((error = _rthread_rwlock_tryrdlock(rwlock)) == EBUSY16) {
145 val = rwlock->value;
146 if (val == UNLOCKED0 || (COUNT(val)((val) & 0x7fffffff)) != WRITER0x7fffffff)
147 continue;
148 new = val | WAITING0x80000000;
149 if (atomic_cas_uint(&rwlock->value, val, new)_atomic_cas_uint((&rwlock->value), (val), (new)) == val) {
150 error = _twait(&rwlock->value, new, CLOCK_REALTIME0,
151 abs);
152 }
153 if (error == ETIMEDOUT60)
154 break;
155 }
156
157 return (error);
158
159}
160
161int
162pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlockp)
163{
164 return (_rthread_rwlock_timedrdlock(rwlockp, 1, NULL((void *)0), 0));
165}
166
167int
168pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlockp,
169 const struct timespec *abs)
170{
171 return (_rthread_rwlock_timedrdlock(rwlockp, 0, abs, 1));
172}
173
174int
175pthread_rwlock_rdlock(pthread_rwlock_t *rwlockp)
176{
177 return (_rthread_rwlock_timedrdlock(rwlockp, 0, NULL((void *)0), 0));
1
Calling '_rthread_rwlock_timedrdlock'
178}
179
180static int
181_rthread_rwlock_tryrwlock(pthread_rwlock_t rwlock)
182{
183 if (atomic_cas_uint(&rwlock->value, UNLOCKED, WRITER)_atomic_cas_uint((&rwlock->value), (0), (0x7fffffff)) != UNLOCKED0)
184 return (EBUSY16);
185
186 membar_enter_after_atomic()do { __asm volatile("" ::: "memory"); } while (0);
187 return (0);
188}
189
190
191static int
192_rthread_rwlock_timedwrlock(pthread_rwlock_t *rwlockp, int trywait,
193 const struct timespec *abs, int timed)
194{
195 pthread_t self = pthread_self();
196 pthread_rwlock_t rwlock;
197 unsigned int val, new;
198 int i, error;
199
200 if ((error = _rthread_rwlock_ensure_init(rwlockp)))
201 return (error);
202
203 rwlock = *rwlockp;
204 _rthread_debug(5, "%p: rwlock_%swrlock %p (%u)\n", self,
205 (timed ? "timed" : (trywait ? "try" : "")), (void *)rwlock,
206 rwlock->value);
207
208 error = _rthread_rwlock_tryrwlock(rwlock);
209 if (error != EBUSY16 || trywait)
210 return (error);
211
212 /* Try hard to not enter the kernel. */
213 for (i = 0; i < SPIN_COUNT128; i++) {
214 val = rwlock->value;
215 if (val == UNLOCKED0 || (val & WAITING0x80000000))
216 break;
217
218 SPIN_WAIT()asm volatile("pause": : : "memory");
219 }
220
221 while ((error = _rthread_rwlock_tryrwlock(rwlock)) == EBUSY16) {
222 val = rwlock->value;
223 if (val == UNLOCKED0)
224 continue;
225 new = val | WAITING0x80000000;
226 if (atomic_cas_uint(&rwlock->value, val, new)_atomic_cas_uint((&rwlock->value), (val), (new)) == val) {
227 error = _twait(&rwlock->value, new, CLOCK_REALTIME0,
228 abs);
229 }
230 if (error == ETIMEDOUT60)
231 break;
232 }
233
234 return (error);
235}
236
237int
238pthread_rwlock_trywrlock(pthread_rwlock_t *rwlockp)
239{
240 return (_rthread_rwlock_timedwrlock(rwlockp, 1, NULL((void *)0), 0));
241}
242
243int
244pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlockp,
245 const struct timespec *abs)
246{
247 return (_rthread_rwlock_timedwrlock(rwlockp, 0, abs, 1));
248}
249
250int
251pthread_rwlock_wrlock(pthread_rwlock_t *rwlockp)
252{
253 return (_rthread_rwlock_timedwrlock(rwlockp, 0, NULL((void *)0), 0));
254}
255
256int
257pthread_rwlock_unlock(pthread_rwlock_t *rwlockp)
258{
259 pthread_t self = pthread_self();
260 pthread_rwlock_t rwlock;
261 unsigned int val, new;
262
263 rwlock = *rwlockp;
264 _rthread_debug(5, "%p: rwlock_unlock %p\n", self, (void *)rwlock);
265
266 membar_exit_before_atomic()do { __asm volatile("" ::: "memory"); } while (0);
267 do {
268 val = rwlock->value;
269 if (COUNT(val)((val) & 0x7fffffff) == WRITER0x7fffffff || COUNT(val)((val) & 0x7fffffff) == 1)
270 new = UNLOCKED0;
271 else
272 new = val - 1;
273 } while (atomic_cas_uint(&rwlock->value, val, new)_atomic_cas_uint((&rwlock->value), (val), (new)) != val);
274
275 if (new == UNLOCKED0 && (val & WAITING0x80000000))
276 _wake(&rwlock->value, INT_MAX2147483647);
277
278 return (0);
279}