block: Guarantee that *file is set on bdrv_get_block_status()
We document that *file is valid if the return is not an error and includes BDRV_BLOCK_OFFSET_VALID, but forgot to obey this contract when a driver (such as blkdebug) lacks a callback. Messed up in commit 67a0fd2 (v2.6), when we added the file parameter. Enhance qemu-iotest 177 to cover this, using a sequence that would print garbage or even SEGV, because it was dererefencing through uninitialized memory. [The resulting test output shows that we have less-than-ideal block status from the blkdebug driver, but that's a separate fix coming up soon.] Setting *file on all paths that return BDRV_BLOCK_OFFSET_VALID is enough to fix the crash, but we can go one step further: always setting *file, even on error, means that a broken caller that blindly dereferences file without checking for error is now more likely to get a reliable SEGV instead of randomly acting on garbage, making it easier to diagnose such buggy callers. Adding an assertion that file is set where expected doesn't hurt either. CC: qemu-stable@nongnu.org Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
		
							parent
							
								
									64ebf55648
								
							
						
					
					
						commit
						81c219ac6c
					
				@ -1734,6 +1734,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
 | 
				
			|||||||
    int64_t n;
 | 
					    int64_t n;
 | 
				
			||||||
    int64_t ret, ret2;
 | 
					    int64_t ret, ret2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *file = NULL;
 | 
				
			||||||
    total_sectors = bdrv_nb_sectors(bs);
 | 
					    total_sectors = bdrv_nb_sectors(bs);
 | 
				
			||||||
    if (total_sectors < 0) {
 | 
					    if (total_sectors < 0) {
 | 
				
			||||||
        return total_sectors;
 | 
					        return total_sectors;
 | 
				
			||||||
@ -1757,11 +1758,11 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        if (bs->drv->protocol_name) {
 | 
					        if (bs->drv->protocol_name) {
 | 
				
			||||||
            ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
 | 
					            ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
 | 
				
			||||||
 | 
					            *file = bs;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    *file = NULL;
 | 
					 | 
				
			||||||
    bdrv_inc_in_flight(bs);
 | 
					    bdrv_inc_in_flight(bs);
 | 
				
			||||||
    ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
 | 
					    ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
 | 
				
			||||||
                                            file);
 | 
					                                            file);
 | 
				
			||||||
@ -1771,7 +1772,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ret & BDRV_BLOCK_RAW) {
 | 
					    if (ret & BDRV_BLOCK_RAW) {
 | 
				
			||||||
        assert(ret & BDRV_BLOCK_OFFSET_VALID);
 | 
					        assert(ret & BDRV_BLOCK_OFFSET_VALID && *file);
 | 
				
			||||||
        ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
 | 
					        ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
 | 
				
			||||||
                                       *pnum, pnum, file);
 | 
					                                       *pnum, pnum, file);
 | 
				
			||||||
        goto out;
 | 
					        goto out;
 | 
				
			||||||
 | 
				
			|||||||
@ -43,6 +43,7 @@ _supported_proto file
 | 
				
			|||||||
CLUSTER_SIZE=1M
 | 
					CLUSTER_SIZE=1M
 | 
				
			||||||
size=128M
 | 
					size=128M
 | 
				
			||||||
options=driver=blkdebug,image.driver=qcow2
 | 
					options=driver=blkdebug,image.driver=qcow2
 | 
				
			||||||
 | 
					nested_opts=image.file.driver=file,image.file.filename=$TEST_IMG
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo
 | 
					echo
 | 
				
			||||||
echo "== setting up files =="
 | 
					echo "== setting up files =="
 | 
				
			||||||
@ -106,6 +107,8 @@ function verify_io()
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
 | 
					verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
 | 
				
			||||||
 | 
					$QEMU_IMG map --image-opts "$options,$nested_opts,align=4k" \
 | 
				
			||||||
 | 
					    | _filter_qemu_img_map
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_check_test_img
 | 
					_check_test_img
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -45,5 +45,7 @@ read 30408704/30408704 bytes at offset 80740352
 | 
				
			|||||||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
					29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
				
			||||||
read 23068672/23068672 bytes at offset 111149056
 | 
					read 23068672/23068672 bytes at offset 111149056
 | 
				
			||||||
22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
					22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 | 
				
			||||||
 | 
					Offset          Length          File
 | 
				
			||||||
 | 
					0               0x8000000       json:{"image": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}}, "driver": "blkdebug", "align": "4k"}
 | 
				
			||||||
No errors were found on the image.
 | 
					No errors were found on the image.
 | 
				
			||||||
*** done
 | 
					*** done
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user