Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4stacklimits.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical raeson:low-level-memory-management
4
5#include <private/qv4stacklimits_p.h>
6#include <private/qobject_p.h>
7#include <private/qthread_p.h>
8
9#include <QtCore/qfile.h>
10
11#if defined(Q_OS_UNIX)
12# include <pthread.h>
13#endif
14
15#ifdef Q_OS_WIN
16# include <QtCore/qt_windows.h>
17#elif defined(Q_OS_FREEBSD_KERNEL) || defined(Q_OS_OPENBSD)
18# include <pthread_np.h>
19#elif defined(Q_OS_LINUX)
20# include <unistd.h>
21# include <sys/resource.h> // for getrlimit()
22# include <sys/syscall.h> // for SYS_gettid
23# if defined(__GLIBC__) && QT_CONFIG(dlopen)
24# include <dlfcn.h>
25# endif
26#elif defined(Q_OS_DARWIN)
27# include <sys/resource.h> // for getrlimit()
28#elif defined(Q_OS_QNX)
29# include <devctl.h>
30# include <sys/procfs.h>
31# include <sys/types.h>
32# include <unistd.h>
33# include <fcntl.h>
34#elif defined(Q_OS_INTEGRITY)
35# include <INTEGRITY.h>
36#elif defined(Q_OS_VXWORKS)
37# include <taskLib.h>
38#elif defined(Q_OS_WASM)
39# include <emscripten/stack.h>
40#endif
41
42QT_BEGIN_NAMESPACE
43
44namespace QV4 {
45
47 // Default safety margin at the end of the usable stack.
48 // Since we don't check the stack on every instruction, we might overrun our soft limit.
49 DefaultSafetyMargin = 128 * 1024,
50#if defined(Q_OS_IOS)
51 PlatformStackSize = 1024 * 1024,
53#elif defined(Q_OS_MACOS)
54 PlatformStackSize = 8 * 1024 * 1024,
56#elif defined(Q_OS_ANDROID)
57 // Android appears to have 1MB stacks.
58 PlatformStackSize = 1024 * 1024,
60#elif defined(Q_OS_LINUX)
61 // On linux, we assume 8MB stacks if rlimit doesn't work.
62 PlatformStackSize = 8 * 1024 * 1024,
64#elif defined(Q_OS_QNX)
65 // QNX's stack is only 512k by default
66 PlatformStackSize = 512 * 1024,
68#else
69 // We try to claim 512k if we don't know anything else.
70 PlatformStackSize = 512 * 1024,
72#endif
73};
74
75static StackProperties createStackProperties(void *base, qsizetype size = PlatformStackSize, qsizetype margin = PlatformSafetyMargin)
76{
77 return StackProperties {
78 base,
79 incrementStackPointer(base, size - margin),
80 incrementStackPointer(base, size),
81 };
82}
83
84#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX)
85
86// On linux and darwin, on the main thread, the pthread functions
87// may not return the true stack size since the main thread stack
88// may grow. Use rlimit instead. rlimit does not work for secondary
89// threads, though. If getrlimit fails, we assume the platform
90// stack size.
92{
97}
98#endif
99
100#if defined(Q_OS_INTEGRITY)
101
103{
106# if Q_STACK_GROWTH_DIRECTION < 0
107 return createStackProperties(reinterpret_cast<void *>(stackHigh), stackHigh - stackLow);
108# else
109 return createStackProperties(reinterpret_cast<void *>(stackLow), stackHigh - stackLow);
110# endif
111}
112
113#elif defined(Q_OS_DARWIN)
114
116{
123}
124
125#elif defined(Q_OS_WIN)
126
127static_assert(Q_STACK_GROWTH_DIRECTION < 0);
129{
130 // MinGW complains about out of bounds array access in compiler headers
132 QT_WARNING_DISABLE_GCC("-Warray-bounds")
133
134 // Get the stack base.
135# ifdef _WIN64
136 PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
137# else
138 PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
139# endif
140
142
143 quint8 *stackBase = reinterpret_cast<quint8 *>(pTib->StackBase);
144
145 // Get the stack limit. tib->StackLimit is the size of the
146 // currently mapped stack. The address space is larger.
148 if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
149 qFatal("Could not retrieve memory information for stack.");
150
151 quint8 *stackLimit = reinterpret_cast<quint8 *>(mbi.AllocationBase);
153}
154
155#elif defined(Q_OS_OPENBSD)
156
158{
159 // From the OpenBSD docs:
160 //
161 // The pthread_stackseg_np() function returns information about the given thread's stack.
162 // A stack_t is the same as a struct sigaltstack (see sigaltstack(2)) except the ss_sp
163 // variable points to the top of the stack instead of the base.
164 //
165 // Since the example in the sigaltstack(2) documentation shows ss_sp being assigned the result
166 // of a malloc() call, we can assume that "top of the stack" means "the highest address", not
167 // the logical top of the stack.
168
169 stack_t ss;
171#if Q_STACK_GROWTH_DIRECTION < 0
173#else
175#endif
176}
177
178#elif defined(Q_OS_QNX)
179
181{
182 const auto tid = pthread_self();
184 status.tid = tid;
185
186 const int fd = open("/proc/self/ctl", O_RDONLY);
187 if (fd == -1)
188 qFatal("Could not open /proc/self/ctl");
189 const auto guard = qScopeGuard([fd]() { close(fd); });
190
191 if (devctl(fd, DCMD_PROC_TIDSTATUS, &status, sizeof(status), 0) != EOK)
192 qFatal("Could not query thread status for current thread");
193
194 if (status.tid != tid)
195 qFatal("Thread status query returned garbage");
196
197#if Q_STACK_GROWTH_DIRECTION < 0
199 decrementStackPointer(reinterpret_cast<void *>(status.stkbase), status.stksize),
201#else
202 return createStackProperties(reinterpret_cast<void *>(status.stkbase), status.stksize);
203#endif
204}
205
206#elif defined(Q_OS_WASM)
207
209{
212 const size_t size = base - end;
213 return createStackProperties(reinterpret_cast<void *>(base), size);
214}
215
216#elif defined(Q_OS_VXWORKS)
217
219{
224}
225
226#else
227
229{
230 // If stackSize is given, do not trust the stack size returned by pthread_attr_getstack
231
232 pthread_t thread = pthread_self();
233 pthread_attr_t sattr;
234# if defined(PTHREAD_NP_H) || defined(_PTHREAD_NP_H_) || defined(Q_OS_NETBSD)
235 pthread_attr_init(&sattr);
236 pthread_attr_get_np(thread, &sattr);
237# else
238 pthread_getattr_np(thread, &sattr);
239# endif
240
241 // pthread_attr_getstack returns the address of the memory region, which is the physical
242 // base of the stack, not the logical one.
243 void *stackBase;
244 size_t regionSize;
245 int rc = pthread_attr_getstack(&sattr, &stackBase, &regionSize);
246 pthread_attr_destroy(&sattr);
247
248 if (rc)
249 qFatal("Cannot find stack base");
250
251# if Q_STACK_GROWTH_DIRECTION < 0
252 stackBase = decrementStackPointer(stackBase, regionSize);
253# endif
254
255 return createStackProperties(stackBase, stackSize ? stackSize : regionSize);
256}
257
258#if defined(Q_OS_LINUX)
259
260static void *stackBaseFromLibc()
261{
262#if defined(__GLIBC__) && QT_CONFIG(dlopen)
263 void **libcStackEnd = static_cast<void **>(dlsym(RTLD_DEFAULT, "__libc_stack_end"));
264 if (!libcStackEnd)
265 return nullptr;
266 if (void *stackBase = *libcStackEnd)
267 return stackBase;
268#endif
269 return nullptr;
270}
271
272struct StackSegment {
275};
276
278{
279 QFile maps(QStringLiteral("/proc/self/maps"));
281 return {0, 0};
282
283 const quintptr stackAddr = reinterpret_cast<quintptr>(&maps);
284
285 char buffer[1024];
286 while (true) {
287 const qint64 length = maps.readLine(buffer, 1024);
288 if (length <= 0)
289 break;
290
292 bool ok = false;
293
294 const qsizetype boundary = line.indexOf('-');
295 if (boundary < 0)
296 continue;
297
298 const quintptr base = line.sliced(0, boundary).toULongLong(&ok, 16);
299 if (!ok || base > stackAddr)
300 continue;
301
302 const qsizetype end = line.indexOf(' ', boundary);
303 if (end < 0)
304 continue;
305
306 const quintptr limit = line.sliced(boundary + 1, end - boundary - 1).toULongLong(&ok, 16);
307 if (!ok || limit <= stackAddr)
308 continue;
309
310 return {base, limit};
311 }
312
313 return {0, 0};
314}
315
317{
318 if (getpid() != static_cast<pid_t>(syscall(SYS_gettid)))
319 return stackPropertiesGeneric();
320
321 // On linux (including android), the pthread functions are expensive
322 // and unreliable on the main thread.
323
324 // First get the stack size from rlimit
326
327 // If we have glibc and libdl, we can query a special symbol in glibc to find the base.
328 // That is extremely cheap, compared to all other options.
329 if (stackSize) {
330 if (void *base = stackBaseFromLibc())
332 }
333
334 // Try to read the stack segment from /proc/self/maps if possible.
336 if (segment.base) {
337# if Q_STACK_GROWTH_DIRECTION > 0
338 void *stackBase = reinterpret_cast<void *>(segment.base);
339# else
340 void *stackBase = reinterpret_cast<void *>(segment.limit);
341# endif
344 }
345
346 // If we can't read /proc/self/maps, use the pthread functions after all, but
347 // override the stackSize. The main thread can grow its stack, and the pthread
348 // functions typically return the currently allocated stack size.
350}
351
352#else // Q_OS_LINUX
353
354StackProperties stackProperties() { return stackPropertiesGeneric(); }
355
356#endif // Q_OS_LINUX
357#endif
358
359} // namespace QV4
360
361QT_END_NAMESPACE
Definition qjsvalue.h:24
@ DefaultSafetyMargin
@ PlatformSafetyMargin
@ PlatformStackSize
static StackProperties createStackProperties(void *base, qsizetype size=PlatformStackSize, qsizetype margin=PlatformSafetyMargin)
StackProperties stackProperties()
StackProperties stackPropertiesGeneric(qsizetype stackSize=0)