On Tue, Jul 07, 2020 at 06:36:42PM +0100, Richard W.M. Jones wrote:
+/* Get the file size. */
+static int64_t
+tar_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle)
+{
+ struct handle *h = handle;
+ return h->size;
+}
Interesting crash caused here. I'm going to document this on the
public mailing list so we can find it in future, but I do not offer a fix.
I noticed during testing that the filter segfaulted if you used
something like:
nbdkit --filter=tar --filter=xz file disk.tar.xz tar-entry=disk
and did a simple query of the export TWICE:
qemu-img info nbd:localhost:10809
qemu-img info nbd:localhost:10809
Notice what I'm saying here is that the first qemu-img command worked
and the second one caused nbdkit to segfault.
The segfault turned out to be this assertion failure:
263 assert (h->exportsize <= INT64_MAX); /* Guaranteed by negotiation phase */
with this stack trace:
#1 0x00007fd737780884 in abort () from /lib64/libc.so.6
#2 0x00007fd737780769 in __assert_fail_base.cold () from /lib64/libc.so.6
#3 0x00007fd73778ff86 in __assert_fail () from /lib64/libc.so.6
#4 0x00000000004057b0 in backend_valid_range (b=0x85dbf20, offset=512,
count=512) at backend.c:263
#5 0x0000000000406488 in backend_pread (b=0x85dbf20, buf=0x85ec730,
count=512, offset=512, flags=0, err=0x7fd736afaa78) at backend.c:478
#6 0x00007fd7372b3b94 in tar_pread (next_ops=0x424520 <next_ops>,
nxdata=0x85dbf20, handle=0x85a3130, buf=0x85ec730, count=512, offs=0,
flags=0, err=0x7fd736afaa78) at tar.c:315
#7 0x000000000040bdbc in filter_pread (b=0x85da790, handle=0x85a3130,
buf=0x85ec730, count=512, offset=0, flags=0, err=0x7fd736afaa78)
at filters.c:441
#8 0x0000000000406536 in backend_pread (b=0x85da790, buf=0x85ec730,
count=512, offset=0, flags=0, err=0x7fd736afaa78) at backend.c:483
#9 0x00000000004119ad in handle_request (cmd=0, flags=0, offset=0, count=512,
buf=0x85ec730, extents=0x0) at protocol.c:241
#10 0x0000000000412bf8 in protocol_recv_request_send_reply () at protocol.c:713
#11 0x0000000000408356 in handle_single_connection (sockin=7, sockout=7)
at connections.c:175
#12 0x000000000041820f in start_thread (datav=0x85a1fc0) at sockets.c:337
#13 0x00007fd73792f479 in start_thread () from /lib64/libpthread.so.0
#14 0x00007fd73785cb53 in clone () from /lib64/libc.so.6
The problem is that the xz backend’s exportsize is not cached from a
previous call to xz_get_size(), so when we come to try to validate the
range the assert fails.
The reason for this is tar_prepare only calls the underlying
next_ops->get_size() on the very first connection, because on
subsequent connections the offset/size has already been cached so we
don't need to do the work in calculate_offset_of_entry, and
calculate_offset_of_entry is what calls next_ops->get_size.
Also tar_get_size doesn't call next_ops->get_size because it doesn't
need the result, which would have been the second opportunity to cache
exportsize.
If neither of these things happen then the core server will crash.
I have fixed this in my copy of the tar filter by calling
next_ops->get_size in tar_get_size even though the result is not
needed.
I checked the other plugins and the suspicious ones were:
- ext2
- partition
- truncate
- xz
However I was not able to produce a test case that crashed any of
them.
Rich.
--
Richard Jones, Virtualization Group, Red Hat
http://people.redhat.com/~rjones
Read my programming and virtualization blog:
http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW