Page MenuHomePhabricator

Multiple issues with io_block.c:block_seek()
Open, Needs TriagePublic

Description

I've identified what I believe are a few problems with the block_seek() implementation in io_block.c:

/* parameter offset is relative address at here */
static int block_seek(io_entity_t *entity, int mode, signed long long offset)
{
	block_dev_state_t *cur;

	assert(entity->info != (uintptr_t)NULL);

	cur = (block_dev_state_t *)entity->info;
	assert((offset >= 0) && ((unsigned long long)offset < cur->size));

	switch (mode) {
	case IO_SEEK_SET:
		cur->file_pos = (unsigned long long)offset;
		break;
	case IO_SEEK_CUR:
		cur->file_pos += (unsigned long long)offset;
		break;
	default:
		return -EINVAL;
	}
	assert(cur->file_pos < cur->size);
	return 0;
}
  1. The code uses only assert() and includes no release-mode error checks. IOW it will always return 0 as long as mode is valid.
  1. The first assert() is only correct when mode == SEEK_SET, because the semantics of offset depend on the value of mode:
    • SEEK_SET: offset is a new file position and should be bound to 0 >= offset < size.
    • SEEK_CUR: offset is a relative position, and can be negative. It should also be limited to the distance to the beginning (negative) or end (positive) of the device.
  1. When mode == SEEK_CUR, offset is allowed to be negative (a backwards seek), so it should not be cast to unsigned.

I think it should look something like this:

/* parameter offset is relative address at here */
static int block_seek(io_entity_t *entity, int mode, signed long long offset)
{
	block_dev_state_t *cur;

	assert(entity->info != (uintptr_t)NULL);

	cur = (block_dev_state_t *)entity->info;

	switch (mode) {
	case IO_SEEK_SET:
                if ((offset < 0) || (unsigned long long)offset >= cur->size))
                        return -EINVAL;
		cur->file_pos = (unsigned long long)offset;
		break;
	case IO_SEEK_CUR:
                if ((cur->file_pos + offset < 0) || (cur->file_pos + offset >= cur->size))
                        return -EINVAL;
		cur->file_pos += offset;
		break;
	default:
		return -EINVAL;
	}
	assert(cur->file_pos < cur->size);
	return 0;
}

Event Timeline