1
The following changes since commit 812b835fb4d23dd108b2f9802158472d50b73579:
1
The following changes since commit 9cf289af47bcfae5c75de37d8e5d6fd23705322c:
2
2
3
Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2019-05-07' into staging (2019-05-09 16:31:12 +0100)
3
Merge tag 'qga-pull-request' of gitlab.com:marcandre.lureau/qemu into staging (2022-05-04 03:42:49 -0700)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
https://github.com/stefanha/qemu.git tags/block-pull-request
7
https://gitlab.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to e84125761f78919fe63616d9888ea45e72dc956f:
9
for you to fetch changes up to bef2e050d6a7feb865854c65570c496ac5a8cf53:
10
10
11
docs: add Security chapter to the documentation (2019-05-10 10:53:52 +0100)
11
util/event-loop-base: Introduce options to set the thread pool size (2022-05-04 17:02:19 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Pull request
14
Pull request
15
15
16
Add new thread-pool-min/thread-pool-max parameters to control the thread pool
17
used for async I/O.
18
16
----------------------------------------------------------------
19
----------------------------------------------------------------
17
20
18
Andrey Shinkevich (1):
21
Nicolas Saenz Julienne (3):
19
block/io.c: fix for the allocation failure
22
Introduce event-loop-base abstract class
23
util/main-loop: Introduce the main loop into QOM
24
util/event-loop-base: Introduce options to set the thread pool size
20
25
21
Jules Irenge (3):
26
qapi/qom.json | 43 ++++++++--
22
util/readline: add a space to fix errors by checkpatch tool
27
meson.build | 26 +++---
23
util: readline: replace tab indent by four spaces to fix checkpatch
28
include/block/aio.h | 10 +++
24
errors
29
include/block/thread-pool.h | 3 +
25
util/readline: Add braces to fix checkpatch errors
30
include/qemu/main-loop.h | 10 +++
26
31
include/sysemu/event-loop-base.h | 41 +++++++++
27
Nikita Alekseev (1):
32
include/sysemu/iothread.h | 6 +-
28
block: Add coroutine_fn to bdrv_check_co_entry
33
event-loop-base.c | 140 +++++++++++++++++++++++++++++++
29
34
iothread.c | 68 +++++----------
30
Paolo Bonzini (1):
35
util/aio-posix.c | 1 +
31
aio-posix: ensure poll mode is left when aio_notify is called
36
util/async.c | 20 +++++
32
37
util/main-loop.c | 65 ++++++++++++++
33
Stefan Hajnoczi (2):
38
util/thread-pool.c | 55 +++++++++++-
34
docs: add Secure Coding Practices to developer docs
39
13 files changed, 419 insertions(+), 69 deletions(-)
35
docs: add Security chapter to the documentation
40
create mode 100644 include/sysemu/event-loop-base.h
36
41
create mode 100644 event-loop-base.c
37
Makefile | 2 +-
38
block.c | 2 +-
39
block/io.c | 2 +-
40
util/aio-posix.c | 12 +-
41
util/readline.c | 174 ++++++++++++++-----------
42
docs/devel/index.rst | 1 +
43
docs/devel/secure-coding-practices.rst | 106 +++++++++++++++
44
docs/security.texi | 131 +++++++++++++++++++
45
qemu-doc.texi | 3 +
46
9 files changed, 347 insertions(+), 86 deletions(-)
47
create mode 100644 docs/devel/secure-coding-practices.rst
48
create mode 100644 docs/security.texi
49
42
50
--
43
--
51
2.21.0
44
2.35.1
52
53
diff view generated by jsdifflib
Deleted patch
1
From: Jules Irenge <jbi.octave@gmail.com>
2
1
3
util/readline: add a space to fix errors reported by checkpatch.pl tool
4
"ERROR: space required before the open parenthesis"
5
"ERROR: space required after that ..."
6
within "util/redline.c" file
7
8
Signed-off-by: Jules Irenge <jbi.octave@gmail.com>
9
Reviewed-by: Thomas Huth <thuth@redhat.com>
10
Message-id: 20190401024406.10819-2-jbi.octave@gmail.com
11
Message-Id: <20190401024406.10819-2-jbi.octave@gmail.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
util/readline.c | 34 +++++++++++++++++-----------------
15
1 file changed, 17 insertions(+), 17 deletions(-)
16
17
diff --git a/util/readline.c b/util/readline.c
18
index XXXXXXX..XXXXXXX 100644
19
--- a/util/readline.c
20
+++ b/util/readline.c
21
@@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs)
22
23
if (rs->cmd_buf_size != rs->last_cmd_buf_size ||
24
memcmp(rs->cmd_buf, rs->last_cmd_buf, rs->cmd_buf_size) != 0) {
25
- for(i = 0; i < rs->last_cmd_buf_index; i++) {
26
+ for (i = 0; i < rs->last_cmd_buf_index; i++) {
27
rs->printf_func(rs->opaque, "\033[D");
28
}
29
rs->cmd_buf[rs->cmd_buf_size] = '\0';
30
if (rs->read_password) {
31
len = strlen(rs->cmd_buf);
32
- for(i = 0; i < len; i++)
33
+ for (i = 0; i < len; i++)
34
rs->printf_func(rs->opaque, "*");
35
} else {
36
rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
37
@@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs)
38
if (rs->cmd_buf_index != rs->last_cmd_buf_index) {
39
delta = rs->cmd_buf_index - rs->last_cmd_buf_index;
40
if (delta > 0) {
41
- for(i = 0;i < delta; i++) {
42
+ for (i = 0; i < delta; i++) {
43
rs->printf_func(rs->opaque, "\033[C");
44
}
45
} else {
46
delta = -delta;
47
- for(i = 0;i < delta; i++) {
48
+ for (i = 0; i < delta; i++) {
49
rs->printf_func(rs->opaque, "\033[D");
50
}
51
}
52
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
53
return;
54
if (rs->nb_completions == 1) {
55
len = strlen(rs->completions[0]);
56
- for(i = rs->completion_index; i < len; i++) {
57
+ for (i = rs->completion_index; i < len; i++) {
58
readline_insert_char(rs, rs->completions[0][i]);
59
}
60
/* extra space for next argument. XXX: make it more generic */
61
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
62
completion_comp);
63
rs->printf_func(rs->opaque, "\n");
64
max_width = 0;
65
- max_prefix = 0;    
66
- for(i = 0; i < rs->nb_completions; i++) {
67
+ max_prefix = 0;
68
+ for (i = 0; i < rs->nb_completions; i++) {
69
len = strlen(rs->completions[i]);
70
- if (i==0) {
71
+ if (i == 0) {
72
max_prefix = len;
73
} else {
74
if (len < max_prefix)
75
max_prefix = len;
76
- for(j=0; j<max_prefix; j++) {
77
+ for (j = 0; j < max_prefix; j++) {
78
if (rs->completions[i][j] != rs->completions[0][j])
79
max_prefix = j;
80
}
81
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
82
if (len > max_width)
83
max_width = len;
84
}
85
- if (max_prefix > 0)
86
- for(i = rs->completion_index; i < max_prefix; i++) {
87
+ if (max_prefix > 0)
88
+ for (i = rs->completion_index; i < max_prefix; i++) {
89
readline_insert_char(rs, rs->completions[0][i]);
90
}
91
max_width += 2;
92
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
93
max_width = 80;
94
nb_cols = 80 / max_width;
95
j = 0;
96
- for(i = 0; i < rs->nb_completions; i++) {
97
+ for (i = 0; i < rs->nb_completions; i++) {
98
rs->printf_func(rs->opaque, "%-*s", max_width, rs->completions[i]);
99
if (++j == nb_cols || i == (rs->nb_completions - 1)) {
100
rs->printf_func(rs->opaque, "\n");
101
@@ -XXX,XX +XXX,XX @@ static void readline_clear_screen(ReadLineState *rs)
102
/* return true if command handled */
103
void readline_handle_byte(ReadLineState *rs, int ch)
104
{
105
- switch(rs->esc_state) {
106
+ switch (rs->esc_state) {
107
case IS_NORM:
108
- switch(ch) {
109
+ switch (ch) {
110
case 1:
111
readline_bol(rs);
112
break;
113
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
114
}
115
break;
116
case IS_CSI:
117
- switch(ch) {
118
+ switch (ch) {
119
    case 'A':
120
    case 'F':
121
     readline_up_char(rs);
122
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
123
rs->esc_param = rs->esc_param * 10 + (ch - '0');
124
goto the_end;
125
case '~':
126
- switch(rs->esc_param) {
127
+ switch (rs->esc_param) {
128
case 1:
129
readline_bol(rs);
130
break;
131
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
132
the_end:
133
break;
134
case IS_SS3:
135
- switch(ch) {
136
+ switch (ch) {
137
case 'F':
138
readline_eol(rs);
139
break;
140
--
141
2.21.0
142
143
diff view generated by jsdifflib
1
This new chapter in the QEMU documentation covers the security
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
requirements that QEMU is designed to meet and principles for securely
2
3
deploying QEMU.
3
Introduce the 'event-loop-base' abstract class, it'll hold the
4
4
properties common to all event loops and provide the necessary hooks for
5
It is just a starting point that can be extended in the future with more
5
their creation and maintenance. Then have iothread inherit from it.
6
information.
6
7
7
EventLoopBaseClass is defined as user creatable and provides a hook for
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
its children to attach themselves to the user creatable class 'complete'
9
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
9
function. It also provides an update_params() callback to propagate
10
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
10
property changes onto its children.
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
11
12
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
12
The new 'event-loop-base' class will live in the root directory. It is
13
Reviewed-by: Li Qiang <liq3ea@gmail.com>
13
built on its own using the 'link_whole' option (there are no direct
14
Message-id: 20190509121820.16294-3-stefanha@redhat.com
14
function dependencies between the class and its children, it all happens
15
Message-Id: <20190509121820.16294-3-stefanha@redhat.com>
15
trough 'constructor' magic). And also imposes new compilation
16
dependencies:
17
18
qom <- event-loop-base <- blockdev (iothread.c)
19
20
And in subsequent patches:
21
22
qom <- event-loop-base <- qemuutil (util/main-loop.c)
23
24
All this forced some amount of reordering in meson.build:
25
26
- Moved qom build definition before qemuutil. Doing it the other way
27
around (i.e. moving qemuutil after qom) isn't possible as a lot of
28
core libraries that live in between the two depend on it.
29
30
- Process the 'hw' subdir earlier, as it introduces files into the
31
'qom' source set.
32
33
No functional changes intended.
34
35
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
36
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
37
Acked-by: Markus Armbruster <armbru@redhat.com>
38
Message-id: 20220425075723.20019-2-nsaenzju@redhat.com
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
39
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
---
40
---
18
Makefile | 2 +-
41
qapi/qom.json | 22 +++++--
19
docs/security.texi | 131 +++++++++++++++++++++++++++++++++++++++++++++
42
meson.build | 23 ++++---
20
qemu-doc.texi | 3 ++
43
include/sysemu/event-loop-base.h | 36 +++++++++++
21
3 files changed, 135 insertions(+), 1 deletion(-)
44
include/sysemu/iothread.h | 6 +-
22
create mode 100644 docs/security.texi
45
event-loop-base.c | 104 +++++++++++++++++++++++++++++++
23
46
iothread.c | 65 ++++++-------------
24
diff --git a/Makefile b/Makefile
47
6 files changed, 192 insertions(+), 64 deletions(-)
48
create mode 100644 include/sysemu/event-loop-base.h
49
create mode 100644 event-loop-base.c
50
51
diff --git a/qapi/qom.json b/qapi/qom.json
25
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
26
--- a/Makefile
53
--- a/qapi/qom.json
27
+++ b/Makefile
54
+++ b/qapi/qom.json
28
@@ -XXX,XX +XXX,XX @@ qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
55
@@ -XXX,XX +XXX,XX @@
29
    qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
56
'*repeat': 'bool',
30
    qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
57
'*grab-toggle': 'GrabToggleKeys' } }
31
    qemu-monitor-info.texi docs/qemu-block-drivers.texi \
58
32
-    docs/qemu-cpu-models.texi
59
+##
33
+    docs/qemu-cpu-models.texi docs/security.texi
60
+# @EventLoopBaseProperties:
34
61
+#
35
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
62
+# Common properties for event loops
36
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
63
+#
37
diff --git a/docs/security.texi b/docs/security.texi
64
+# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
65
+# 0 means that the engine will use its default.
66
+# (default: 0)
67
+#
68
+# Since: 7.1
69
+##
70
+{ 'struct': 'EventLoopBaseProperties',
71
+ 'data': { '*aio-max-batch': 'int' } }
72
+
73
##
74
# @IothreadProperties:
75
#
76
@@ -XXX,XX +XXX,XX @@
77
# algorithm detects it is spending too long polling without
78
# encountering events. 0 selects a default behaviour (default: 0)
79
#
80
-# @aio-max-batch: maximum number of requests in a batch for the AIO engine,
81
-# 0 means that the engine will use its default
82
-# (default:0, since 6.1)
83
+# The @aio-max-batch option is available since 6.1.
84
#
85
# Since: 2.0
86
##
87
{ 'struct': 'IothreadProperties',
88
+ 'base': 'EventLoopBaseProperties',
89
'data': { '*poll-max-ns': 'int',
90
'*poll-grow': 'int',
91
- '*poll-shrink': 'int',
92
- '*aio-max-batch': 'int' } }
93
+ '*poll-shrink': 'int' } }
94
95
##
96
# @MemoryBackendProperties:
97
diff --git a/meson.build b/meson.build
98
index XXXXXXX..XXXXXXX 100644
99
--- a/meson.build
100
+++ b/meson.build
101
@@ -XXX,XX +XXX,XX @@ subdir('qom')
102
subdir('authz')
103
subdir('crypto')
104
subdir('ui')
105
+subdir('hw')
106
107
108
if enable_modules
109
@@ -XXX,XX +XXX,XX @@ if enable_modules
110
modulecommon = declare_dependency(link_whole: libmodulecommon, compile_args: '-DBUILD_DSO')
111
endif
112
113
+qom_ss = qom_ss.apply(config_host, strict: false)
114
+libqom = static_library('qom', qom_ss.sources() + genh,
115
+ dependencies: [qom_ss.dependencies()],
116
+ name_suffix: 'fa')
117
+qom = declare_dependency(link_whole: libqom)
118
+
119
+event_loop_base = files('event-loop-base.c')
120
+event_loop_base = static_library('event-loop-base', sources: event_loop_base + genh,
121
+ build_by_default: true)
122
+event_loop_base = declare_dependency(link_whole: event_loop_base,
123
+ dependencies: [qom])
124
+
125
stub_ss = stub_ss.apply(config_all, strict: false)
126
127
util_ss.add_all(trace_ss)
128
@@ -XXX,XX +XXX,XX @@ subdir('monitor')
129
subdir('net')
130
subdir('replay')
131
subdir('semihosting')
132
-subdir('hw')
133
subdir('tcg')
134
subdir('fpu')
135
subdir('accel')
136
@@ -XXX,XX +XXX,XX @@ qemu_syms = custom_target('qemu.syms', output: 'qemu.syms',
137
capture: true,
138
command: [undefsym, nm, '@INPUT@'])
139
140
-qom_ss = qom_ss.apply(config_host, strict: false)
141
-libqom = static_library('qom', qom_ss.sources() + genh,
142
- dependencies: [qom_ss.dependencies()],
143
- name_suffix: 'fa')
144
-
145
-qom = declare_dependency(link_whole: libqom)
146
-
147
authz_ss = authz_ss.apply(config_host, strict: false)
148
libauthz = static_library('authz', authz_ss.sources() + genh,
149
dependencies: [authz_ss.dependencies()],
150
@@ -XXX,XX +XXX,XX @@ libblockdev = static_library('blockdev', blockdev_ss.sources() + genh,
151
build_by_default: false)
152
153
blockdev = declare_dependency(link_whole: [libblockdev],
154
- dependencies: [block])
155
+ dependencies: [block, event_loop_base])
156
157
qmp_ss = qmp_ss.apply(config_host, strict: false)
158
libqmp = static_library('qmp', qmp_ss.sources() + genh,
159
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
38
new file mode 100644
160
new file mode 100644
39
index XXXXXXX..XXXXXXX
161
index XXXXXXX..XXXXXXX
40
--- /dev/null
162
--- /dev/null
41
+++ b/docs/security.texi
163
+++ b/include/sysemu/event-loop-base.h
42
@@ -XXX,XX +XXX,XX @@
164
@@ -XXX,XX +XXX,XX @@
43
+@node Security
165
+/*
44
+@chapter Security
166
+ * QEMU event-loop backend
45
+
167
+ *
46
+@section Overview
168
+ * Copyright (C) 2022 Red Hat Inc
47
+
169
+ *
48
+This chapter explains the security requirements that QEMU is designed to meet
170
+ * Authors:
49
+and principles for securely deploying QEMU.
171
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
50
+
172
+ *
51
+@section Security Requirements
173
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
52
+
174
+ * See the COPYING file in the top-level directory.
53
+QEMU supports many different use cases, some of which have stricter security
175
+ */
54
+requirements than others. The community has agreed on the overall security
176
+#ifndef QEMU_EVENT_LOOP_BASE_H
55
+requirements that users may depend on. These requirements define what is
177
+#define QEMU_EVENT_LOOP_BASE_H
56
+considered supported from a security perspective.
178
+
57
+
179
+#include "qom/object.h"
58
+@subsection Virtualization Use Case
180
+#include "block/aio.h"
59
+
181
+#include "qemu/typedefs.h"
60
+The virtualization use case covers cloud and virtual private server (VPS)
182
+
61
+hosting, as well as traditional data center and desktop virtualization. These
183
+#define TYPE_EVENT_LOOP_BASE "event-loop-base"
62
+use cases rely on hardware virtualization extensions to execute guest code
184
+OBJECT_DECLARE_TYPE(EventLoopBase, EventLoopBaseClass,
63
+safely on the physical CPU at close-to-native speed.
185
+ EVENT_LOOP_BASE)
64
+
186
+
65
+The following entities are untrusted, meaning that they may be buggy or
187
+struct EventLoopBaseClass {
66
+malicious:
188
+ ObjectClass parent_class;
67
+
189
+
68
+@itemize
190
+ void (*init)(EventLoopBase *base, Error **errp);
69
+@item Guest
191
+ void (*update_params)(EventLoopBase *base, Error **errp);
70
+@item User-facing interfaces (e.g. VNC, SPICE, WebSocket)
192
+};
71
+@item Network protocols (e.g. NBD, live migration)
193
+
72
+@item User-supplied files (e.g. disk images, kernels, device trees)
194
+struct EventLoopBase {
73
+@item Passthrough devices (e.g. PCI, USB)
195
+ Object parent;
74
+@end itemize
196
+
75
+
197
+ /* AioContext AIO engine parameters */
76
+Bugs affecting these entities are evaluated on whether they can cause damage in
198
+ int64_t aio_max_batch;
77
+real-world use cases and treated as security bugs if this is the case.
199
+};
78
+
200
+#endif
79
+@subsection Non-virtualization Use Case
201
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
80
+
81
+The non-virtualization use case covers emulation using the Tiny Code Generator
82
+(TCG). In principle the TCG and device emulation code used in conjunction with
83
+the non-virtualization use case should meet the same security requirements as
84
+the virtualization use case. However, for historical reasons much of the
85
+non-virtualization use case code was not written with these security
86
+requirements in mind.
87
+
88
+Bugs affecting the non-virtualization use case are not considered security
89
+bugs at this time. Users with non-virtualization use cases must not rely on
90
+QEMU to provide guest isolation or any security guarantees.
91
+
92
+@section Architecture
93
+
94
+This section describes the design principles that ensure the security
95
+requirements are met.
96
+
97
+@subsection Guest Isolation
98
+
99
+Guest isolation is the confinement of guest code to the virtual machine. When
100
+guest code gains control of execution on the host this is called escaping the
101
+virtual machine. Isolation also includes resource limits such as throttling of
102
+CPU, memory, disk, or network. Guests must be unable to exceed their resource
103
+limits.
104
+
105
+QEMU presents an attack surface to the guest in the form of emulated devices.
106
+The guest must not be able to gain control of QEMU. Bugs in emulated devices
107
+could allow malicious guests to gain code execution in QEMU. At this point the
108
+guest has escaped the virtual machine and is able to act in the context of the
109
+QEMU process on the host.
110
+
111
+Guests often interact with other guests and share resources with them. A
112
+malicious guest must not gain control of other guests or access their data.
113
+Disk image files and network traffic must be protected from other guests unless
114
+explicitly shared between them by the user.
115
+
116
+@subsection Principle of Least Privilege
117
+
118
+The principle of least privilege states that each component only has access to
119
+the privileges necessary for its function. In the case of QEMU this means that
120
+each process only has access to resources belonging to the guest.
121
+
122
+The QEMU process should not have access to any resources that are inaccessible
123
+to the guest. This way the guest does not gain anything by escaping into the
124
+QEMU process since it already has access to those same resources from within
125
+the guest.
126
+
127
+Following the principle of least privilege immediately fulfills guest isolation
128
+requirements. For example, guest A only has access to its own disk image file
129
+@code{a.img} and not guest B's disk image file @code{b.img}.
130
+
131
+In reality certain resources are inaccessible to the guest but must be
132
+available to QEMU to perform its function. For example, host system calls are
133
+necessary for QEMU but are not exposed to guests. A guest that escapes into
134
+the QEMU process can then begin invoking host system calls.
135
+
136
+New features must be designed to follow the principle of least privilege.
137
+Should this not be possible for technical reasons, the security risk must be
138
+clearly documented so users are aware of the trade-off of enabling the feature.
139
+
140
+@subsection Isolation mechanisms
141
+
142
+Several isolation mechanisms are available to realize this architecture of
143
+guest isolation and the principle of least privilege. With the exception of
144
+Linux seccomp, these mechanisms are all deployed by management tools that
145
+launch QEMU, such as libvirt. They are also platform-specific so they are only
146
+described briefly for Linux here.
147
+
148
+The fundamental isolation mechanism is that QEMU processes must run as
149
+unprivileged users. Sometimes it seems more convenient to launch QEMU as
150
+root to give it access to host devices (e.g. @code{/dev/net/tun}) but this poses a
151
+huge security risk. File descriptor passing can be used to give an otherwise
152
+unprivileged QEMU process access to host devices without running QEMU as root.
153
+It is also possible to launch QEMU as a non-root user and configure UNIX groups
154
+for access to @code{/dev/kvm}, @code{/dev/net/tun}, and other device nodes.
155
+Some Linux distros already ship with UNIX groups for these devices by default.
156
+
157
+@itemize
158
+@item SELinux and AppArmor make it possible to confine processes beyond the
159
+traditional UNIX process and file permissions model. They restrict the QEMU
160
+process from accessing processes and files on the host system that are not
161
+needed by QEMU.
162
+
163
+@item Resource limits and cgroup controllers provide throughput and utilization
164
+limits on key resources such as CPU time, memory, and I/O bandwidth.
165
+
166
+@item Linux namespaces can be used to make process, file system, and other system
167
+resources unavailable to QEMU. A namespaced QEMU process is restricted to only
168
+those resources that were granted to it.
169
+
170
+@item Linux seccomp is available via the QEMU @option{--sandbox} option. It disables
171
+system calls that are not needed by QEMU, thereby reducing the host kernel
172
+attack surface.
173
+@end itemize
174
diff --git a/qemu-doc.texi b/qemu-doc.texi
175
index XXXXXXX..XXXXXXX 100644
202
index XXXXXXX..XXXXXXX 100644
176
--- a/qemu-doc.texi
203
--- a/include/sysemu/iothread.h
177
+++ b/qemu-doc.texi
204
+++ b/include/sysemu/iothread.h
178
@@ -XXX,XX +XXX,XX @@
205
@@ -XXX,XX +XXX,XX @@
179
* QEMU Guest Agent::
206
#include "block/aio.h"
180
* QEMU User space emulator::
207
#include "qemu/thread.h"
181
* System requirements::
208
#include "qom/object.h"
182
+* Security::
209
+#include "sysemu/event-loop-base.h"
183
* Implementation notes::
210
184
* Deprecated features::
211
#define TYPE_IOTHREAD "iothread"
185
* Supported build platforms::
212
186
@@ -XXX,XX +XXX,XX @@ added with Linux 4.5 which is supported by the major distros. And even
213
struct IOThread {
187
if RHEL7 has kernel 3.10, KVM there has the required functionality there
214
- Object parent_obj;
188
to make it close to a 4.5 or newer kernel.
215
+ EventLoopBase parent_obj;
189
216
190
+@include docs/security.texi
217
QemuThread thread;
191
+
218
AioContext *ctx;
192
@include qemu-tech.texi
219
@@ -XXX,XX +XXX,XX @@ struct IOThread {
193
220
int64_t poll_max_ns;
194
@include qemu-deprecated.texi
221
int64_t poll_grow;
222
int64_t poll_shrink;
223
-
224
- /* AioContext AIO engine parameters */
225
- int64_t aio_max_batch;
226
};
227
typedef struct IOThread IOThread;
228
229
diff --git a/event-loop-base.c b/event-loop-base.c
230
new file mode 100644
231
index XXXXXXX..XXXXXXX
232
--- /dev/null
233
+++ b/event-loop-base.c
234
@@ -XXX,XX +XXX,XX @@
235
+/*
236
+ * QEMU event-loop base
237
+ *
238
+ * Copyright (C) 2022 Red Hat Inc
239
+ *
240
+ * Authors:
241
+ * Stefan Hajnoczi <stefanha@redhat.com>
242
+ * Nicolas Saenz Julienne <nsaenzju@redhat.com>
243
+ *
244
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
245
+ * See the COPYING file in the top-level directory.
246
+ */
247
+
248
+#include "qemu/osdep.h"
249
+#include "qom/object_interfaces.h"
250
+#include "qapi/error.h"
251
+#include "sysemu/event-loop-base.h"
252
+
253
+typedef struct {
254
+ const char *name;
255
+ ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
256
+} EventLoopBaseParamInfo;
257
+
258
+static EventLoopBaseParamInfo aio_max_batch_info = {
259
+ "aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
260
+};
261
+
262
+static void event_loop_base_get_param(Object *obj, Visitor *v,
263
+ const char *name, void *opaque, Error **errp)
264
+{
265
+ EventLoopBase *event_loop_base = EVENT_LOOP_BASE(obj);
266
+ EventLoopBaseParamInfo *info = opaque;
267
+ int64_t *field = (void *)event_loop_base + info->offset;
268
+
269
+ visit_type_int64(v, name, field, errp);
270
+}
271
+
272
+static void event_loop_base_set_param(Object *obj, Visitor *v,
273
+ const char *name, void *opaque, Error **errp)
274
+{
275
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(obj);
276
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
277
+ EventLoopBaseParamInfo *info = opaque;
278
+ int64_t *field = (void *)base + info->offset;
279
+ int64_t value;
280
+
281
+ if (!visit_type_int64(v, name, &value, errp)) {
282
+ return;
283
+ }
284
+
285
+ if (value < 0) {
286
+ error_setg(errp, "%s value must be in range [0, %" PRId64 "]",
287
+ info->name, INT64_MAX);
288
+ return;
289
+ }
290
+
291
+ *field = value;
292
+
293
+ if (bc->update_params) {
294
+ bc->update_params(base, errp);
295
+ }
296
+
297
+ return;
298
+}
299
+
300
+static void event_loop_base_complete(UserCreatable *uc, Error **errp)
301
+{
302
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
303
+ EventLoopBase *base = EVENT_LOOP_BASE(uc);
304
+
305
+ if (bc->init) {
306
+ bc->init(base, errp);
307
+ }
308
+}
309
+
310
+static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
311
+{
312
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
313
+ ucc->complete = event_loop_base_complete;
314
+
315
+ object_class_property_add(klass, "aio-max-batch", "int",
316
+ event_loop_base_get_param,
317
+ event_loop_base_set_param,
318
+ NULL, &aio_max_batch_info);
319
+}
320
+
321
+static const TypeInfo event_loop_base_info = {
322
+ .name = TYPE_EVENT_LOOP_BASE,
323
+ .parent = TYPE_OBJECT,
324
+ .instance_size = sizeof(EventLoopBase),
325
+ .class_size = sizeof(EventLoopBaseClass),
326
+ .class_init = event_loop_base_class_init,
327
+ .abstract = true,
328
+ .interfaces = (InterfaceInfo[]) {
329
+ { TYPE_USER_CREATABLE },
330
+ { }
331
+ }
332
+};
333
+
334
+static void register_types(void)
335
+{
336
+ type_register_static(&event_loop_base_info);
337
+}
338
+type_init(register_types);
339
diff --git a/iothread.c b/iothread.c
340
index XXXXXXX..XXXXXXX 100644
341
--- a/iothread.c
342
+++ b/iothread.c
343
@@ -XXX,XX +XXX,XX @@
344
#include "qemu/module.h"
345
#include "block/aio.h"
346
#include "block/block.h"
347
+#include "sysemu/event-loop-base.h"
348
#include "sysemu/iothread.h"
349
#include "qapi/error.h"
350
#include "qapi/qapi-commands-misc.h"
351
@@ -XXX,XX +XXX,XX @@ static void iothread_init_gcontext(IOThread *iothread)
352
iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE);
353
}
354
355
-static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
356
+static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
357
{
358
+ IOThread *iothread = IOTHREAD(base);
359
ERRP_GUARD();
360
361
+ if (!iothread->ctx) {
362
+ return;
363
+ }
364
+
365
aio_context_set_poll_params(iothread->ctx,
366
iothread->poll_max_ns,
367
iothread->poll_grow,
368
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(IOThread *iothread, Error **errp)
369
}
370
371
aio_context_set_aio_params(iothread->ctx,
372
- iothread->aio_max_batch,
373
+ iothread->parent_obj.aio_max_batch,
374
errp);
375
}
376
377
-static void iothread_complete(UserCreatable *obj, Error **errp)
378
+
379
+static void iothread_init(EventLoopBase *base, Error **errp)
380
{
381
Error *local_error = NULL;
382
- IOThread *iothread = IOTHREAD(obj);
383
+ IOThread *iothread = IOTHREAD(base);
384
char *thread_name;
385
386
iothread->stopping = false;
387
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
388
*/
389
iothread_init_gcontext(iothread);
390
391
- iothread_set_aio_context_params(iothread, &local_error);
392
+ iothread_set_aio_context_params(base, &local_error);
393
if (local_error) {
394
error_propagate(errp, local_error);
395
aio_context_unref(iothread->ctx);
396
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
397
* to inherit.
398
*/
399
thread_name = g_strdup_printf("IO %s",
400
- object_get_canonical_path_component(OBJECT(obj)));
401
+ object_get_canonical_path_component(OBJECT(base)));
402
qemu_thread_create(&iothread->thread, thread_name, iothread_run,
403
iothread, QEMU_THREAD_JOINABLE);
404
g_free(thread_name);
405
@@ -XXX,XX +XXX,XX @@ static IOThreadParamInfo poll_grow_info = {
406
static IOThreadParamInfo poll_shrink_info = {
407
"poll-shrink", offsetof(IOThread, poll_shrink),
408
};
409
-static IOThreadParamInfo aio_max_batch_info = {
410
- "aio-max-batch", offsetof(IOThread, aio_max_batch),
411
-};
412
413
static void iothread_get_param(Object *obj, Visitor *v,
414
const char *name, IOThreadParamInfo *info, Error **errp)
415
@@ -XXX,XX +XXX,XX @@ static void iothread_set_poll_param(Object *obj, Visitor *v,
416
}
417
}
418
419
-static void iothread_get_aio_param(Object *obj, Visitor *v,
420
- const char *name, void *opaque, Error **errp)
421
-{
422
- IOThreadParamInfo *info = opaque;
423
-
424
- iothread_get_param(obj, v, name, info, errp);
425
-}
426
-
427
-static void iothread_set_aio_param(Object *obj, Visitor *v,
428
- const char *name, void *opaque, Error **errp)
429
-{
430
- IOThread *iothread = IOTHREAD(obj);
431
- IOThreadParamInfo *info = opaque;
432
-
433
- if (!iothread_set_param(obj, v, name, info, errp)) {
434
- return;
435
- }
436
-
437
- if (iothread->ctx) {
438
- aio_context_set_aio_params(iothread->ctx,
439
- iothread->aio_max_batch,
440
- errp);
441
- }
442
-}
443
-
444
static void iothread_class_init(ObjectClass *klass, void *class_data)
445
{
446
- UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
447
- ucc->complete = iothread_complete;
448
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass);
449
+
450
+ bc->init = iothread_init;
451
+ bc->update_params = iothread_set_aio_context_params;
452
453
object_class_property_add(klass, "poll-max-ns", "int",
454
iothread_get_poll_param,
455
@@ -XXX,XX +XXX,XX @@ static void iothread_class_init(ObjectClass *klass, void *class_data)
456
iothread_get_poll_param,
457
iothread_set_poll_param,
458
NULL, &poll_shrink_info);
459
- object_class_property_add(klass, "aio-max-batch", "int",
460
- iothread_get_aio_param,
461
- iothread_set_aio_param,
462
- NULL, &aio_max_batch_info);
463
}
464
465
static const TypeInfo iothread_info = {
466
.name = TYPE_IOTHREAD,
467
- .parent = TYPE_OBJECT,
468
+ .parent = TYPE_EVENT_LOOP_BASE,
469
.class_init = iothread_class_init,
470
.instance_size = sizeof(IOThread),
471
.instance_init = iothread_instance_init,
472
.instance_finalize = iothread_instance_finalize,
473
- .interfaces = (InterfaceInfo[]) {
474
- {TYPE_USER_CREATABLE},
475
- {}
476
- },
477
};
478
479
static void iothread_register_types(void)
480
@@ -XXX,XX +XXX,XX @@ static int query_one_iothread(Object *object, void *opaque)
481
info->poll_max_ns = iothread->poll_max_ns;
482
info->poll_grow = iothread->poll_grow;
483
info->poll_shrink = iothread->poll_shrink;
484
- info->aio_max_batch = iothread->aio_max_batch;
485
+ info->aio_max_batch = iothread->parent_obj.aio_max_batch;
486
487
QAPI_LIST_APPEND(*tail, info);
488
return 0;
195
--
489
--
196
2.21.0
490
2.35.1
197
198
diff view generated by jsdifflib
1
From: Jules Irenge <jbi.octave@gmail.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
Replace tab indent by four spaces to fix errors issued by checkpatch.pl tool
3
'event-loop-base' provides basic property handling for all 'AioContext'
4
"ERROR: code indent should never use tabs" within "util/readline.c" file.
4
based event loops. So let's define a new 'MainLoopClass' that inherits
5
5
from it. This will permit tweaking the main loop's properties through
6
Signed-off-by: Jules Irenge <jbi.octave@gmail.com>
6
qapi as well as through the command line using the '-object' keyword[1].
7
Reviewed-by: Thomas Huth <thuth@redhat.com>
7
Only one instance of 'MainLoopClass' might be created at any time.
8
Message-id: 20190401024406.10819-3-jbi.octave@gmail.com
8
9
Message-Id: <20190401024406.10819-3-jbi.octave@gmail.com>
9
'EventLoopBaseClass' learns a new callback, 'can_be_deleted()' so as to
10
mark 'MainLoop' as non-deletable.
11
12
[1] For example:
13
-object main-loop,id=main-loop,aio-max-batch=<value>
14
15
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Acked-by: Markus Armbruster <armbru@redhat.com>
18
Message-id: 20220425075723.20019-3-nsaenzju@redhat.com
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
20
---
12
util/readline.c | 98 ++++++++++++++++++++++++-------------------------
21
qapi/qom.json | 13 ++++++++
13
1 file changed, 49 insertions(+), 49 deletions(-)
22
meson.build | 3 +-
14
23
include/qemu/main-loop.h | 10 ++++++
15
diff --git a/util/readline.c b/util/readline.c
24
include/sysemu/event-loop-base.h | 1 +
16
index XXXXXXX..XXXXXXX 100644
25
event-loop-base.c | 13 ++++++++
17
--- a/util/readline.c
26
util/main-loop.c | 56 ++++++++++++++++++++++++++++++++
18
+++ b/util/readline.c
27
6 files changed, 95 insertions(+), 1 deletion(-)
19
@@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs)
28
20
int idx;
29
diff --git a/qapi/qom.json b/qapi/qom.json
21
30
index XXXXXXX..XXXXXXX 100644
22
if (rs->hist_entry == 0)
31
--- a/qapi/qom.json
23
-    return;
32
+++ b/qapi/qom.json
24
+ return;
33
@@ -XXX,XX +XXX,XX @@
25
if (rs->hist_entry == -1) {
34
'*poll-grow': 'int',
26
-    /* Find latest entry */
35
'*poll-shrink': 'int' } }
27
-    for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
36
28
-     if (rs->history[idx] == NULL)
37
+##
29
-        break;
38
+# @MainLoopProperties:
30
-    }
39
+#
31
-    rs->hist_entry = idx;
40
+# Properties for the main-loop object.
32
+ /* Find latest entry */
41
+#
33
+ for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
42
+# Since: 7.1
34
+ if (rs->history[idx] == NULL)
43
+##
35
+ break;
44
+{ 'struct': 'MainLoopProperties',
36
+ }
45
+ 'base': 'EventLoopBaseProperties',
37
+ rs->hist_entry = idx;
46
+ 'data': {} }
38
}
47
+
39
rs->hist_entry--;
48
##
40
if (rs->hist_entry >= 0) {
49
# @MemoryBackendProperties:
41
-    pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
50
#
42
+ pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
51
@@ -XXX,XX +XXX,XX @@
43
rs->history[rs->hist_entry]);
52
{ 'name': 'input-linux',
44
-    rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
53
'if': 'CONFIG_LINUX' },
45
+ rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
54
'iothread',
55
+ 'main-loop',
56
{ 'name': 'memory-backend-epc',
57
'if': 'CONFIG_LINUX' },
58
'memory-backend-file',
59
@@ -XXX,XX +XXX,XX @@
60
'input-linux': { 'type': 'InputLinuxProperties',
61
'if': 'CONFIG_LINUX' },
62
'iothread': 'IothreadProperties',
63
+ 'main-loop': 'MainLoopProperties',
64
'memory-backend-epc': { 'type': 'MemoryBackendEpcProperties',
65
'if': 'CONFIG_LINUX' },
66
'memory-backend-file': 'MemoryBackendFileProperties',
67
diff --git a/meson.build b/meson.build
68
index XXXXXXX..XXXXXXX 100644
69
--- a/meson.build
70
+++ b/meson.build
71
@@ -XXX,XX +XXX,XX @@ libqemuutil = static_library('qemuutil',
72
sources: util_ss.sources() + stub_ss.sources() + genh,
73
dependencies: [util_ss.dependencies(), libm, threads, glib, socket, malloc, pixman])
74
qemuutil = declare_dependency(link_with: libqemuutil,
75
- sources: genh + version_res)
76
+ sources: genh + version_res,
77
+ dependencies: [event_loop_base])
78
79
if have_system or have_user
80
decodetree = generator(find_program('scripts/decodetree.py'),
81
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/qemu/main-loop.h
84
+++ b/include/qemu/main-loop.h
85
@@ -XXX,XX +XXX,XX @@
86
#define QEMU_MAIN_LOOP_H
87
88
#include "block/aio.h"
89
+#include "qom/object.h"
90
+#include "sysemu/event-loop-base.h"
91
92
#define SIG_IPI SIGUSR1
93
94
+#define TYPE_MAIN_LOOP "main-loop"
95
+OBJECT_DECLARE_TYPE(MainLoop, MainLoopClass, MAIN_LOOP)
96
+
97
+struct MainLoop {
98
+ EventLoopBase parent_obj;
99
+};
100
+typedef struct MainLoop MainLoop;
101
+
102
/**
103
* qemu_init_main_loop: Set up the process so that it can run the main loop.
104
*
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBaseClass {
110
111
void (*init)(EventLoopBase *base, Error **errp);
112
void (*update_params)(EventLoopBase *base, Error **errp);
113
+ bool (*can_be_deleted)(EventLoopBase *base);
114
};
115
116
struct EventLoopBase {
117
diff --git a/event-loop-base.c b/event-loop-base.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/event-loop-base.c
120
+++ b/event-loop-base.c
121
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_complete(UserCreatable *uc, Error **errp)
46
}
122
}
47
}
123
}
48
124
49
@@ -XXX,XX +XXX,XX @@ static void readline_down_char(ReadLineState *rs)
125
+static bool event_loop_base_can_be_deleted(UserCreatable *uc)
50
return;
126
+{
51
if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
127
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_GET_CLASS(uc);
52
rs->history[++rs->hist_entry] != NULL) {
128
+ EventLoopBase *backend = EVENT_LOOP_BASE(uc);
53
-    pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
129
+
54
+ pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
130
+ if (bc->can_be_deleted) {
55
rs->history[rs->hist_entry]);
131
+ return bc->can_be_deleted(backend);
56
} else {
132
+ }
57
rs->cmd_buf[0] = 0;
133
+
58
-    rs->hist_entry = -1;
134
+ return true;
59
+ rs->hist_entry = -1;
135
+}
60
}
136
+
61
rs->cmd_buf_index = rs->cmd_buf_size = strlen(rs->cmd_buf);
137
static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
138
{
139
UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
140
ucc->complete = event_loop_base_complete;
141
+ ucc->can_be_deleted = event_loop_base_can_be_deleted;
142
143
object_class_property_add(klass, "aio-max-batch", "int",
144
event_loop_base_get_param,
145
diff --git a/util/main-loop.c b/util/main-loop.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/util/main-loop.c
148
+++ b/util/main-loop.c
149
@@ -XXX,XX +XXX,XX @@
150
#include "qemu/error-report.h"
151
#include "qemu/queue.h"
152
#include "qemu/compiler.h"
153
+#include "qom/object.h"
154
155
#ifndef _WIN32
156
#include <sys/wait.h>
157
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
158
return 0;
62
}
159
}
63
@@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline)
160
64
int idx;
161
+static void main_loop_update_params(EventLoopBase *base, Error **errp)
65
162
+{
66
if (cmdline[0] == '\0')
163
+ if (!qemu_aio_context) {
67
-    return;
164
+ error_setg(errp, "qemu aio context not ready");
68
+ return;
165
+ return;
69
new_entry = NULL;
166
+ }
70
if (rs->hist_entry != -1) {
167
+
71
-    /* We were editing an existing history entry: replace it */
168
+ aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
72
-    hist_entry = rs->history[rs->hist_entry];
169
+}
73
-    idx = rs->hist_entry;
170
+
74
-    if (strcmp(hist_entry, cmdline) == 0) {
171
+MainLoop *mloop;
75
-     goto same_entry;
172
+
76
-    }
173
+static void main_loop_init(EventLoopBase *base, Error **errp)
77
+ /* We were editing an existing history entry: replace it */
174
+{
78
+ hist_entry = rs->history[rs->hist_entry];
175
+ MainLoop *m = MAIN_LOOP(base);
79
+ idx = rs->hist_entry;
176
+
80
+ if (strcmp(hist_entry, cmdline) == 0) {
177
+ if (mloop) {
81
+ goto same_entry;
178
+ error_setg(errp, "only one main-loop instance allowed");
82
+ }
179
+ return;
83
}
180
+ }
84
/* Search cmdline in history buffers */
181
+
85
for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
182
+ main_loop_update_params(base, errp);
86
-    hist_entry = rs->history[idx];
183
+
87
-    if (hist_entry == NULL)
184
+ mloop = m;
88
-     break;
185
+ return;
89
-    if (strcmp(hist_entry, cmdline) == 0) {
186
+}
90
-    same_entry:
187
+
91
-     new_entry = hist_entry;
188
+static bool main_loop_can_be_deleted(EventLoopBase *base)
92
-     /* Put this entry at the end of history */
189
+{
93
-     memmove(&rs->history[idx], &rs->history[idx + 1],
190
+ return false;
94
-         (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *));
191
+}
95
-     rs->history[READLINE_MAX_CMDS - 1] = NULL;
192
+
96
-     for (; idx < READLINE_MAX_CMDS; idx++) {
193
+static void main_loop_class_init(ObjectClass *oc, void *class_data)
97
-        if (rs->history[idx] == NULL)
194
+{
98
-         break;
195
+ EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc);
99
-     }
196
+
100
-     break;
197
+ bc->init = main_loop_init;
101
-    }
198
+ bc->update_params = main_loop_update_params;
102
+ hist_entry = rs->history[idx];
199
+ bc->can_be_deleted = main_loop_can_be_deleted;
103
+ if (hist_entry == NULL)
200
+}
104
+ break;
201
+
105
+ if (strcmp(hist_entry, cmdline) == 0) {
202
+static const TypeInfo main_loop_info = {
106
+ same_entry:
203
+ .name = TYPE_MAIN_LOOP,
107
+ new_entry = hist_entry;
204
+ .parent = TYPE_EVENT_LOOP_BASE,
108
+ /* Put this entry at the end of history */
205
+ .class_init = main_loop_class_init,
109
+ memmove(&rs->history[idx], &rs->history[idx + 1],
206
+ .instance_size = sizeof(MainLoop),
110
+ (READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *));
207
+};
111
+ rs->history[READLINE_MAX_CMDS - 1] = NULL;
208
+
112
+ for (; idx < READLINE_MAX_CMDS; idx++) {
209
+static void main_loop_register_types(void)
113
+ if (rs->history[idx] == NULL)
210
+{
114
+ break;
211
+ type_register_static(&main_loop_info);
115
+ }
212
+}
116
+ break;
213
+
117
+ }
214
+type_init(main_loop_register_types)
118
}
215
+
119
if (idx == READLINE_MAX_CMDS) {
216
static int max_priority;
120
-    /* Need to get one free slot */
217
121
+ /* Need to get one free slot */
218
#ifndef _WIN32
122
g_free(rs->history[0]);
123
-    memmove(rs->history, &rs->history[1],
124
-     (READLINE_MAX_CMDS - 1) * sizeof(char *));
125
-    rs->history[READLINE_MAX_CMDS - 1] = NULL;
126
-    idx = READLINE_MAX_CMDS - 1;
127
+ memmove(rs->history, &rs->history[1],
128
+ (READLINE_MAX_CMDS - 1) * sizeof(char *));
129
+ rs->history[READLINE_MAX_CMDS - 1] = NULL;
130
+ idx = READLINE_MAX_CMDS - 1;
131
}
132
if (new_entry == NULL)
133
new_entry = g_strdup(cmdline);
134
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
135
case 8:
136
readline_backspace(rs);
137
break;
138
-    case 155:
139
+ case 155:
140
rs->esc_state = IS_CSI;
141
-     break;
142
+ break;
143
default:
144
if (ch >= 32) {
145
readline_insert_char(rs, ch);
146
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
147
break;
148
case IS_CSI:
149
switch (ch) {
150
-    case 'A':
151
-    case 'F':
152
-     readline_up_char(rs);
153
-     break;
154
-    case 'B':
155
-    case 'E':
156
-     readline_down_char(rs);
157
-     break;
158
+ case 'A':
159
+ case 'F':
160
+ readline_up_char(rs);
161
+ break;
162
+ case 'B':
163
+ case 'E':
164
+ readline_down_char(rs);
165
+ break;
166
case 'D':
167
readline_backward_char(rs);
168
break;
169
--
219
--
170
2.21.0
220
2.35.1
171
172
diff view generated by jsdifflib
Deleted patch
1
From: Jules Irenge <jbi.octave@gmail.com>
2
1
3
Add braces to fix errors issued by checkpatch.pl tool
4
"ERROR: braces {} are necessary for all arms of this statement"
5
Within "util/readline.c" file
6
Message-Id: <20190330112142.14082-1-jbi.octave@gmail.com>
7
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
util/readline.c | 50 ++++++++++++++++++++++++++++++++-----------------
11
1 file changed, 33 insertions(+), 17 deletions(-)
12
13
diff --git a/util/readline.c b/util/readline.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/util/readline.c
16
+++ b/util/readline.c
17
@@ -XXX,XX +XXX,XX @@ static void readline_update(ReadLineState *rs)
18
rs->cmd_buf[rs->cmd_buf_size] = '\0';
19
if (rs->read_password) {
20
len = strlen(rs->cmd_buf);
21
- for (i = 0; i < len; i++)
22
+ for (i = 0; i < len; i++) {
23
rs->printf_func(rs->opaque, "*");
24
+ }
25
} else {
26
rs->printf_func(rs->opaque, "%s", rs->cmd_buf);
27
}
28
@@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs)
29
{
30
int idx;
31
32
- if (rs->hist_entry == 0)
33
+ if (rs->hist_entry == 0) {
34
return;
35
+ }
36
if (rs->hist_entry == -1) {
37
/* Find latest entry */
38
for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
39
- if (rs->history[idx] == NULL)
40
+ if (rs->history[idx] == NULL) {
41
break;
42
+ }
43
}
44
rs->hist_entry = idx;
45
}
46
@@ -XXX,XX +XXX,XX @@ static void readline_up_char(ReadLineState *rs)
47
48
static void readline_down_char(ReadLineState *rs)
49
{
50
- if (rs->hist_entry == -1)
51
+ if (rs->hist_entry == -1) {
52
return;
53
+ }
54
if (rs->hist_entry < READLINE_MAX_CMDS - 1 &&
55
rs->history[++rs->hist_entry] != NULL) {
56
pstrcpy(rs->cmd_buf, sizeof(rs->cmd_buf),
57
@@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline)
58
char *hist_entry, *new_entry;
59
int idx;
60
61
- if (cmdline[0] == '\0')
62
+ if (cmdline[0] == '\0') {
63
return;
64
+ }
65
new_entry = NULL;
66
if (rs->hist_entry != -1) {
67
/* We were editing an existing history entry: replace it */
68
@@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline)
69
/* Search cmdline in history buffers */
70
for (idx = 0; idx < READLINE_MAX_CMDS; idx++) {
71
hist_entry = rs->history[idx];
72
- if (hist_entry == NULL)
73
+ if (hist_entry == NULL) {
74
break;
75
+ }
76
if (strcmp(hist_entry, cmdline) == 0) {
77
same_entry:
78
new_entry = hist_entry;
79
@@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline)
80
(READLINE_MAX_CMDS - (idx + 1)) * sizeof(char *));
81
rs->history[READLINE_MAX_CMDS - 1] = NULL;
82
for (; idx < READLINE_MAX_CMDS; idx++) {
83
- if (rs->history[idx] == NULL)
84
+ if (rs->history[idx] == NULL) {
85
break;
86
+ }
87
}
88
break;
89
}
90
@@ -XXX,XX +XXX,XX @@ static void readline_hist_add(ReadLineState *rs, const char *cmdline)
91
rs->history[READLINE_MAX_CMDS - 1] = NULL;
92
idx = READLINE_MAX_CMDS - 1;
93
}
94
- if (new_entry == NULL)
95
+ if (new_entry == NULL) {
96
new_entry = g_strdup(cmdline);
97
+ }
98
rs->history[idx] = new_entry;
99
rs->hist_entry = -1;
100
}
101
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
102
g_free(cmdline);
103
104
/* no completion found */
105
- if (rs->nb_completions <= 0)
106
+ if (rs->nb_completions <= 0) {
107
return;
108
+ }
109
if (rs->nb_completions == 1) {
110
len = strlen(rs->completions[0]);
111
for (i = rs->completion_index; i < len; i++) {
112
readline_insert_char(rs, rs->completions[0][i]);
113
}
114
/* extra space for next argument. XXX: make it more generic */
115
- if (len > 0 && rs->completions[0][len - 1] != '/')
116
+ if (len > 0 && rs->completions[0][len - 1] != '/') {
117
readline_insert_char(rs, ' ');
118
+ }
119
} else {
120
qsort(rs->completions, rs->nb_completions, sizeof(char *),
121
completion_comp);
122
@@ -XXX,XX +XXX,XX @@ static void readline_completion(ReadLineState *rs)
123
if (i == 0) {
124
max_prefix = len;
125
} else {
126
- if (len < max_prefix)
127
+ if (len < max_prefix) {
128
max_prefix = len;
129
+ }
130
for (j = 0; j < max_prefix; j++) {
131
- if (rs->completions[i][j] != rs->completions[0][j])
132
+ if (rs->completions[i][j] != rs->completions[0][j]) {
133
max_prefix = j;
134
+ }
135
}
136
}
137
- if (len > max_width)
138
+ if (len > max_width) {
139
max_width = len;
140
+ }
141
}
142
if (max_prefix > 0)
143
for (i = rs->completion_index; i < max_prefix; i++) {
144
readline_insert_char(rs, rs->completions[0][i]);
145
}
146
max_width += 2;
147
- if (max_width < 10)
148
+ if (max_width < 10) {
149
max_width = 10;
150
- else if (max_width > 80)
151
+ } else if (max_width > 80) {
152
max_width = 80;
153
+ }
154
nb_cols = 80 / max_width;
155
j = 0;
156
for (i = 0; i < rs->nb_completions; i++) {
157
@@ -XXX,XX +XXX,XX @@ void readline_handle_byte(ReadLineState *rs, int ch)
158
case 10:
159
case 13:
160
rs->cmd_buf[rs->cmd_buf_size] = '\0';
161
- if (!rs->read_password)
162
+ if (!rs->read_password) {
163
readline_hist_add(rs, rs->cmd_buf);
164
+ }
165
rs->printf_func(rs->opaque, "\n");
166
rs->cmd_buf_index = 0;
167
rs->cmd_buf_size = 0;
168
@@ -XXX,XX +XXX,XX @@ void readline_restart(ReadLineState *rs)
169
170
const char *readline_get_history(ReadLineState *rs, unsigned int index)
171
{
172
- if (index >= READLINE_MAX_CMDS)
173
+ if (index >= READLINE_MAX_CMDS) {
174
return NULL;
175
+ }
176
return rs->history[index];
177
}
178
179
--
180
2.21.0
181
182
diff view generated by jsdifflib
Deleted patch
1
From: Nikita Alekseev <n.alekseev2104@gmail.com>
2
1
3
bdrv_check_co_entry calls bdrv_co_check, which is a coroutine function.
4
Thus, it also needs to be marked as a coroutine.
5
6
Signed-off-by: Nikita Alekseev <n.alekseev2104@gmail.com>
7
Message-id: 20190401093051.16488-1-n.alekseev2104@gmail.com
8
Message-Id: <20190401093051.16488-1-n.alekseev2104@gmail.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
block.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
14
diff --git a/block.c b/block.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/block.c
17
+++ b/block.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct CheckCo {
19
int ret;
20
} CheckCo;
21
22
-static void bdrv_check_co_entry(void *opaque)
23
+static void coroutine_fn bdrv_check_co_entry(void *opaque)
24
{
25
CheckCo *cco = opaque;
26
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
27
--
28
2.21.0
29
30
diff view generated by jsdifflib
Deleted patch
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
1
3
On a file system used by the customer, fallocate() returns an error
4
if the block is not properly aligned. So, bdrv_co_pwrite_zeroes()
5
fails. We can handle that case the same way as it is done for the
6
unsupported cases, namely, call to bdrv_driver_pwritev() that writes
7
zeroes to an image for the unaligned chunk of the block.
8
9
Suggested-by: Denis V. Lunev <den@openvz.org>
10
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
11
Reviewed-by: John Snow <jsnow@redhat.com>
12
Message-id: 1554474244-553661-1-git-send-email-andrey.shinkevich@virtuozzo.com
13
Message-Id: <1554474244-553661-1-git-send-email-andrey.shinkevich@virtuozzo.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
---
16
block/io.c | 2 +-
17
1 file changed, 1 insertion(+), 1 deletion(-)
18
19
diff --git a/block/io.c b/block/io.c
20
index XXXXXXX..XXXXXXX 100644
21
--- a/block/io.c
22
+++ b/block/io.c
23
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
24
assert(!bs->supported_zero_flags);
25
}
26
27
- if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) {
28
+ if (ret < 0 && !(flags & BDRV_REQ_NO_FALLBACK)) {
29
/* Fall back to bounce buffer if write zeroes is unsupported */
30
BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE;
31
32
--
33
2.21.0
34
35
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: Nicolas Saenz Julienne <nsaenzju@redhat.com>
2
2
3
With aio=thread, adaptive polling makes latency worse rather than
3
The thread pool regulates itself: when idle, it kills threads until
4
better, because it delays the execution of the ThreadPool's
4
empty, when in demand, it creates new threads until full. This behaviour
5
completion bottom half.
5
doesn't play well with latency sensitive workloads where the price of
6
creating a new thread is too high. For example, when paired with qemu's
7
'-mlock', or using safety features like SafeStack, creating a new thread
8
has been measured take multiple milliseconds.
6
9
7
event_notifier_poll() does run while polling, detecting that
10
In order to mitigate this let's introduce a new 'EventLoopBase'
8
a bottom half was scheduled by a worker thread, but because
11
property to set the thread pool size. The threads will be created during
9
ctx->notifier is explicitly ignored in run_poll_handlers_once(),
12
the pool's initialization or upon updating the property's value, remain
10
scheduling the BH does not count as making progress and
13
available during its lifetime regardless of demand, and destroyed upon
11
run_poll_handlers() keeps running. Fix this by recomputing
14
freeing it. A properly characterized workload will then be able to
12
the deadline after *timeout could have changed.
15
configure the pool to avoid any latency spikes.
13
16
14
With this change, ThreadPool still cannot participate in polling
17
Signed-off-by: Nicolas Saenz Julienne <nsaenzju@redhat.com>
15
but at least it does not suffer from extra latency.
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
19
Acked-by: Markus Armbruster <armbru@redhat.com>
17
Reported-by: Sergio Lopez <slp@redhat.com>
20
Message-id: 20220425075723.20019-4-nsaenzju@redhat.com
18
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
19
Message-id: 20190409122823.12416-1-pbonzini@redhat.com
20
Cc: Stefan Hajnoczi <stefanha@gmail.com>
21
Cc: Kevin Wolf <kwolf@redhat.com>
22
Cc: qemu-block@nongnu.org
23
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
24
Message-Id: <1553692145-86728-1-git-send-email-pbonzini@redhat.com>
25
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
26
Message-Id: <20190409122823.12416-1-pbonzini@redhat.com>
27
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
28
---
22
---
29
util/aio-posix.c | 12 ++++++++----
23
qapi/qom.json | 10 +++++-
30
1 file changed, 8 insertions(+), 4 deletions(-)
24
include/block/aio.h | 10 ++++++
25
include/block/thread-pool.h | 3 ++
26
include/sysemu/event-loop-base.h | 4 +++
27
event-loop-base.c | 23 +++++++++++++
28
iothread.c | 3 ++
29
util/aio-posix.c | 1 +
30
util/async.c | 20 ++++++++++++
31
util/main-loop.c | 9 ++++++
32
util/thread-pool.c | 55 +++++++++++++++++++++++++++++---
33
10 files changed, 133 insertions(+), 5 deletions(-)
31
34
35
diff --git a/qapi/qom.json b/qapi/qom.json
36
index XXXXXXX..XXXXXXX 100644
37
--- a/qapi/qom.json
38
+++ b/qapi/qom.json
39
@@ -XXX,XX +XXX,XX @@
40
# 0 means that the engine will use its default.
41
# (default: 0)
42
#
43
+# @thread-pool-min: minimum number of threads reserved in the thread pool
44
+# (default:0)
45
+#
46
+# @thread-pool-max: maximum number of threads the thread pool can contain
47
+# (default:64)
48
+#
49
# Since: 7.1
50
##
51
{ 'struct': 'EventLoopBaseProperties',
52
- 'data': { '*aio-max-batch': 'int' } }
53
+ 'data': { '*aio-max-batch': 'int',
54
+ '*thread-pool-min': 'int',
55
+ '*thread-pool-max': 'int' } }
56
57
##
58
# @IothreadProperties:
59
diff --git a/include/block/aio.h b/include/block/aio.h
60
index XXXXXXX..XXXXXXX 100644
61
--- a/include/block/aio.h
62
+++ b/include/block/aio.h
63
@@ -XXX,XX +XXX,XX @@ struct AioContext {
64
QSLIST_HEAD(, Coroutine) scheduled_coroutines;
65
QEMUBH *co_schedule_bh;
66
67
+ int thread_pool_min;
68
+ int thread_pool_max;
69
/* Thread pool for performing work and receiving completion callbacks.
70
* Has its own locking.
71
*/
72
@@ -XXX,XX +XXX,XX @@ void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns,
73
void aio_context_set_aio_params(AioContext *ctx, int64_t max_batch,
74
Error **errp);
75
76
+/**
77
+ * aio_context_set_thread_pool_params:
78
+ * @ctx: the aio context
79
+ * @min: min number of threads to have readily available in the thread pool
80
+ * @min: max number of threads the thread pool can contain
81
+ */
82
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
83
+ int64_t max, Error **errp);
84
#endif
85
diff --git a/include/block/thread-pool.h b/include/block/thread-pool.h
86
index XXXXXXX..XXXXXXX 100644
87
--- a/include/block/thread-pool.h
88
+++ b/include/block/thread-pool.h
89
@@ -XXX,XX +XXX,XX @@
90
91
#include "block/block.h"
92
93
+#define THREAD_POOL_MAX_THREADS_DEFAULT 64
94
+
95
typedef int ThreadPoolFunc(void *opaque);
96
97
typedef struct ThreadPool ThreadPool;
98
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *thread_pool_submit_aio(ThreadPool *pool,
99
int coroutine_fn thread_pool_submit_co(ThreadPool *pool,
100
ThreadPoolFunc *func, void *arg);
101
void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg);
102
+void thread_pool_update_params(ThreadPool *pool, struct AioContext *ctx);
103
104
#endif
105
diff --git a/include/sysemu/event-loop-base.h b/include/sysemu/event-loop-base.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/sysemu/event-loop-base.h
108
+++ b/include/sysemu/event-loop-base.h
109
@@ -XXX,XX +XXX,XX @@ struct EventLoopBase {
110
111
/* AioContext AIO engine parameters */
112
int64_t aio_max_batch;
113
+
114
+ /* AioContext thread pool parameters */
115
+ int64_t thread_pool_min;
116
+ int64_t thread_pool_max;
117
};
118
#endif
119
diff --git a/event-loop-base.c b/event-loop-base.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/event-loop-base.c
122
+++ b/event-loop-base.c
123
@@ -XXX,XX +XXX,XX @@
124
#include "qemu/osdep.h"
125
#include "qom/object_interfaces.h"
126
#include "qapi/error.h"
127
+#include "block/thread-pool.h"
128
#include "sysemu/event-loop-base.h"
129
130
typedef struct {
131
@@ -XXX,XX +XXX,XX @@ typedef struct {
132
ptrdiff_t offset; /* field's byte offset in EventLoopBase struct */
133
} EventLoopBaseParamInfo;
134
135
+static void event_loop_base_instance_init(Object *obj)
136
+{
137
+ EventLoopBase *base = EVENT_LOOP_BASE(obj);
138
+
139
+ base->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
140
+}
141
+
142
static EventLoopBaseParamInfo aio_max_batch_info = {
143
"aio-max-batch", offsetof(EventLoopBase, aio_max_batch),
144
};
145
+static EventLoopBaseParamInfo thread_pool_min_info = {
146
+ "thread-pool-min", offsetof(EventLoopBase, thread_pool_min),
147
+};
148
+static EventLoopBaseParamInfo thread_pool_max_info = {
149
+ "thread-pool-max", offsetof(EventLoopBase, thread_pool_max),
150
+};
151
152
static void event_loop_base_get_param(Object *obj, Visitor *v,
153
const char *name, void *opaque, Error **errp)
154
@@ -XXX,XX +XXX,XX @@ static void event_loop_base_class_init(ObjectClass *klass, void *class_data)
155
event_loop_base_get_param,
156
event_loop_base_set_param,
157
NULL, &aio_max_batch_info);
158
+ object_class_property_add(klass, "thread-pool-min", "int",
159
+ event_loop_base_get_param,
160
+ event_loop_base_set_param,
161
+ NULL, &thread_pool_min_info);
162
+ object_class_property_add(klass, "thread-pool-max", "int",
163
+ event_loop_base_get_param,
164
+ event_loop_base_set_param,
165
+ NULL, &thread_pool_max_info);
166
}
167
168
static const TypeInfo event_loop_base_info = {
169
.name = TYPE_EVENT_LOOP_BASE,
170
.parent = TYPE_OBJECT,
171
.instance_size = sizeof(EventLoopBase),
172
+ .instance_init = event_loop_base_instance_init,
173
.class_size = sizeof(EventLoopBaseClass),
174
.class_init = event_loop_base_class_init,
175
.abstract = true,
176
diff --git a/iothread.c b/iothread.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/iothread.c
179
+++ b/iothread.c
180
@@ -XXX,XX +XXX,XX @@ static void iothread_set_aio_context_params(EventLoopBase *base, Error **errp)
181
aio_context_set_aio_params(iothread->ctx,
182
iothread->parent_obj.aio_max_batch,
183
errp);
184
+
185
+ aio_context_set_thread_pool_params(iothread->ctx, base->thread_pool_min,
186
+ base->thread_pool_max, errp);
187
}
188
189
32
diff --git a/util/aio-posix.c b/util/aio-posix.c
190
diff --git a/util/aio-posix.c b/util/aio-posix.c
33
index XXXXXXX..XXXXXXX 100644
191
index XXXXXXX..XXXXXXX 100644
34
--- a/util/aio-posix.c
192
--- a/util/aio-posix.c
35
+++ b/util/aio-posix.c
193
+++ b/util/aio-posix.c
36
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout)
194
@@ -XXX,XX +XXX,XX @@
37
if (!node->deleted && node->io_poll &&
195
38
aio_node_check(ctx, node->is_external) &&
196
#include "qemu/osdep.h"
39
node->io_poll(node->opaque)) {
197
#include "block/block.h"
40
+ /*
198
+#include "block/thread-pool.h"
41
+ * Polling was successful, exit try_poll_mode immediately
199
#include "qemu/main-loop.h"
42
+ * to adjust the next polling time.
200
#include "qemu/rcu.h"
43
+ */
201
#include "qemu/rcu_queue.h"
44
*timeout = 0;
202
diff --git a/util/async.c b/util/async.c
45
if (node->opaque != &ctx->notifier) {
203
index XXXXXXX..XXXXXXX 100644
46
progress = true;
204
--- a/util/async.c
47
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout)
205
+++ b/util/async.c
48
do {
206
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
49
progress = run_poll_handlers_once(ctx, timeout);
207
50
elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time;
208
ctx->aio_max_batch = 0;
51
- } while (!progress && elapsed_time < max_ns
209
52
- && !atomic_read(&ctx->poll_disable_cnt));
210
+ ctx->thread_pool_min = 0;
53
+ max_ns = qemu_soonest_timeout(*timeout, max_ns);
211
+ ctx->thread_pool_max = THREAD_POOL_MAX_THREADS_DEFAULT;
54
+ assert(!(max_ns && progress));
212
+
55
+ } while (elapsed_time < max_ns && !atomic_read(&ctx->poll_disable_cnt));
213
return ctx;
56
214
fail:
57
/* If time has passed with no successful polling, adjust *timeout to
215
g_source_destroy(&ctx->source);
58
* keep the same ending time.
216
@@ -XXX,XX +XXX,XX @@ void qemu_set_current_aio_context(AioContext *ctx)
59
@@ -XXX,XX +XXX,XX @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout)
217
assert(!get_my_aiocontext());
60
*/
218
set_my_aiocontext(ctx);
61
static bool try_poll_mode(AioContext *ctx, int64_t *timeout)
219
}
220
+
221
+void aio_context_set_thread_pool_params(AioContext *ctx, int64_t min,
222
+ int64_t max, Error **errp)
223
+{
224
+
225
+ if (min > max || !max || min > INT_MAX || max > INT_MAX) {
226
+ error_setg(errp, "bad thread-pool-min/thread-pool-max values");
227
+ return;
228
+ }
229
+
230
+ ctx->thread_pool_min = min;
231
+ ctx->thread_pool_max = max;
232
+
233
+ if (ctx->thread_pool) {
234
+ thread_pool_update_params(ctx->thread_pool, ctx);
235
+ }
236
+}
237
diff --git a/util/main-loop.c b/util/main-loop.c
238
index XXXXXXX..XXXXXXX 100644
239
--- a/util/main-loop.c
240
+++ b/util/main-loop.c
241
@@ -XXX,XX +XXX,XX @@
242
#include "sysemu/replay.h"
243
#include "qemu/main-loop.h"
244
#include "block/aio.h"
245
+#include "block/thread-pool.h"
246
#include "qemu/error-report.h"
247
#include "qemu/queue.h"
248
#include "qemu/compiler.h"
249
@@ -XXX,XX +XXX,XX @@ int qemu_init_main_loop(Error **errp)
250
251
static void main_loop_update_params(EventLoopBase *base, Error **errp)
62
{
252
{
63
- /* See qemu_soonest_timeout() uint64_t hack */
253
+ ERRP_GUARD();
64
- int64_t max_ns = MIN((uint64_t)*timeout, (uint64_t)ctx->poll_ns);
254
+
65
+ int64_t max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns);
255
if (!qemu_aio_context) {
66
256
error_setg(errp, "qemu aio context not ready");
67
if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) {
257
return;
68
poll_set_started(ctx, true);
258
}
259
260
aio_context_set_aio_params(qemu_aio_context, base->aio_max_batch, errp);
261
+ if (*errp) {
262
+ return;
263
+ }
264
+
265
+ aio_context_set_thread_pool_params(qemu_aio_context, base->thread_pool_min,
266
+ base->thread_pool_max, errp);
267
}
268
269
MainLoop *mloop;
270
diff --git a/util/thread-pool.c b/util/thread-pool.c
271
index XXXXXXX..XXXXXXX 100644
272
--- a/util/thread-pool.c
273
+++ b/util/thread-pool.c
274
@@ -XXX,XX +XXX,XX @@ struct ThreadPool {
275
QemuMutex lock;
276
QemuCond worker_stopped;
277
QemuSemaphore sem;
278
- int max_threads;
279
QEMUBH *new_thread_bh;
280
281
/* The following variables are only accessed from one AioContext. */
282
@@ -XXX,XX +XXX,XX @@ struct ThreadPool {
283
int new_threads; /* backlog of threads we need to create */
284
int pending_threads; /* threads created but not running yet */
285
bool stopping;
286
+ int min_threads;
287
+ int max_threads;
288
};
289
290
+static inline bool back_to_sleep(ThreadPool *pool, int ret)
291
+{
292
+ /*
293
+ * The semaphore timed out, we should exit the loop except when:
294
+ * - There is work to do, we raced with the signal.
295
+ * - The max threads threshold just changed, we raced with the signal.
296
+ * - The thread pool forces a minimum number of readily available threads.
297
+ */
298
+ if (ret == -1 && (!QTAILQ_EMPTY(&pool->request_list) ||
299
+ pool->cur_threads > pool->max_threads ||
300
+ pool->cur_threads <= pool->min_threads)) {
301
+ return true;
302
+ }
303
+
304
+ return false;
305
+}
306
+
307
static void *worker_thread(void *opaque)
308
{
309
ThreadPool *pool = opaque;
310
@@ -XXX,XX +XXX,XX @@ static void *worker_thread(void *opaque)
311
ret = qemu_sem_timedwait(&pool->sem, 10000);
312
qemu_mutex_lock(&pool->lock);
313
pool->idle_threads--;
314
- } while (ret == -1 && !QTAILQ_EMPTY(&pool->request_list));
315
- if (ret == -1 || pool->stopping) {
316
+ } while (back_to_sleep(pool, ret));
317
+ if (ret == -1 || pool->stopping ||
318
+ pool->cur_threads > pool->max_threads) {
319
break;
320
}
321
322
@@ -XXX,XX +XXX,XX @@ void thread_pool_submit(ThreadPool *pool, ThreadPoolFunc *func, void *arg)
323
thread_pool_submit_aio(pool, func, arg, NULL, NULL);
324
}
325
326
+void thread_pool_update_params(ThreadPool *pool, AioContext *ctx)
327
+{
328
+ qemu_mutex_lock(&pool->lock);
329
+
330
+ pool->min_threads = ctx->thread_pool_min;
331
+ pool->max_threads = ctx->thread_pool_max;
332
+
333
+ /*
334
+ * We either have to:
335
+ * - Increase the number available of threads until over the min_threads
336
+ * threshold.
337
+ * - Decrease the number of available threads until under the max_threads
338
+ * threshold.
339
+ * - Do nothing. The current number of threads fall in between the min and
340
+ * max thresholds. We'll let the pool manage itself.
341
+ */
342
+ for (int i = pool->cur_threads; i < pool->min_threads; i++) {
343
+ spawn_thread(pool);
344
+ }
345
+
346
+ for (int i = pool->cur_threads; i > pool->max_threads; i--) {
347
+ qemu_sem_post(&pool->sem);
348
+ }
349
+
350
+ qemu_mutex_unlock(&pool->lock);
351
+}
352
+
353
static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
354
{
355
if (!ctx) {
356
@@ -XXX,XX +XXX,XX @@ static void thread_pool_init_one(ThreadPool *pool, AioContext *ctx)
357
qemu_mutex_init(&pool->lock);
358
qemu_cond_init(&pool->worker_stopped);
359
qemu_sem_init(&pool->sem, 0);
360
- pool->max_threads = 64;
361
pool->new_thread_bh = aio_bh_new(ctx, spawn_thread_bh_fn, pool);
362
363
QLIST_INIT(&pool->head);
364
QTAILQ_INIT(&pool->request_list);
365
+
366
+ thread_pool_update_params(pool, ctx);
367
}
368
369
ThreadPool *thread_pool_new(AioContext *ctx)
69
--
370
--
70
2.21.0
371
2.35.1
71
72
diff view generated by jsdifflib
Deleted patch
1
At KVM Forum 2018 I gave a presentation on security in QEMU:
2
https://www.youtube.com/watch?v=YAdRf_hwxU8 (video)
3
https://vmsplice.net/~stefan/stefanha-kvm-forum-2018.pdf (slides)
4
1
5
This patch adds a guide to secure coding practices. This document
6
covers things that developers should know about security in QEMU. It is
7
just a starting point that we can expand on later. I hope it will be
8
useful as a resource for new contributors and will save code reviewers
9
from explaining the same concepts many times.
10
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
13
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
14
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
15
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
16
Reviewed-by: Li Qiang <liq3ea@gmail.com>
17
Message-id: 20190509121820.16294-2-stefanha@redhat.com
18
Message-Id: <20190509121820.16294-2-stefanha@redhat.com>
19
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
20
---
21
docs/devel/index.rst | 1 +
22
docs/devel/secure-coding-practices.rst | 106 +++++++++++++++++++++++++
23
2 files changed, 107 insertions(+)
24
create mode 100644 docs/devel/secure-coding-practices.rst
25
26
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
27
index XXXXXXX..XXXXXXX 100644
28
--- a/docs/devel/index.rst
29
+++ b/docs/devel/index.rst
30
@@ -XXX,XX +XXX,XX @@ Contents:
31
stable-process
32
testing
33
decodetree
34
+ secure-coding-practices
35
diff --git a/docs/devel/secure-coding-practices.rst b/docs/devel/secure-coding-practices.rst
36
new file mode 100644
37
index XXXXXXX..XXXXXXX
38
--- /dev/null
39
+++ b/docs/devel/secure-coding-practices.rst
40
@@ -XXX,XX +XXX,XX @@
41
+=======================
42
+Secure Coding Practices
43
+=======================
44
+This document covers topics that both developers and security researchers must
45
+be aware of so that they can develop safe code and audit existing code
46
+properly.
47
+
48
+Reporting Security Bugs
49
+-----------------------
50
+For details on how to report security bugs or ask questions about potential
51
+security bugs, see the `Security Process wiki page
52
+<https://wiki.qemu.org/SecurityProcess>`_.
53
+
54
+General Secure C Coding Practices
55
+---------------------------------
56
+Most CVEs (security bugs) reported against QEMU are not specific to
57
+virtualization or emulation. They are simply C programming bugs. Therefore
58
+it's critical to be aware of common classes of security bugs.
59
+
60
+There is a wide selection of resources available covering secure C coding. For
61
+example, the `CERT C Coding Standard
62
+<https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard>`_
63
+covers the most important classes of security bugs.
64
+
65
+Instead of describing them in detail here, only the names of the most important
66
+classes of security bugs are mentioned:
67
+
68
+* Buffer overflows
69
+* Use-after-free and double-free
70
+* Integer overflows
71
+* Format string vulnerabilities
72
+
73
+Some of these classes of bugs can be detected by analyzers. Static analysis is
74
+performed regularly by Coverity and the most obvious of these bugs are even
75
+reported by compilers. Dynamic analysis is possible with valgrind, tsan, and
76
+asan.
77
+
78
+Input Validation
79
+----------------
80
+Inputs from the guest or external sources (e.g. network, files) cannot be
81
+trusted and may be invalid. Inputs must be checked before using them in a way
82
+that could crash the program, expose host memory to the guest, or otherwise be
83
+exploitable by an attacker.
84
+
85
+The most sensitive attack surface is device emulation. All hardware register
86
+accesses and data read from guest memory must be validated. A typical example
87
+is a device that contains multiple units that are selectable by the guest via
88
+an index register::
89
+
90
+ typedef struct {
91
+ ProcessingUnit unit[2];
92
+ ...
93
+ } MyDeviceState;
94
+
95
+ static void mydev_writel(void *opaque, uint32_t addr, uint32_t val)
96
+ {
97
+ MyDeviceState *mydev = opaque;
98
+ ProcessingUnit *unit;
99
+
100
+ switch (addr) {
101
+ case MYDEV_SELECT_UNIT:
102
+ unit = &mydev->unit[val]; <-- this input wasn't validated!
103
+ ...
104
+ }
105
+ }
106
+
107
+If ``val`` is not in range [0, 1] then an out-of-bounds memory access will take
108
+place when ``unit`` is dereferenced. The code must check that ``val`` is 0 or
109
+1 and handle the case where it is invalid.
110
+
111
+Unexpected Device Accesses
112
+--------------------------
113
+The guest may access device registers in unusual orders or at unexpected
114
+moments. Device emulation code must not assume that the guest follows the
115
+typical "theory of operation" presented in driver writer manuals. The guest
116
+may make nonsense accesses to device registers such as starting operations
117
+before the device has been fully initialized.
118
+
119
+A related issue is that device emulation code must be prepared for unexpected
120
+device register accesses while asynchronous operations are in progress. A
121
+well-behaved guest might wait for a completion interrupt before accessing
122
+certain device registers. Device emulation code must handle the case where the
123
+guest overwrites registers or submits further requests before an ongoing
124
+request completes. Unexpected accesses must not cause memory corruption or
125
+leaks in QEMU.
126
+
127
+Invalid device register accesses can be reported with
128
+``qemu_log_mask(LOG_GUEST_ERROR, ...)``. The ``-d guest_errors`` command-line
129
+option enables these log messages.
130
+
131
+Live Migration
132
+--------------
133
+Device state can be saved to disk image files and shared with other users.
134
+Live migration code must validate inputs when loading device state so an
135
+attacker cannot gain control by crafting invalid device states. Device state
136
+is therefore considered untrusted even though it is typically generated by QEMU
137
+itself.
138
+
139
+Guest Memory Access Races
140
+-------------------------
141
+Guests with multiple vCPUs may modify guest RAM while device emulation code is
142
+running. Device emulation code must copy in descriptors and other guest RAM
143
+structures and only process the local copy. This prevents
144
+time-of-check-to-time-of-use (TOCTOU) race conditions that could cause QEMU to
145
+crash when a vCPU thread modifies guest RAM while device emulation is
146
+processing it.
147
--
148
2.21.0
149
150
diff view generated by jsdifflib