libbtrfsutil: use safe access to potentially unaligned data

There's a lot of places with unsafe access to data that come from a
search buffer, which is packed and the structures there are not
guaranteed to be aligned, also accessing the on-disk format structures.

- search header - this is an in-memory buffer with a series of on-disk
  structures, no alignment must be assumed
- anything that's not a byte buffer must be accessed as an unaligned
  buffer (the exceptions are name-like buffers)

Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
David Sterba 2024-05-25 02:05:37 +02:00
parent 9a01491042
commit 94e058d8b2

View file

@ -220,12 +220,12 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
header = (struct btrfs_ioctl_search_header *)search.buf;
ref = (struct btrfs_root_ref *)(header + 1);
name = (char *)(ref + 1);
name_len = le16_to_cpu(ref->name_len);
name_len = get_unaligned_le16(&ref->name_len);
id = header->offset;
id = btrfs_search_header_offset(header);
lookup.treeid = id;
lookup.objectid = le64_to_cpu(ref->dirid);
lookup.objectid = get_unaligned_le64(&ref->dirid);
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
if (ret == -1) {
free(path);
@ -268,27 +268,27 @@ PUBLIC enum btrfs_util_error btrfs_util_subvolume_path_fd(int fd, uint64_t id,
PUBLIC enum btrfs_util_error btrfs_util_subvolume_get_path_fd(int fd, uint64_t id, char **path_ret)
LIBBTRFSUTIL_ALIAS(btrfs_util_subvolume_path_fd);
/* The @timespec could be from a raw buffer, do not assume any alignment. */
static void copy_timespec(struct timespec *timespec,
const struct btrfs_timespec *btrfs_timespec)
{
timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec);
timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec);
timespec->tv_sec = get_unaligned_le64(&btrfs_timespec->sec);
timespec->tv_nsec = get_unaligned_le32(&btrfs_timespec->nsec);
}
/* The @root could be from a raw search buffer, do not assume any alignment. */
static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
const struct btrfs_root_item *root)
{
subvol->flags = le64_to_cpu(root->flags);
subvol->flags = get_unaligned_le64(&root->flags);
memcpy(subvol->uuid, root->uuid, sizeof(subvol->uuid));
memcpy(subvol->parent_uuid, root->parent_uuid,
sizeof(subvol->parent_uuid));
memcpy(subvol->received_uuid, root->received_uuid,
sizeof(subvol->received_uuid));
subvol->generation = le64_to_cpu(root->generation);
subvol->ctransid = le64_to_cpu(root->ctransid);
subvol->otransid = le64_to_cpu(root->otransid);
subvol->stransid = le64_to_cpu(root->stransid);
subvol->rtransid = le64_to_cpu(root->rtransid);
memcpy(subvol->parent_uuid, root->parent_uuid, sizeof(subvol->parent_uuid));
memcpy(subvol->received_uuid, root->received_uuid, sizeof(subvol->received_uuid));
subvol->generation = get_unaligned_le64(&root->generation);
subvol->ctransid = get_unaligned_le64(&root->ctransid);
subvol->otransid = get_unaligned_le64(&root->otransid);
subvol->stransid = get_unaligned_le64(&root->stransid);
subvol->rtransid = get_unaligned_le64(&root->rtransid);
copy_timespec(&subvol->ctime, &root->ctime);
copy_timespec(&subvol->otime, &root->otime);
copy_timespec(&subvol->stime, &root->stime);
@ -389,7 +389,7 @@ static enum btrfs_util_error get_subvolume_info_privileged(int fd, uint64_t id,
ref = (const struct btrfs_root_ref *)(header + 1);
subvol->parent_id = btrfs_search_header_offset(header);
subvol->dir_id = le64_to_cpu(ref->dirid);
subvol->dir_id = get_unaligned_le64(&ref->dirid);
}
need_root_backref = false;
search.key.min_type = UINT32_MAX;
@ -600,23 +600,23 @@ PUBLIC enum btrfs_util_error btrfs_util_get_default_subvolume_fd(int fd,
}
header = (struct btrfs_ioctl_search_header *)(search.buf + buf_off);
if (header->type == BTRFS_DIR_ITEM_KEY) {
if (btrfs_search_header_type(header) == BTRFS_DIR_ITEM_KEY) {
const struct btrfs_dir_item *dir;
const char *name;
uint16_t name_len;
dir = (struct btrfs_dir_item *)(header + 1);
name = (const char *)(dir + 1);
name_len = le16_to_cpu(dir->name_len);
name_len = get_unaligned_le16(&dir->name_len);
if (strncmp(name, "default", name_len) == 0) {
*id_ret = le64_to_cpu(dir->location.objectid);
*id_ret = get_unaligned_le64(&dir->location.objectid);
break;
}
}
items_pos++;
buf_off += sizeof(*header) + header->len;
search.key.min_offset = header->offset + 1;
buf_off += sizeof(*header) + btrfs_search_header_len(header);
search.key.min_offset = btrfs_search_header_offset(header) + 1;
}
return BTRFS_UTIL_OK;
@ -1442,6 +1442,9 @@ static enum btrfs_util_error build_subvol_path(struct btrfs_util_subvolume_itera
return BTRFS_UTIL_OK;
}
/*
* The @ref could be from raw search buffer, do not assume any alignment
*/
static enum btrfs_util_error build_subvol_path_privileged(struct btrfs_util_subvolume_iterator *iter,
const struct btrfs_ioctl_search_header *header,
const struct btrfs_root_ref *ref,
@ -1450,7 +1453,7 @@ static enum btrfs_util_error build_subvol_path_privileged(struct btrfs_util_subv
{
struct btrfs_ioctl_ino_lookup_args lookup = {
.treeid = btrfs_search_header_objectid(header),
.objectid = le64_to_cpu(ref->dirid),
.objectid = get_unaligned_le64(&ref->dirid),
};
int ret;
@ -1458,7 +1461,7 @@ static enum btrfs_util_error build_subvol_path_privileged(struct btrfs_util_subv
if (ret == -1)
return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED;
return build_subvol_path(iter, name, le16_to_cpu(ref->name_len),
return build_subvol_path(iter, name, get_unaligned_le16(&ref->name_len),
lookup.name, strlen(lookup.name),
path_len_ret);
}
@ -1761,7 +1764,7 @@ PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(int fd,
* The orphan item might be for a free space cache inode, so
* check if there's a matching root item.
*/
err = btrfs_util_subvolume_info_fd(fd, header->offset, &subvol);
err = btrfs_util_subvolume_info_fd(fd, btrfs_search_header_offset(header), &subvol);
if (!err) {
if (*n >= capacity) {
size_t new_capacity;
@ -1778,14 +1781,14 @@ PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(int fd,
*ids = new_ids;
capacity = new_capacity;
}
(*ids)[(*n)++] = header->offset;
(*ids)[(*n)++] = btrfs_search_header_offset(header);
} else if (err != BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
goto out;
}
items_pos++;
buf_off += sizeof(*header) + header->len;
search.key.min_offset = header->offset + 1;
buf_off += sizeof(*header) + btrfs_search_header_len(header);
search.key.min_offset = btrfs_search_header_offset(header) + 1;
}
err = BTRFS_UTIL_OK;