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; header = (struct btrfs_ioctl_search_header *)search.buf;
ref = (struct btrfs_root_ref *)(header + 1); ref = (struct btrfs_root_ref *)(header + 1);
name = (char *)(ref + 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.treeid = id;
lookup.objectid = le64_to_cpu(ref->dirid); lookup.objectid = get_unaligned_le64(&ref->dirid);
ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup); ret = ioctl(fd, BTRFS_IOC_INO_LOOKUP, &lookup);
if (ret == -1) { if (ret == -1) {
free(path); 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) 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); 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, static void copy_timespec(struct timespec *timespec,
const struct btrfs_timespec *btrfs_timespec) const struct btrfs_timespec *btrfs_timespec)
{ {
timespec->tv_sec = le64_to_cpu(btrfs_timespec->sec); timespec->tv_sec = get_unaligned_le64(&btrfs_timespec->sec);
timespec->tv_nsec = le32_to_cpu(btrfs_timespec->nsec); 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, static void copy_root_item(struct btrfs_util_subvolume_info *subvol,
const struct btrfs_root_item *root) 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->uuid, root->uuid, sizeof(subvol->uuid));
memcpy(subvol->parent_uuid, root->parent_uuid, memcpy(subvol->parent_uuid, root->parent_uuid, sizeof(subvol->parent_uuid));
sizeof(subvol->parent_uuid)); memcpy(subvol->received_uuid, root->received_uuid, sizeof(subvol->received_uuid));
memcpy(subvol->received_uuid, root->received_uuid, subvol->generation = get_unaligned_le64(&root->generation);
sizeof(subvol->received_uuid)); subvol->ctransid = get_unaligned_le64(&root->ctransid);
subvol->generation = le64_to_cpu(root->generation); subvol->otransid = get_unaligned_le64(&root->otransid);
subvol->ctransid = le64_to_cpu(root->ctransid); subvol->stransid = get_unaligned_le64(&root->stransid);
subvol->otransid = le64_to_cpu(root->otransid); subvol->rtransid = get_unaligned_le64(&root->rtransid);
subvol->stransid = le64_to_cpu(root->stransid);
subvol->rtransid = le64_to_cpu(root->rtransid);
copy_timespec(&subvol->ctime, &root->ctime); copy_timespec(&subvol->ctime, &root->ctime);
copy_timespec(&subvol->otime, &root->otime); copy_timespec(&subvol->otime, &root->otime);
copy_timespec(&subvol->stime, &root->stime); 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); ref = (const struct btrfs_root_ref *)(header + 1);
subvol->parent_id = btrfs_search_header_offset(header); 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; need_root_backref = false;
search.key.min_type = UINT32_MAX; 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); 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 struct btrfs_dir_item *dir;
const char *name; const char *name;
uint16_t name_len; uint16_t name_len;
dir = (struct btrfs_dir_item *)(header + 1); dir = (struct btrfs_dir_item *)(header + 1);
name = (const char *)(dir + 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) { if (strncmp(name, "default", name_len) == 0) {
*id_ret = le64_to_cpu(dir->location.objectid); *id_ret = get_unaligned_le64(&dir->location.objectid);
break; break;
} }
} }
items_pos++; items_pos++;
buf_off += sizeof(*header) + header->len; buf_off += sizeof(*header) + btrfs_search_header_len(header);
search.key.min_offset = header->offset + 1; search.key.min_offset = btrfs_search_header_offset(header) + 1;
} }
return BTRFS_UTIL_OK; 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; 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, 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_ioctl_search_header *header,
const struct btrfs_root_ref *ref, 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 = { struct btrfs_ioctl_ino_lookup_args lookup = {
.treeid = btrfs_search_header_objectid(header), .treeid = btrfs_search_header_objectid(header),
.objectid = le64_to_cpu(ref->dirid), .objectid = get_unaligned_le64(&ref->dirid),
}; };
int ret; int ret;
@ -1458,7 +1461,7 @@ static enum btrfs_util_error build_subvol_path_privileged(struct btrfs_util_subv
if (ret == -1) if (ret == -1)
return BTRFS_UTIL_ERROR_INO_LOOKUP_FAILED; 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), lookup.name, strlen(lookup.name),
path_len_ret); 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 * The orphan item might be for a free space cache inode, so
* check if there's a matching root item. * 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 (!err) {
if (*n >= capacity) { if (*n >= capacity) {
size_t new_capacity; size_t new_capacity;
@ -1778,14 +1781,14 @@ PUBLIC enum btrfs_util_error btrfs_util_deleted_subvolumes_fd(int fd,
*ids = new_ids; *ids = new_ids;
capacity = new_capacity; capacity = new_capacity;
} }
(*ids)[(*n)++] = header->offset; (*ids)[(*n)++] = btrfs_search_header_offset(header);
} else if (err != BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) { } else if (err != BTRFS_UTIL_ERROR_SUBVOLUME_NOT_FOUND) {
goto out; goto out;
} }
items_pos++; items_pos++;
buf_off += sizeof(*header) + header->len; buf_off += sizeof(*header) + btrfs_search_header_len(header);
search.key.min_offset = header->offset + 1; search.key.min_offset = btrfs_search_header_offset(header) + 1;
} }
err = BTRFS_UTIL_OK; err = BTRFS_UTIL_OK;