hostmem: Validate host-nodes before setting bitmap
PATCH v2 1/1 hostmem: Validate host-nodes before setting bitmap
host_memory_backend_set_host_nodes() was not validating host-nodes before writing to backend->host_nodes, making QEMU write beyond the end of the bitmap.
Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by visit_type_uint16List().
---
Changes v1 -> v2:
* Remove extra l = l->next statement
(reported by Stefano Garzarella)
* Fix (existing) leak of host_nodes
(reported by Markus Armbruster)
--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.py
backends/hostmem.c +13 -4
@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } + +for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; } + +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif
tests/acceptance/host-nodes-limit.py new file +36 -0
@@ -0,0 +1,36 @@ +# Regression test for host-nodes limit validation +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost <[email protected]> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test +from subprocess import Popen, PIPE + +MAX_NODES = 128 + +class HostNodesValidation(Test): + def test_large_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'Invalid host-nodes', stderr) + self.assertEquals(stdout, b'') + self.assertEquals(p.returncode, 1) + + def test_valid_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES - 1)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'host-nodes must be empty', stderr) + self.assertEquals(p.returncode, 1)
Signature · 1 line
2.18.0.rc1.1.g3f1ff2140
Quoted 99 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Changes v1 -> v2:
* Remove extral = l->nextstatement
(reported by Stefano Garzarella)
* Fix (existing) leak ofhost_nodes
(reported by Markus Armbruster)--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.pybackends/hostmem.c +13
-4@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } + +for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; } + +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif tests/acceptance/host-nodes-limit.py new file +36
-1@@ -0,0 +1,36 @@ +# Regression test for host-nodes limit validation +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost <[email protected]> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test +from subprocess import Popen, PIPE + +MAX_NODES = 128 + +class HostNodesValidation(Test): + def test_large_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'Invalid host-nodes', stderr) + self.assertEquals(stdout, b'') + self.assertEquals(p.returncode, 1) + + def test_valid_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES - 1)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'host-nodes must be empty', stderr) + self.assertEquals(p.returncode, 1) --
Quoted 2 lines
2.18.0.rc1.1.g3f1ff2140
Signature · 2 lines
Stefano Garzarella Red Hat
Quoted 88 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Changes v1 -> v2:
* Remove extral = l->nextstatement
(reported by Stefano Garzarella)
* Fix (existing) leak ofhost_nodes
(reported by Markus Armbruster)--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.pybackends/hostmem.c +13
-4@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } + +for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; } + +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif tests/acceptance/host-nodes-limit.py new file +26
-0@@ -0,0 +1,36 @@ +# Regression test for host-nodes limit validation +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost <[email protected]> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test +from subprocess import Popen, PIPE + +MAX_NODES = 128 + +class HostNodesValidation(Test): + def test_large_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'Invalid host-nodes', stderr) + self.assertEquals(stdout, b'') + self.assertEquals(p.returncode, 1)
tests/acceptance/host-nodes-limit.py line 36
Quoted 10 lines
tests/acceptance/host-nodes-limit.py new file +10
-0+ + def test_valid_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES - 1)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'host-nodes must be empty', stderr) + self.assertEquals(p.returncode, 1)
Signature · 3 lines
Thanks, David / dhildenb
Quoted 45 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Changes v1 -> v2:
* Remove extral = l->nextstatement
(reported by Stefano Garzarella)
* Fix (existing) leak ofhost_nodes
(reported by Markus Armbruster)--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.pybackends/hostmem.c +9
-3@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } +
backends/hostmem.c host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, · line 125
Quoted 10 lines
backends/hostmem.c +4
-1+for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; } + +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif
Care to fix the leak in host_memory_backend_get_host_nodes(), too?
[...]
Quoted 51 lines · Markus Armbruster
Quoted 49 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Changes v1 -> v2:
* Remove extral = l->nextstatement
(reported by Stefano Garzarella)
* Fix (existing) leak ofhost_nodes
(reported by Markus Armbruster)--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.pybackends/hostmem.c +10
-4@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } + +for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; }
Quoted 8 lines
backends/hostmem.c host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, · line 125
Quoted 6 lines
backends/hostmem.c +3
-0+ +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif Care to fix the leak in
host_memory_backend_get_host_nodes(), too?
I don't understand yet if there's a leak at host_memory_backend_get_host_nodes(). Won't visit_type_uint16List() take ownership of the list on that case?
Quoted 4 lines
[...]
Reviewed-by Markus Armbruster <[email protected]>
Signature · 1 line
Eduardo
Quoted 3 lines · Eduardo Habkost
I don't understand yet if there's a leak at
host_memory_backend_get_host_nodes(). Won'tvisit_type_uint16List()take ownership of the list on that case?
Nope. I checked with valgrind:
$ valgrind --leak-check=full upstream-qemu -nodefaults -S -display none -qmp stdio -object memory-backend-file,id=mem0,mem-path=x,size=4096,host-nodes=1,policy=bind
[...]
{"QMP": {"version": {"qemu": {"micro": 92, "minor": 0, "major": 3}, "package": "v3.1.0-rc2-48-g039d4e3df0-dirty"}, "capabilities": []}}
{"execute": "qmp_capabilities"}
{"return": {}}
{ "execute": "qom-get", "arguments": { "path": "mem0", "property": "host-nodes" {"execute": "qom-get", "arguments": {"path": "mem0", "property": "host-nodes"}}
{"return": [1]}
{"execute": "quit"}
{"return": {}}
{"timestamp": {"seconds": 1543592652, "microseconds": 950994}, "event": "SHUTDOWN", "data": {"guest": false}}
==4954==
==4954== HEAP SUMMARY:
==4954== in use at exit: 3,631,673 bytes in 14,706 blocks
==4954== total heap usage: 51,347 allocs, 36,641 frees, 24,195,921 bytes allocated
[...]
==4954== 16 bytes in 1 blocks are definitely lost in loss record 1,964 of 5,297
==4954== at 0x4C3111A: calloc (vg_replace_malloc.c:752)
==4954== by 0x574948D: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.5600.3)
==4954== by 0x9E1CE0: opts_start_list (opts-visitor.c:228)
==4954== by 0x9DAB35: visit_start_list (qapi-visit-core.c:78)
==4954== by 0x99BA3A: visit_type_uint16List (qapi-builtin-visit.c:272)
==4954== by 0x5F911B: host_memory_backend_set_host_nodes (hostmem.c:108)
==4954== by 0x8AC7D4: object_property_set (object.c:1183)
==4954== by 0x8AFC82: user_creatable_add_type (object_interfaces.c:73)
==4954== by 0x8AFED2: user_creatable_add_opts (object_interfaces.c:131)
==4954== by 0x8AFFCD: user_creatable_add_opts_foreach (object_interfaces.c:154)
==4954== by 0xA0B9B9: qemu_opts_foreach (qemu-option.c:1171)
==4954== by 0x5C6C44: main (vl.c:4415)
==4954==
==4954== 16 bytes in 1 blocks are definitely lost in loss record 1,965 of 5,297
==4954== at 0x4C3111A: calloc (vg_replace_malloc.c:752)
==4954== by 0x574948D: g_malloc0 (in /usr/lib64/libglib-2.0.so.0.5600.3)
==4954== by 0x5F8FF5: host_memory_backend_get_host_nodes (hostmem.c:82)
==4954== by 0x8AC739: object_property_get (object.c:1168)
==4954== by 0x8AF910: object_property_get_qobject (qom-qobject.c:39)
==4954== by 0x5E1736: qmp_qom_get (qmp.c:249)
==4954== by 0x5D872F: qmp_marshal_qom_get (qapi-commands-misc.c:1284)
==4954== by 0x9DF5C1: do_qmp_dispatch (qmp-dispatch.c:129)
==4954== by 0x9DF788: qmp_dispatch (qmp-dispatch.c:171)
==4954== by 0x42C0C1: monitor_qmp_dispatch (monitor.c:4085)
==4954== by 0x42C3E1: monitor_qmp_bh_dispatcher (monitor.c:4157)
==4954== by 0x9EEDB1: aio_bh_call (async.c:90)
[...]
==4954== LEAK SUMMARY:
==4954== definitely lost: 32 bytes in 2 blocks
==4954== indirectly lost: 0 bytes in 0 blocks
==4954== possibly lost: 2,504 bytes in 20 blocks
==4954== still reachable: 3,629,137 bytes in 14,684 blocks
==4954== of which reachable via heuristic:
==4954== newarray : 1,536 bytes in 16 blocks
==4954== suppressed: 0 bytes in 0 blocks
==4954== Reachable blocks (those to which a pointer was found) are not shown.
==4954== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==4954==
==4954== For counts of detected and suppressed errors, rerun with: -v
==4954== Use --track-origins=yes to see where uninitialised values come from
==4954== ERROR SUMMARY: 24 errors from 24 contexts (suppressed: 0 from 0)The first block shown is leaked in host_memory_backend_set_host_nodes() on behalf of -object, the second block in host_memory_backend_get_host_nodes() on behalf of qom-get.
Full disclosure: I hacked host_memory_backend_complete() to skip mbind():
backends/hostmem.c +1 -1
@@ -333,7 +333,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)assert (sizeof (backend->host_nodes) >=BITS_TO_LONGS (MAX_NODES +1 ) *sizeof (unsigned long ));assert (maxnode <=MAX_NODES ); -if (mbind (ptr, sz, backend->policy, +if (0 &&mbind (ptr, sz, backend->policy, maxnode ? backend->host_nodes :NULL , maxnode +1 , flags)) {if (backend->policy !=MPOL_DEFAULT || errno !=ENOSYS ) { error_setg_errno(errp, errno,
Quoted 4 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.
Quoted 8 lines
Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Looks like we may have 3.1-rc4 due to some CVE fixes; is this worth including in 3.1 as well?
Signature · 3 lines
Eric Blake, Principal Software Engineer Red Hat, Inc. +1-919-301-3266 Virtualization: qemu.org | libvirt.org
Quoted 10 lines · Eric Blake
Quoted 9 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().
Quoted 6 lines
Quoted 3 lines
Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Looks like we may have 3.1-rc4 due to some CVE fixes; is this worth including in 3.1 as well?
You could conceivably crash a running VM with object-add. On the other hand, the bug has been around for a while, and was only found by code inspection.
Quoted 16 lines · Markus Armbruster
Quoted 14 lines · Eric Blake
Quoted 12 lines · Eduardo Habkost
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>---
Quoted 6 lines
Quoted 2 lines
Looks like we may have 3.1-rc4 due to some CVE fixes; is this worth including in 3.1 as well?
You could conceivably crash a running VM with object-add. On the other hand, the bug has been around for a while, and was only found by code inspection.
I think the fix would be appropriate for -rc2 or -rc3, but I don't think it's critical enough for -rc4.
Signature · 1 line
Eduardo
Quoted 11 lines · On Fri, 30 Nov 2018 10:28:44 -0200 Eduardo Habkost <[email protected]> wrote:
host_memory_backend_set_host_nodes()was not validating host-nodes before writing tobackend->host_nodes, making QEMU write beyond the end of the bitmap.Fix the crash and add a simple regression test for the fix.
While at it, fix memory leak of the list returned by
visit_type_uint16List().Reported-by Markus Armbruster <[email protected]>Signed-off-by Eduardo Habkost <[email protected]>
Quoted 86 lines
---
Changes v1 -> v2:
* Remove extral = l->nextstatement
(reported by Stefano Garzarella)
* Fix (existing) leak ofhost_nodes
(reported by Markus Armbruster)--- backends/hostmem.c | 17 +++++++++---- tests/acceptance/host-nodes-limit.py | 36 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 tests/acceptance/host-nodes-limit.pybackends/hostmem.c +13
-4@@ -103,14 +103,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, {#ifdef CONFIG_NUMA HostMemoryBackend *backend =MEMORY_BACKEND (obj); -uint16List *l =NULL ; +uint16List *l, *host_nodes =NULL ; -visit_type_uint16List (v, name, &l, errp); +visit_type_uint16List (v, name, &host_nodes, errp); -while (l) { +for (l = host_nodes; l; l = l->next) { +if (l->value >=MAX_NODES ) { +error_setg (errp,"Invalid host-nodes value: %d" , l->value); + goto out; + } + } + +for (l = host_nodes; l; l = l->next) {bitmap_set (backend->host_nodes, l->value,1 ); - l = l->next; } + +out: +qapi_free_uint16List (host_nodes);#else error_setg (errp,"NUMA node binding are not supported by this QEMU" );#endif tests/acceptance/host-nodes-limit.py new file +36
-0@@ -0,0 +1,36 @@ +# Regression test for host-nodes limit validation +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost <[email protected]> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test +from subprocess import Popen, PIPE + +MAX_NODES = 128 + +class HostNodesValidation(Test): + def test_large_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'Invalid host-nodes', stderr) + self.assertEquals(stdout, b'') + self.assertEquals(p.returncode, 1) + + def test_valid_host_nodes(self): + p = Popen([self.qemu_bin, '-display', 'none', '-nodefaults', + '-object', 'memory-backend-ram,id=m0,' + 'size=4096,host-nodes=%d' % (MAX_NODES - 1)], + stderr=PIPE, stdout=PIPE) + stdout,stderr = p.communicate() + + self.assertIn(b'host-nodes must be empty', stderr) + self.assertEquals(p.returncode, 1)