Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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 reason: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
42
QT_BEGIN_NAMESPACE
43
44
namespace
QV4
{
45
46
enum
StackDefaults
:
qsizetype
{
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,
52
PlatformSafetyMargin
=
DefaultSafetyMargin
,
53
#
elif
defined
(
Q_OS_MACOS
)
54
PlatformStackSize
= 8 * 1024 * 1024,
55
PlatformSafetyMargin
=
DefaultSafetyMargin
,
56
#
elif
defined
(
Q_OS_ANDROID
)
57
// Android appears to have 1MB stacks.
58
PlatformStackSize
= 1024 * 1024,
59
PlatformSafetyMargin
=
DefaultSafetyMargin
,
60
#
elif
defined
(
Q_OS_LINUX
)
61
// On linux, we assume 8MB stacks if rlimit doesn't work.
62
PlatformStackSize
= 8 * 1024 * 1024,
63
PlatformSafetyMargin
=
DefaultSafetyMargin
,
64
#
elif
defined
(
Q_OS_QNX
)
65
// QNX's stack is only 512k by default
66
PlatformStackSize
= 512 * 1024,
67
PlatformSafetyMargin
=
DefaultSafetyMargin
,
68
#
else
69
// We try to claim 512k if we don't know anything else.
70
PlatformStackSize
= 512 * 1024,
71
PlatformSafetyMargin
=
DefaultSafetyMargin
,
72
#
endif
73
};
74
75
static
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.
91
static
qsizetype
getMainStackSizeFromRlimit
()
92
{
93
rlimit
limit
;
94
return
(
getrlimit
(
RLIMIT_STACK
, &
limit
) == 0 &&
limit
.
rlim_cur
!=
RLIM_INFINITY
)
95
?
qsizetype
(
limit
.
rlim_cur
)
96
:
qsizetype
(
PlatformStackSize
);
97
}
98
#
endif
99
100
#
if
defined
(
Q_OS_INTEGRITY
)
101
102
StackProperties
stackProperties
()
103
{
104
Address
stackLow
,
stackHigh
;
105
CheckSuccess
(
GetTaskStackLimits
(
CurrentTask
(), &
stackLow
, &
stackHigh
));
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
115
StackProperties
stackProperties
()
116
{
117
pthread_t
thread
=
pthread_self
();
118
return
createStackProperties
(
119
pthread_get_stackaddr_np
(
thread
),
120
pthread_main_np
()
121
?
getMainStackSizeFromRlimit
()
122
:
qsizetype
(
pthread_get_stacksize_np
(
thread
)));
123
}
124
125
#
elif
defined
(
Q_OS_WIN
)
126
127
static_assert
(
Q_STACK_GROWTH_DIRECTION
< 0);
128
StackProperties
stackProperties
()
129
{
130
// MinGW complains about out of bounds array access in compiler headers
131
QT_WARNING_PUSH
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
141
QT_WARNING_POP
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.
147
MEMORY_BASIC_INFORMATION
mbi
= {};
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
);
152
return
createStackProperties
(
stackBase
,
qsizetype
(
stackBase
-
stackLimit
));
153
}
154
155
#
elif
defined
(
Q_OS_OPENBSD
)
156
157
StackProperties
stackProperties
()
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
;
170
rc
=
pthread_stackseg_np
(
pthread_self
, &
ss
);
171
#
if
Q_STACK_GROWTH_DIRECTION
<
0
172
return
createStackProperties
(
ss
.
ss_sp
);
173
#
else
174
return
createStackProperties
(
decrementStackPointer
(
ss
.
ss_sp
,
ss
.
ss_size
));
175
#
endif
176
}
177
178
#
elif
defined
(
Q_OS_QNX
)
179
180
StackProperties
stackProperties
()
181
{
182
const
auto
tid
=
pthread_self
();
183
procfs_status
status
;
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
198
return
createStackProperties
(
199
decrementStackPointer
(
reinterpret_cast
<
void
*>(
status
.
stkbase
),
status
.
stksize
),
200
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
208
StackProperties
stackProperties
()
209
{
210
const
uintptr_t
base
=
emscripten_stack_get_base
();
211
const
uintptr_t
end
=
emscripten_stack_get_end
();
212
const
size_t
size
=
base
-
end
;
213
return
createStackProperties
(
reinterpret_cast
<
void
*>(
base
),
size
);
214
}
215
216
#
elif
defined
(
Q_OS_VXWORKS
)
217
218
StackProperties
stackProperties
()
219
{
220
TASK_DESC
taskDescription
;
221
taskInfoGet
(
taskIdSelf
(), &
taskDescription
);
222
return
createStackProperties
(
taskDescription
.
td_pStackBase
,
taskDescription
.
td_stackSize
,
223
taskDescription
.
td_stackSize
/ 8);
224
}
225
226
#
else
227
228
StackProperties
stackPropertiesGeneric
(qsizetype stackSize = 0)
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, ®ionSize);
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
260
static
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
272
struct
StackSegment
{
273
quintptr
base
;
274
quintptr
limit
;
275
};
276
277
static
StackSegment
stackSegmentFromProc
()
278
{
279
QFile
maps
(
QStringLiteral
(
"/proc/self/maps"
));
280
if
(!
maps
.
open
(
QIODevice
::
ReadOnly
))
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
291
const
QByteArrayView
line
(
buffer
,
length
);
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
316
StackProperties
stackProperties
()
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
325
const
qsizetype
stackSize
=
getMainStackSizeFromRlimit
();
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
())
331
return
createStackProperties
(
base
,
stackSize
);
332
}
333
334
// Try to read the stack segment from /proc/self/maps if possible.
335
const
StackSegment
segment
=
stackSegmentFromProc
();
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
342
return
createStackProperties
(
343
stackBase
,
stackSize
?
stackSize
:
segment
.
limit
-
segment
.
base
);
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.
349
return
stackPropertiesGeneric
(
stackSize
);
350
}
351
352
#
else
// Q_OS_LINUX
353
354
StackProperties
stackProperties
() {
return
stackPropertiesGeneric(); }
355
356
#
endif
// Q_OS_LINUX
357
#
endif
358
359
}
// namespace QV4
360
361
QT_END_NAMESPACE
QV4
Definition
qjsvalue.h:24
QV4::StackDefaults
StackDefaults
Definition
qv4stacklimits.cpp:46
QV4::DefaultSafetyMargin
@ DefaultSafetyMargin
Definition
qv4stacklimits.cpp:49
QV4::PlatformSafetyMargin
@ PlatformSafetyMargin
Definition
qv4stacklimits.cpp:71
QV4::PlatformStackSize
@ PlatformStackSize
Definition
qv4stacklimits.cpp:70
QV4::createStackProperties
static StackProperties createStackProperties(void *base, qsizetype size=PlatformStackSize, qsizetype margin=PlatformSafetyMargin)
Definition
qv4stacklimits.cpp:75
QV4::stackProperties
StackProperties stackProperties()
Definition
qv4stacklimits.cpp:354
QV4::stackPropertiesGeneric
StackProperties stackPropertiesGeneric(qsizetype stackSize=0)
Definition
qv4stacklimits.cpp:228
qtdeclarative
src
qml
memory
qv4stacklimits.cpp
Generated on
for Qt by
1.18.0