Bug Summary

File:build/source/lldb/source/Utility/SelectHelper.cpp
Warning:line 246, column 15
Array access (via field 'fds_bits') results in a null pointer dereference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SelectHelper.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -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 -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-17/lib/clang/17 -I tools/lldb/source/Utility -I /build/source/lldb/source/Utility -I /build/source/lldb/include -I tools/lldb/include -I include -I /build/source/llvm/include -I /usr/include/python3.9 -I /build/source/clang/include -I tools/lldb/../clang/include -I /usr/include/libxml2 -I /build/source/lldb/source -I tools/lldb/source -D HAVE_ROUND -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-17/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/source/= -fcoverage-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/source/= -source-date-epoch 1675289259 -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-stringop-truncation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/source/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/source/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2023-02-02-055145-558594-1 -x c++ /build/source/lldb/source/Utility/SelectHelper.cpp
1//===-- SelectHelper.cpp --------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#if defined(__APPLE__)
10// Enable this special support for Apple builds where we can have unlimited
11// select bounds. We tried switching to poll() and kqueue and we were panicing
12// the kernel, so we have to stick with select for now.
13#define _DARWIN_UNLIMITED_SELECT
14#endif
15
16#include "lldb/Utility/SelectHelper.h"
17#include "lldb/Utility/LLDBAssert.h"
18#include "lldb/Utility/Status.h"
19#include "lldb/lldb-enumerations.h"
20#include "lldb/lldb-types.h"
21
22#include "llvm/ADT/DenseMap.h"
23
24#include <algorithm>
25#include <chrono>
26#include <optional>
27
28#include <cerrno>
29#if defined(_WIN32)
30// Define NOMINMAX to avoid macros that conflict with std::min and std::max
31#define NOMINMAX
32#include <winsock2.h>
33#else
34#include <sys/time.h>
35#include <sys/select.h>
36#endif
37
38
39SelectHelper::SelectHelper()
40 : m_fd_map(), m_end_time() // Infinite timeout unless
41 // SelectHelper::SetTimeout() gets called
42{}
43
44void SelectHelper::SetTimeout(const std::chrono::microseconds &timeout) {
45 using namespace std::chrono;
46 m_end_time = steady_clock::time_point(steady_clock::now() + timeout);
47}
48
49void SelectHelper::FDSetRead(lldb::socket_t fd) {
50 m_fd_map[fd].read_set = true;
51}
52
53void SelectHelper::FDSetWrite(lldb::socket_t fd) {
54 m_fd_map[fd].write_set = true;
55}
56
57void SelectHelper::FDSetError(lldb::socket_t fd) {
58 m_fd_map[fd].error_set = true;
59}
60
61bool SelectHelper::FDIsSetRead(lldb::socket_t fd) const {
62 auto pos = m_fd_map.find(fd);
63 if (pos != m_fd_map.end())
64 return pos->second.read_is_set;
65 else
66 return false;
67}
68
69bool SelectHelper::FDIsSetWrite(lldb::socket_t fd) const {
70 auto pos = m_fd_map.find(fd);
71 if (pos != m_fd_map.end())
72 return pos->second.write_is_set;
73 else
74 return false;
75}
76
77bool SelectHelper::FDIsSetError(lldb::socket_t fd) const {
78 auto pos = m_fd_map.find(fd);
79 if (pos != m_fd_map.end())
80 return pos->second.error_is_set;
81 else
82 return false;
83}
84
85static void updateMaxFd(std::optional<lldb::socket_t> &vold,
86 lldb::socket_t vnew) {
87 if (!vold)
88 vold = vnew;
89 else
90 vold = std::max(*vold, vnew);
91}
92
93lldb_private::Status SelectHelper::Select() {
94 lldb_private::Status error;
95#ifdef _WIN32
96 // On windows FD_SETSIZE limits the number of file descriptors, not their
97 // numeric value.
98 lldbassert(m_fd_map.size() <= FD_SETSIZE)lldb_private::lldb_assert(static_cast<bool>(m_fd_map.size
() <= 1024), "m_fd_map.size() <= FD_SETSIZE", __FUNCTION__
, "lldb/source/Utility/SelectHelper.cpp", 98)
;
99 if (m_fd_map.size() > FD_SETSIZE1024)
100 return lldb_private::Status("Too many file descriptors for select()");
101#endif
102
103 std::optional<lldb::socket_t> max_read_fd;
104 std::optional<lldb::socket_t> max_write_fd;
105 std::optional<lldb::socket_t> max_error_fd;
106 std::optional<lldb::socket_t> max_fd;
107 for (auto &pair : m_fd_map) {
108 pair.second.PrepareForSelect();
109 const lldb::socket_t fd = pair.first;
110#if !defined(__APPLE__) && !defined(_WIN32)
111 lldbassert(fd < static_cast<int>(FD_SETSIZE))lldb_private::lldb_assert(static_cast<bool>(fd < static_cast
<int>(1024)), "fd < static_cast<int>(FD_SETSIZE)"
, __FUNCTION__, "lldb/source/Utility/SelectHelper.cpp", 111)
;
1
Assuming 'fd' is < 1024
112 if (fd
1.1
'fd' is < 1024
>= static_cast<int>(FD_SETSIZE1024)) {
2
Taking false branch
113 error.SetErrorStringWithFormat("%i is too large for select()", fd);
114 return error;
115 }
116#endif
117 if (pair.second.read_set)
3
Assuming field 'read_set' is false
4
Taking false branch
118 updateMaxFd(max_read_fd, fd);
119 if (pair.second.write_set)
5
Assuming field 'write_set' is false
6
Taking false branch
120 updateMaxFd(max_write_fd, fd);
121 if (pair.second.error_set)
7
Assuming field 'error_set' is false
8
Taking false branch
122 updateMaxFd(max_error_fd, fd);
123 updateMaxFd(max_fd, fd);
124 }
125
126 if (!max_fd) {
9
Taking false branch
127 error.SetErrorString("no valid file descriptors");
128 return error;
129 }
130
131 const unsigned nfds = static_cast<unsigned>(*max_fd) + 1;
132 fd_set *read_fdset_ptr = nullptr;
133 fd_set *write_fdset_ptr = nullptr;
134 fd_set *error_fdset_ptr = nullptr;
10
'error_fdset_ptr' initialized to a null pointer value
135// Initialize and zero out the fdsets
136#if defined(__APPLE__)
137 llvm::SmallVector<fd_set, 1> read_fdset;
138 llvm::SmallVector<fd_set, 1> write_fdset;
139 llvm::SmallVector<fd_set, 1> error_fdset;
140
141 if (max_read_fd.has_value()) {
142 read_fdset.resize((nfds / FD_SETSIZE1024) + 1);
143 read_fdset_ptr = read_fdset.data();
144 }
145 if (max_write_fd.has_value()) {
146 write_fdset.resize((nfds / FD_SETSIZE1024) + 1);
147 write_fdset_ptr = write_fdset.data();
148 }
149 if (max_error_fd.has_value()) {
150 error_fdset.resize((nfds / FD_SETSIZE1024) + 1);
151 error_fdset_ptr = error_fdset.data();
152 }
153 for (auto &fd_set : read_fdset)
154 FD_ZERO(&fd_set)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&fd_set)->fds_bits)[
0]) : "memory"); } while (0)
;
155 for (auto &fd_set : write_fdset)
156 FD_ZERO(&fd_set)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&fd_set)->fds_bits)[
0]) : "memory"); } while (0)
;
157 for (auto &fd_set : error_fdset)
158 FD_ZERO(&fd_set)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&fd_set)->fds_bits)[
0]) : "memory"); } while (0)
;
159#else
160 fd_set read_fdset;
161 fd_set write_fdset;
162 fd_set error_fdset;
163
164 if (max_read_fd) {
11
Taking false branch
165 FD_ZERO(&read_fdset)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&read_fdset)->fds_bits
)[0]) : "memory"); } while (0)
;
166 read_fdset_ptr = &read_fdset;
167 }
168 if (max_write_fd) {
12
Taking false branch
169 FD_ZERO(&write_fdset)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&write_fdset)->fds_bits
)[0]) : "memory"); } while (0)
;
170 write_fdset_ptr = &write_fdset;
171 }
172 if (max_error_fd) {
13
Taking false branch
173 FD_ZERO(&error_fdset)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&error_fdset)->fds_bits
)[0]) : "memory"); } while (0)
;
174 error_fdset_ptr = &error_fdset;
175 }
176#endif
177 // Set the FD bits in the fdsets for read/write/error
178 for (auto &pair : m_fd_map) {
179 const lldb::socket_t fd = pair.first;
180
181 if (pair.second.read_set)
182 FD_SET(fd, read_fdset_ptr)((void) (((read_fdset_ptr)->fds_bits)[__extension__ ({ long
int __d = (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] |= ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))))
;
183
184 if (pair.second.write_set)
185 FD_SET(fd, write_fdset_ptr)((void) (((write_fdset_ptr)->fds_bits)[__extension__ ({ long
int __d = (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] |= ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))))
;
186
187 if (pair.second.error_set)
188 FD_SET(fd, error_fdset_ptr)((void) (((error_fdset_ptr)->fds_bits)[__extension__ ({ long
int __d = (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] |= ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))))
;
189 }
190
191 // Setup our timeout time value if needed
192 struct timeval *tv_ptr = nullptr;
193 struct timeval tv = {0, 0};
194
195 while (true) {
14
Loop condition is true. Entering loop body
196 using namespace std::chrono;
197 // Setup out relative timeout based on the end time if we have one
198 if (m_end_time) {
15
Assuming the condition is false
16
Taking false branch
199 tv_ptr = &tv;
200 const auto remaining_dur =
201 duration_cast<microseconds>(*m_end_time - steady_clock::now());
202 if (remaining_dur.count() > 0) {
203 // Wait for a specific amount of time
204 const auto dur_secs = duration_cast<seconds>(remaining_dur);
205 const auto dur_usecs = remaining_dur % seconds(1);
206 tv.tv_sec = dur_secs.count();
207 tv.tv_usec = dur_usecs.count();
208 } else {
209 // Just poll once with no timeout
210 tv.tv_sec = 0;
211 tv.tv_usec = 0;
212 }
213 }
214 const int num_set_fds = ::select(nfds, read_fdset_ptr, write_fdset_ptr,
215 error_fdset_ptr, tv_ptr);
216 if (num_set_fds < 0) {
17
Assuming 'num_set_fds' is >= 0
18
Taking false branch
217 // We got an error
218 error.SetErrorToErrno();
219 if (error.GetError() == EINTR4) {
220 error.Clear();
221 continue; // Keep calling select if we get EINTR
222 } else
223 return error;
224 } else if (num_set_fds == 0) {
19
Assuming 'num_set_fds' is not equal to 0
20
Taking false branch
225 // Timeout
226 error.SetError(ETIMEDOUT110, lldb::eErrorTypePOSIX);
227 error.SetErrorString("timed out");
228 return error;
229 } else {
230 // One or more descriptors were set, update the FDInfo::select_is_set
231 // mask so users can ask the SelectHelper class so clients can call one
232 // of:
233
234 for (auto &pair : m_fd_map) {
235 const int fd = pair.first;
236
237 if (pair.second.read_set) {
21
Assuming field 'read_set' is false
22
Taking false branch
238 if (FD_ISSET(fd, read_fdset_ptr)((((read_fdset_ptr)->fds_bits)[__extension__ ({ long int __d
= (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] & ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))) != 0)
)
239 pair.second.read_is_set = true;
240 }
241 if (pair.second.write_set) {
23
Assuming field 'write_set' is false
24
Taking false branch
242 if (FD_ISSET(fd, write_fdset_ptr)((((write_fdset_ptr)->fds_bits)[__extension__ ({ long int __d
= (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] & ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))) != 0)
)
243 pair.second.write_is_set = true;
244 }
245 if (pair.second.error_set) {
25
Assuming field 'error_set' is true
26
Taking true branch
246 if (FD_ISSET(fd, error_fdset_ptr)((((error_fdset_ptr)->fds_bits)[__extension__ ({ long int __d
= (fd); (__builtin_constant_p (__d) ? (0 <= __d &&
__d < 1024 ? (__d / (8 * (int) sizeof (__fd_mask))) : __fdelt_warn
(__d)) : __fdelt_chk (__d)); })] & ((__fd_mask) (1UL <<
((fd) % (8 * (int) sizeof (__fd_mask)))))) != 0)
)
27
'?' condition is false
28
Array access (via field 'fds_bits') results in a null pointer dereference
247 pair.second.error_is_set = true;
248 }
249 }
250 break;
251 }
252 }
253 return error;
254}