mirror of
https://github.com/Zygo/bees.git
synced 2025-05-17 13:25:45 +02:00
lib: fs: stop using libbtrfs-dev helper functions to re-enable buffer length checks
The Linux kernel's btrfs headers are better than the libbtrfs-dev headers: - the libbtrfs-dev headers have C++ language compatibility issues - upstream version in Linux kernel is more accurate and up to date - macros in libbtrfs-dev's ctree.h hide information that would enable bees to perform runtime buffer length checking - enum types whose presence cannot be detected with #ifdef When accessing members of metadata items from the filesystem, we want to verify that the member we are accessing is within the boundaries of the item that was retrieved; otherwise, a memory access violation may occur or garbage may be returned to the caller. A simple C++ template, given a pointer to a structure member and a buffer, can determine that the buffer contains enough bytes to safely access a struct member. This was implemented back in 2016, but left unused due to ctree.h issues. Some btrfs metadata structures have variable length despite using a fixed-size in-memory structure. The members that appear earliest in the structure contain information about which following members of the structure are used. The item stored in the filesystem is truncated after the last used member, and all following members must not be accessed. 'btrfs_stack_*' accessor macros obscure the memory boundaries of the members they access, which makes it impossible for a C++ template to verify the memory access. If the template checks the length of the entire structure, it will find an access violation for variable-length metadata items because the item is rarely large enough for the entire structure. Get rid of all the libbtrfs-dev accessor macros and reimplement them with the necessary buffer length checks. Signed-off-by: Zygo Blaxell <bees@furryterror.org>
This commit is contained in:
parent
6eb7afa65c
commit
7f660f50b8
@ -13,20 +13,22 @@
|
|||||||
// __u64 typedef and friends
|
// __u64 typedef and friends
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
// try Linux headers first
|
// the btrfs headers
|
||||||
#include <btrfs/ioctl.h>
|
#include <linux/btrfs.h>
|
||||||
|
#include <linux/btrfs_tree.h>
|
||||||
|
|
||||||
// Supply any missing definitions
|
// And now all the things that have been missing in some version of
|
||||||
#define mutex not_mutex
|
// the headers.
|
||||||
#include <btrfs/ctree.h>
|
|
||||||
// Repair the damage
|
|
||||||
#undef crc32c
|
|
||||||
#undef min
|
|
||||||
#undef max
|
|
||||||
#undef mutex
|
|
||||||
#undef swap
|
|
||||||
|
|
||||||
#ifndef BTRFS_FIRST_FREE_OBJECTID
|
enum btrfs_compression_type {
|
||||||
|
BTRFS_COMPRESS_NONE,
|
||||||
|
BTRFS_COMPRESS_ZLIB,
|
||||||
|
BTRFS_COMPRESS_LZO,
|
||||||
|
BTRFS_COMPRESS_ZSTD,
|
||||||
|
};
|
||||||
|
|
||||||
|
// BTRFS_CSUM_ITEM_KEY is not defined in include/uapi
|
||||||
|
#ifndef BTRFS_CSUM_ITEM_KEY
|
||||||
|
|
||||||
#define BTRFS_ROOT_TREE_OBJECTID 1ULL
|
#define BTRFS_ROOT_TREE_OBJECTID 1ULL
|
||||||
#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
|
#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
|
||||||
|
58
include/crucible/endian.h
Normal file
58
include/crucible/endian.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#ifndef CRUCIBLE_ENDIAN_H
|
||||||
|
#define CRUCIBLE_ENDIAN_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
namespace crucible {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct le_to_cpu_helper {
|
||||||
|
T operator()(const T v);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct le_to_cpu_helper<uint64_t> {
|
||||||
|
uint64_t operator()(const uint64_t v) { return le64toh(v); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if __SIZEOF_LONG__ == 8
|
||||||
|
// uint64_t is unsigned long on LP64 platforms
|
||||||
|
template<> struct le_to_cpu_helper<unsigned long long> {
|
||||||
|
unsigned long long operator()(const unsigned long long v) { return le64toh(v); }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<> struct le_to_cpu_helper<uint32_t> {
|
||||||
|
uint32_t operator()(const uint32_t v) { return le32toh(v); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct le_to_cpu_helper<uint16_t> {
|
||||||
|
uint16_t operator()(const uint16_t v) { return le64toh(v); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<> struct le_to_cpu_helper<uint8_t> {
|
||||||
|
uint8_t operator()(const uint8_t v) { return v; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T
|
||||||
|
le_to_cpu(const T v)
|
||||||
|
{
|
||||||
|
return le_to_cpu_helper<T>()(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
T
|
||||||
|
get_unaligned(const void *const p)
|
||||||
|
{
|
||||||
|
struct not_aligned {
|
||||||
|
T v;
|
||||||
|
} __attribute__((packed));
|
||||||
|
const not_aligned *const nap = reinterpret_cast<const not_aligned*>(p);
|
||||||
|
return nap->v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CRUCIBLE_ENDIAN_H
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef CRUCIBLE_FS_H
|
#ifndef CRUCIBLE_FS_H
|
||||||
#define CRUCIBLE_FS_H
|
#define CRUCIBLE_FS_H
|
||||||
|
|
||||||
|
#include "crucible/endian.h"
|
||||||
#include "crucible/error.h"
|
#include "crucible/error.h"
|
||||||
#include "crucible/spanner.h"
|
#include "crucible/spanner.h"
|
||||||
|
|
||||||
@ -206,47 +207,21 @@ namespace crucible {
|
|||||||
const T*
|
const T*
|
||||||
get_struct_ptr(const V &v, size_t offset = 0)
|
get_struct_ptr(const V &v, size_t offset = 0)
|
||||||
{
|
{
|
||||||
// OK so sometimes btrfs overshoots a little
|
THROW_CHECK2(out_of_range, v.size(), offset + sizeof(T), offset + sizeof(T) <= v.size());
|
||||||
// if (offset + sizeof(T) > v.size()) {
|
const uint8_t *const data_ptr = v.data();
|
||||||
// v.resize(offset + sizeof(T), 0);
|
return reinterpret_cast<const T*>(data_ptr + offset);
|
||||||
// }
|
|
||||||
// THROW_CHECK2(invalid_argument, v.size(), offset + sizeof(T), offset + sizeof(T) <= v.size());
|
|
||||||
return reinterpret_cast<const T*>(v.data() + offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class A, class R, class V>
|
|
||||||
R
|
|
||||||
call_btrfs_get(R (*func)(const A*), const V &v, size_t offset = 0)
|
|
||||||
{
|
|
||||||
return func(get_struct_ptr<A, V>(v, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T> struct btrfs_get_le;
|
|
||||||
|
|
||||||
template<> struct btrfs_get_le<__le64> {
|
|
||||||
uint64_t operator()(const void *p) { return get_unaligned_le64(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct btrfs_get_le<__le32> {
|
|
||||||
uint32_t operator()(const void *p) { return get_unaligned_le32(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct btrfs_get_le<__le16> {
|
|
||||||
uint16_t operator()(const void *p) { return get_unaligned_le16(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<> struct btrfs_get_le<__le8> {
|
|
||||||
uint8_t operator()(const void *p) { return get_unaligned_le8(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class S, class T, class V>
|
template<class S, class T, class V>
|
||||||
T
|
T
|
||||||
btrfs_get_member(T S::* member, V &v, size_t offset = 0)
|
btrfs_get_member(T S::* member, V &v, size_t offset = 0)
|
||||||
{
|
{
|
||||||
const S *const sp = nullptr;
|
const S *const sp = nullptr;
|
||||||
const T *const spm = &(sp->*member);
|
const T *const spm = &(sp->*member);
|
||||||
auto member_offset = reinterpret_cast<const uint8_t *>(spm) - reinterpret_cast<const uint8_t *>(sp);
|
const auto member_offset = reinterpret_cast<const uint8_t *>(spm) - reinterpret_cast<const uint8_t *>(sp);
|
||||||
return btrfs_get_le<T>()(get_struct_ptr<S>(v, offset + member_offset));
|
const void *struct_ptr = get_struct_ptr<T>(v, offset + member_offset);
|
||||||
|
const T unaligned_t = get_unaligned<T>(struct_ptr);
|
||||||
|
return le_to_cpu(unaligned_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Statvfs : public statvfs {
|
struct Statvfs : public statvfs {
|
||||||
|
@ -512,20 +512,20 @@ namespace crucible {
|
|||||||
|
|
||||||
Extent e;
|
Extent e;
|
||||||
e.m_begin = i.offset;
|
e.m_begin = i.offset;
|
||||||
auto compressed = call_btrfs_get(btrfs_stack_file_extent_compression, i.m_data);
|
auto compressed = btrfs_get_member(&btrfs_file_extent_item::compression, i.m_data);
|
||||||
// FIEMAP told us about compressed extents and we can too
|
// FIEMAP told us about compressed extents and we can too
|
||||||
if (compressed) {
|
if (compressed) {
|
||||||
e.m_flags |= FIEMAP_EXTENT_ENCODED;
|
e.m_flags |= FIEMAP_EXTENT_ENCODED;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = call_btrfs_get(btrfs_stack_file_extent_type, i.m_data);
|
auto type = btrfs_get_member(&btrfs_file_extent_item::type, i.m_data);
|
||||||
off_t len = -1;
|
off_t len = -1;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default:
|
default:
|
||||||
cerr << "Unhandled file extent type " << type << " in root " << m_tree_id << " ino " << m_stat.st_ino << endl;
|
cerr << "Unhandled file extent type " << type << " in root " << m_tree_id << " ino " << m_stat.st_ino << endl;
|
||||||
break;
|
break;
|
||||||
case BTRFS_FILE_EXTENT_INLINE:
|
case BTRFS_FILE_EXTENT_INLINE:
|
||||||
len = ranged_cast<off_t>(call_btrfs_get(btrfs_stack_file_extent_ram_bytes, i.m_data));
|
len = ranged_cast<off_t>(btrfs_get_member(&btrfs_file_extent_item::ram_bytes, i.m_data));
|
||||||
e.m_flags |= FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED;
|
e.m_flags |= FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED;
|
||||||
// Inline extents are never obscured, so don't bother filling in m_physical_len, etc.
|
// Inline extents are never obscured, so don't bother filling in m_physical_len, etc.
|
||||||
break;
|
break;
|
||||||
@ -533,17 +533,17 @@ namespace crucible {
|
|||||||
e.m_flags |= Extent::PREALLOC;
|
e.m_flags |= Extent::PREALLOC;
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case BTRFS_FILE_EXTENT_REG: {
|
case BTRFS_FILE_EXTENT_REG: {
|
||||||
e.m_physical = call_btrfs_get(btrfs_stack_file_extent_disk_bytenr, i.m_data);
|
e.m_physical = btrfs_get_member(&btrfs_file_extent_item::disk_bytenr, i.m_data);
|
||||||
|
|
||||||
// This is the length of the full extent (decompressed)
|
// This is the length of the full extent (decompressed)
|
||||||
off_t ram = ranged_cast<off_t>(call_btrfs_get(btrfs_stack_file_extent_ram_bytes, i.m_data));
|
off_t ram = ranged_cast<off_t>(btrfs_get_member(&btrfs_file_extent_item::ram_bytes, i.m_data));
|
||||||
|
|
||||||
// This is the length of the part of the extent appearing in the file (decompressed)
|
// This is the length of the part of the extent appearing in the file (decompressed)
|
||||||
len = ranged_cast<off_t>(call_btrfs_get(btrfs_stack_file_extent_num_bytes, i.m_data));
|
len = ranged_cast<off_t>(btrfs_get_member(&btrfs_file_extent_item::num_bytes, i.m_data));
|
||||||
|
|
||||||
// This is the offset from start of on-disk extent to the part we see in the file (decompressed)
|
// This is the offset from start of on-disk extent to the part we see in the file (decompressed)
|
||||||
// May be negative due to the kind of bug we're stuck with forever, so no cast range check
|
// May be negative due to the kind of bug we're stuck with forever, so no cast range check
|
||||||
off_t offset = call_btrfs_get(btrfs_stack_file_extent_offset, i.m_data);
|
off_t offset = btrfs_get_member(&btrfs_file_extent_item::offset, i.m_data);
|
||||||
|
|
||||||
// If there is a physical address there must be size too
|
// If there is a physical address there must be size too
|
||||||
if (e.m_physical) {
|
if (e.m_physical) {
|
||||||
|
@ -1018,7 +1018,7 @@ namespace crucible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (i.objectid == root_id && i.type == BTRFS_ROOT_ITEM_KEY) {
|
if (i.objectid == root_id && i.type == BTRFS_ROOT_ITEM_KEY) {
|
||||||
rv = max(rv, uint64_t(call_btrfs_get(btrfs_root_generation, i.m_data)));
|
rv = max(rv, uint64_t(btrfs_get_member(&btrfs_root_item::generation, i.m_data)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sk.min_offset < numeric_limits<decltype(sk.min_offset)>::max()) {
|
if (sk.min_offset < numeric_limits<decltype(sk.min_offset)>::max()) {
|
||||||
|
@ -649,8 +649,8 @@ BeesRoots::open_root_nocache(uint64_t rootid)
|
|||||||
for (auto i : sk.m_result) {
|
for (auto i : sk.m_result) {
|
||||||
sk.next_min(i);
|
sk.next_min(i);
|
||||||
if (i.type == BTRFS_ROOT_BACKREF_KEY && i.objectid == rootid) {
|
if (i.type == BTRFS_ROOT_BACKREF_KEY && i.objectid == rootid) {
|
||||||
auto dirid = call_btrfs_get(btrfs_stack_root_ref_dirid, i.m_data);
|
auto dirid = btrfs_get_member(&btrfs_root_ref::dirid, i.m_data);
|
||||||
auto name_len = call_btrfs_get(btrfs_stack_root_ref_name_len, i.m_data);
|
auto name_len = btrfs_get_member(&btrfs_root_ref::name_len, i.m_data);
|
||||||
auto name_start = sizeof(struct btrfs_root_ref);
|
auto name_start = sizeof(struct btrfs_root_ref);
|
||||||
auto name_end = name_len + name_start;
|
auto name_end = name_len + name_start;
|
||||||
THROW_CHECK2(runtime_error, i.m_data.size(), name_end, i.m_data.size() >= name_end);
|
THROW_CHECK2(runtime_error, i.m_data.size(), name_end, i.m_data.size() >= name_end);
|
||||||
@ -1100,7 +1100,7 @@ BeesCrawl::fetch_extents()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto gen = call_btrfs_get(btrfs_stack_file_extent_generation, i.m_data);
|
auto gen = btrfs_get_member(&btrfs_file_extent_item::generation, i.m_data);
|
||||||
if (gen < get_state_end().m_min_transid) {
|
if (gen < get_state_end().m_min_transid) {
|
||||||
BEESCOUNT(crawl_gen_low);
|
BEESCOUNT(crawl_gen_low);
|
||||||
++count_low;
|
++count_low;
|
||||||
@ -1126,7 +1126,7 @@ BeesCrawl::fetch_extents()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto type = call_btrfs_get(btrfs_stack_file_extent_type, i.m_data);
|
auto type = btrfs_get_member(&btrfs_file_extent_item::type, i.m_data);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
default:
|
default:
|
||||||
BEESLOGDEBUG("Unhandled file extent type " << type << " in root " << get_state_end().m_root << " ino " << i.objectid << " offset " << to_hex(i.offset));
|
BEESLOGDEBUG("Unhandled file extent type " << type << " in root " << get_state_end().m_root << " ino " << i.objectid << " offset " << to_hex(i.offset));
|
||||||
@ -1144,10 +1144,10 @@ BeesCrawl::fetch_extents()
|
|||||||
BEESCOUNT(crawl_prealloc);
|
BEESCOUNT(crawl_prealloc);
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case BTRFS_FILE_EXTENT_REG: {
|
case BTRFS_FILE_EXTENT_REG: {
|
||||||
auto physical = call_btrfs_get(btrfs_stack_file_extent_disk_bytenr, i.m_data);
|
auto physical = btrfs_get_member(&btrfs_file_extent_item::disk_bytenr, i.m_data);
|
||||||
auto ram = call_btrfs_get(btrfs_stack_file_extent_ram_bytes, i.m_data);
|
auto ram = btrfs_get_member(&btrfs_file_extent_item::ram_bytes, i.m_data);
|
||||||
auto len = call_btrfs_get(btrfs_stack_file_extent_num_bytes, i.m_data);
|
auto len = btrfs_get_member(&btrfs_file_extent_item::num_bytes, i.m_data);
|
||||||
auto offset = call_btrfs_get(btrfs_stack_file_extent_offset, i.m_data);
|
auto offset = btrfs_get_member(&btrfs_file_extent_item::offset, i.m_data);
|
||||||
BEESTRACE("Root " << get_state_end().m_root << " ino " << i.objectid << " physical " << to_hex(physical)
|
BEESTRACE("Root " << get_state_end().m_root << " ino " << i.objectid << " physical " << to_hex(physical)
|
||||||
<< " logical " << to_hex(i.offset) << ".." << to_hex(i.offset + len)
|
<< " logical " << to_hex(i.offset) << ".." << to_hex(i.offset + len)
|
||||||
<< " gen " << gen);
|
<< " gen " << gen);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user