extents: Cap maximum reply length
A devious plugin can cause an 8-fold increase in the reply size in
relation to the request size, by alternating status every byte of the
request. In the worst case, this can cause the client to reject our
response and kill the connection because the response is too large.
What's more, it consumes a lot of memory to track that many extents.
Let's put an upper bound on the maximum number of extents we are
willing to return (1M extents is an 8M reply chunk).
Pre-patch, this can be used to demonstrate the problem:
$ nbdkit -f sh - <<\EOF
#!/bin/bash
size=$((9*1024*1024))
case $1 in
get_size) echo $size ;;
pread) dd iflag=skip_bytes,count_bytes skip=$4 count=$3 if=/dev/zero || exit 1 ;;
can_extents) ;;
extents)
# Unrealistic in real life, but works to provoke the bug. For a full 9M
# query, this produces ~100M for nbdkit to parse, and in turn tries to
# produce a 72M reply chunk if we don't cap extents.
for ((i=$4; i<$4+$3; i++)); do
echo $i 1 $((i&1))
done ;;
*) exit 2 ;;
esac
EOF
$ ~/libnbd/run sh/nbdsh
...
nbd> h.connect_tcp("localhost","10809")
nbd> def f(data,metacontext,offset,e):
... print ("entries:%d" % len(e))
...
nbd> h.block_status(9*1024*1024,0,0,f)
Traceback (most recent call last):
File "/usr/lib64/python3.7/code.py", line 90, in runcode
exec(code, self.locals)
File "<console>", line 1, in <module>
File "/home/eblake/libnbd/python/nbd.py", line 577, in block_status
return libnbdmod.block_status (self._o, count, offset, data, extent, flags)
RuntimeError: nbd_block_status: invalid server reply length
Signed-off-by: Eric Blake <eblake@redhat.com>