Improve ELF/Trophy loader's error checking

This commit is contained in:
Eladash 2021-03-21 07:03:21 +02:00 committed by Ivan
parent a67b347966
commit 2f333424a6
6 changed files with 61 additions and 53 deletions

View file

@ -1556,6 +1556,17 @@ bool fs::dir::open(const std::string& path)
return true;
}
bool fs::file::strict_read_check(u64 _size, u64 type_size) const
{
if (usz pos0 = pos(), size0 = size(); pos0 >= size0 || (size0 - pos0) / type_size < _size)
{
fs::g_tls_error = fs::error::inval;
return false;
}
return true;
}
const std::string& fs::get_config_dir()
{
// Use magic static

View file

@ -200,6 +200,8 @@ namespace fs
{
std::unique_ptr<file_base> m_file;
bool strict_read_check(u64 size, u64 type_size) const;
public:
// Default constructor
file() = default;
@ -377,15 +379,24 @@ namespace fs
}
// Read std::basic_string
template <typename T>
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str, usz size,
template <bool IsStrict = false, typename T>
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str, usz _size,
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION(),
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN()) const
{
str.resize(size);
return read(&str[0], size * sizeof(T), line, col, file, func) == size * sizeof(T);
if (!m_file) xnull({line, col, file, func});
if (!_size) return true;
if constexpr (IsStrict)
{
// If _size arg is too high std::bad_alloc may happen in resize and then we cannot error check
if (!strict_read_check(_size, sizeof(T))) return false;
}
str.resize(_size);
return read(str.data(), sizeof(T) * _size, line, col, file, func) == sizeof(T) * _size;
}
// Read POD, sizeof(T) is used
@ -412,15 +423,23 @@ namespace fs
}
// Read POD std::vector
template <typename T>
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec, usz size,
template <bool IsStrict = false, typename T>
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec, usz _size,
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION(),
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN()) const
{
vec.resize(size);
return read(vec.data(), sizeof(T) * size, line, col, file, func) == sizeof(T) * size;
if (!m_file) xnull({line, col, file, func});
if (!_size) return true;
if constexpr (IsStrict)
{
if (!strict_read_check(_size, sizeof(T))) return false;
}
vec.resize(_size);
return read(vec.data(), sizeof(T) * _size, line, col, file, func) == sizeof(T) * _size;
}
// Read POD (experimental)

View file

@ -241,17 +241,15 @@ public:
if (!(opts & elf_opt::no_programs))
{
_phdrs.resize(header.e_phnum);
stream.seek(offset + header.e_phoff);
if (!stream.read(_phdrs))
if (!stream.read<true>(_phdrs, header.e_phnum))
return set_error(elf_error::stream_phdrs);
}
if (!(opts & elf_opt::no_sections))
{
shdrs.resize(header.e_shnum);
stream.seek(offset + header.e_shoff);
if (!stream.read(shdrs))
if (!stream.read<true>(shdrs, header.e_shnum))
return set_error(elf_error::stream_shdrs);
}
@ -265,9 +263,8 @@ public:
if (!(opts & elf_opt::no_data))
{
progs.back().bin.resize(hdr.p_filesz);
stream.seek(offset + hdr.p_offset);
if (!stream.read(progs.back().bin))
if (!stream.read<true>(progs.back().bin, hdr.p_filesz))
return set_error(elf_error::stream_data);
}
}

View file

@ -40,23 +40,15 @@ pup_object::pup_object(fs::file&& file) : m_file(std::move(file))
return;
}
constexpr usz entry_size = sizeof(PUPFileEntry) + sizeof(PUPHashEntry);
if (!m_header.file_count || (file_size - sizeof(PUPHeader)) / entry_size < m_header.file_count)
if (!m_header.file_count)
{
// These checks before read() are to avoid some std::bad_alloc exceptions when file_count is too large
// So we cannot rely on read() for error checking in such cases
m_error = pup_error::header_file_count;
return;
}
m_file_tbl.resize(m_header.file_count);
m_hash_tbl.resize(m_header.file_count);
if (!m_file.read(m_file_tbl) || !m_file.read(m_hash_tbl))
if (!m_file.read<true>(m_file_tbl, m_header.file_count) || !m_file.read<true>(m_hash_tbl, m_header.file_count))
{
// If these fail it is an unexpected filesystem error, because file size must suffice as we checked in previous checks
m_error = pup_error::file_entries;
m_error = pup_error::header_file_count;
return;
}

View file

@ -52,12 +52,10 @@ bool TROPUSRLoader::LoadTableHeaders()
m_file.seek(0x30);
m_tableHeaders.clear();
m_tableHeaders.resize(m_header.tables_count);
for (TROPUSRTableHeader& tableHeader : m_tableHeaders)
if (!m_file.read<true>(m_tableHeaders, m_header.tables_count))
{
if (!m_file.read(tableHeader))
return false;
return false;
}
return true;
@ -77,25 +75,17 @@ bool TROPUSRLoader::LoadTables()
if (tableHeader.type == 4u)
{
m_table4.clear();
m_table4.resize(tableHeader.entries_count);
for (auto& entry : m_table4)
{
if (!m_file.read(entry))
return false;
}
if (!m_file.read<true>(m_table4, tableHeader.entries_count))
return false;
}
if (tableHeader.type == 6u)
{
m_table6.clear();
m_table6.resize(tableHeader.entries_count);
for (auto& entry : m_table6)
{
if (!m_file.read(entry))
return false;
}
if (!m_file.read<true>(m_table6, tableHeader.entries_count))
return false;
}
// TODO: Other tables

View file

@ -39,8 +39,8 @@ bool TRPLoader::Install(const std::string& dest, bool /*show*/)
for (const TRPEntry& entry : m_entries)
{
trp_f.seek(entry.offset);
buffer.resize(entry.size);
if (!trp_f.read(buffer))
if (!trp_f.read<true>(buffer, entry.size))
{
trp_log.error("Failed to read TRPEntry at: offset=0x%x, size=0x%x", entry.offset, entry.size);
continue; // ???
@ -103,10 +103,10 @@ bool TRPLoader::LoadHeader(bool show)
if (m_header.trp_version >= 2)
{
unsigned char hash[20];
std::vector<unsigned char> file_contents(m_header.trp_file_size);
std::vector<u8> file_contents;
trp_f.seek(0);
if (!trp_f.read(file_contents))
if (!trp_f.read<true>(file_contents, m_header.trp_file_size))
{
trp_log.notice("Failed verifying checksum");
}
@ -126,18 +126,17 @@ bool TRPLoader::LoadHeader(bool show)
}
m_entries.clear();
m_entries.resize(m_header.trp_files_count);
for (u32 i = 0; i < m_header.trp_files_count; i++)
if (!trp_f.read<true>(m_entries, m_header.trp_files_count))
{
if (!trp_f.read(m_entries[i]))
{
return false;
}
return false;
}
if (show)
if (show)
{
for (const auto& entry : m_entries)
{
trp_log.notice("TRP entry #%d: %s", m_entries[i].name);
trp_log.notice("TRP entry #%u: %s", &entry - m_entries.data(), entry.name);
}
}