QElfParser: rewrite using elf.h

This rewrite uses the actual structures supplied by the system's C
library, so it should be easier to read. It removes hardcoded constants
with little evident meaning in favor of sizeof() and the macros from
that header. It also removes advancing the data pointer in favor of
having absolute offsets.

The resulting implementation is stricter than the original, checking
more fields in the header. Because the QPluginLoader and QFactoryLoader
users may make decisions based on availability of plugins before
attempting to load them, it's better to be stricter here than to fail
later when trying to dlopen() them.

Debugging and testing are much improved. Instead of stored artifacts, I
added a routine to modify a valid plugin to make it invalid, given the
conditions we've found so far.

If you turn debugging on for this category, you'll see things like:

 not-elf.fcqdMq.so : Not an ELF file (invalid signature)
 wrong-word-size.QrnSAx.so : ELF 32-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 invalid-word-size.bOkXvp.so : Invalid ELF file (class 0), LSB (GNU/Linux)
 unknown-word-size.ogYKeF.so : Invalid ELF file (class 66), LSB (GNU/Linux)
 wrong-endian.owiElX.so : ELF 64-bit MSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 invalid-endian.FRxClR.so : ELF 64-bit invalid endianness (0) (GNU/Linux)
 unknown-endian.FfvRrP.so : ELF 64-bit invalid endianness (65) (GNU/Linux)
 elf-version-0.gPTdpQ.so : ELF 64-bit LSB (GNU/Linux), file version 0
 elf-version-2.jlIUUg.so : ELF 64-bit LSB (GNU/Linux), file version 2
 executable.LlXiFp.so : ELF 64-bit LSB (GNU/Linux), version 1, executable, x86-64
 relocatable.UsOYuy.so : ELF 64-bit LSB (GNU/Linux), version 1, relocatable, x86-64
 core-file.hqvNRz.so : ELF 64-bit LSB (GNU/Linux), version 1, core dump, x86-64
 invalid-type.CIJgfS.so : ELF 64-bit LSB (GNU/Linux), version 1, unknown type 259, x86-64
 wrong-arch.UcNmgz.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, AArch64
 file-version-0.lZYuda.so : ELF 64-bit LSB (GNU/Linux), version 0, shared library or PIC executable, x86-64
 file-version-2.ucfdwL.so : ELF 64-bit LSB (GNU/Linux), version 2, shared library or PIC executable, x86-64
 no-sections.rSjsHh.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 no-sections.rSjsHh.so : contains 0 sections of 64 bytes at offset 0 ; section header string table (shstrtab) is entry 0
 no-sections.rSjsHh.so : no section table present, not able to find Qt metadata
 qtmetadata-executable.vrxcIf.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 qtmetadata-executable.vrxcIf.so : contains 42 sections of 64 bytes at offset 997256 ; section header string table (shstrtab) is entry 41
 qtmetadata-executable.vrxcIf.so : shstrtab section is located at offset 996831 size 423
 qtmetadata-executable.vrxcIf.so : section 0 name "" type NULL flags X offset 0x0 size 0x0
 qtmetadata-executable.vrxcIf.so : section 1 name ".note.gnu.property" type NOTE flags AX offset 0x2a8 size 0x30
 qtmetadata-executable.vrxcIf.so : section 2 name ".note.gnu.build-id" type NOTE flags AX offset 0x2d8 size 0x24
 qtmetadata-executable.vrxcIf.so : section 3 name ".hash" type HASH flags AX offset 0x300 size 0x44c
 qtmetadata-executable.vrxcIf.so : section 4 name ".gnu.hash" type 0x6ffffff6 flags AX offset 0x750 size 0x3b8
 qtmetadata-executable.vrxcIf.so : section 5 name ".dynsym" type DYNSYM flags AX offset 0xb08 size 0xd50
 qtmetadata-executable.vrxcIf.so : section 6 name ".dynstr" type STRTAB flags AX offset 0x1858 size 0x15d8
 qtmetadata-executable.vrxcIf.so : section 7 name ".gnu.version" type 0x6fffffff flags AX offset 0x2e30 size 0x11c
 qtmetadata-executable.vrxcIf.so : section 8 name ".gnu.version_r" type 0x6ffffffe flags AX offset 0x2f50 size 0xb0
 qtmetadata-executable.vrxcIf.so : section 9 name ".rela.dyn" type RELA flags AX offset 0x3000 size 0x480
 qtmetadata-executable.vrxcIf.so : section 10 name ".rela.plt" type RELA flags AX offset 0x3480 size 0x7e0
 qtmetadata-executable.vrxcIf.so : section 11 name ".init" type PROGBITS flags AX offset 0x4000 size 0x1b
 qtmetadata-executable.vrxcIf.so : section 12 name ".plt" type PROGBITS flags AX offset 0x4020 size 0x550
 qtmetadata-executable.vrxcIf.so : section 13 name ".plt.got" type PROGBITS flags AX offset 0x4570 size 0x8
 qtmetadata-executable.vrxcIf.so : section 14 name ".text" type PROGBITS flags AX offset 0x4580 size 0x110e
 qtmetadata-executable.vrxcIf.so : section 15 name ".fini" type PROGBITS flags AX offset 0x5690 size 0xd
 qtmetadata-executable.vrxcIf.so : section 16 name ".rodata" type PROGBITS flags AX offset 0x6000 size 0x473
 qtmetadata-executable.vrxcIf.so : section 17 name ".qtversion" type PROGBITS flags AX offset 0x6478 size 0x10
 qtmetadata-executable.vrxcIf.so : section 18 name ".qtmetadata" type PROGBITS flags AX offset 0x64a0 size 0x19b
 qtmetadata-executable.vrxcIf.so : found .qtmetadata section
 qtmetadata-writable.stzwrk.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64

Change-Id: I42eb903a916645db9900fffd16a4437af9728eea
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2021-09-10 20:30:08 -07:00
parent 3abcff49eb
commit 46fc01d7ca
12 changed files with 827 additions and 233 deletions

View File

@ -1,6 +1,7 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the QtCore module of the Qt Toolkit. ** This file is part of the QtCore module of the Qt Toolkit.
@ -39,189 +40,542 @@
#include "qelfparser_p.h" #include "qelfparser_p.h"
#if defined (Q_OF_ELF) && defined(Q_CC_GNU) #if defined (Q_OF_ELF) && __has_include(<elf.h>)
#include "qlibrary_p.h" #include "qlibrary_p.h"
#include <qdebug.h>
#include <qloggingcategory.h>
#include <qnumeric.h>
#include <qsysinfo.h>
#include <elf.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// #define QELFPARSER_DEBUG 1 // Whether we include some extra validity checks
// (checks to ensure we don't read out-of-bounds are always included)
static constexpr bool IncludeValidityChecks = true;
const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh) #ifdef QT_BUILD_INTERNAL
# define QELFPARSER_DEBUG
#endif
#if defined(QELFPARSER_DEBUG)
static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser")
# define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
#else
# define qEDebug if (false) {} else QNoDebug()
#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
namespace {
template <QSysInfo::Endian Order> struct ElfEndianTraits
{ {
sh->name = qFromUnaligned<qelfword_t>(data); static constexpr unsigned char DataOrder = ELFDATA2LSB;
data += sizeof(qelfword_t); // sh_name template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
sh->type = qFromUnaligned<qelfword_t>(data); };
data += sizeof(qelfword_t) // sh_type template <> struct ElfEndianTraits<QSysInfo::BigEndian>
+ sizeof(qelfaddr_t) // sh_flags {
+ sizeof(qelfaddr_t); // sh_addr static constexpr unsigned char DataOrder = ELFDATA2MSB;
sh->offset = qFromUnaligned<qelfoff_t>(data); template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
data += sizeof(qelfoff_t); // sh_offset };
sh->size = qFromUnaligned<qelfoff_t>(data);
data += sizeof(qelfoff_t); // sh_size template <typename EquivalentPointerType> struct ElfTypeTraits
return data; {
static constexpr unsigned char Class = ELFCLASS64;
// integer types
using Half = Elf64_Half;
using Word = Elf64_Word;
using Addr = Elf64_Addr;
using Off = Elf64_Off;
// structure types
using Ehdr = Elf64_Ehdr;
using Shdr = Elf64_Shdr;
using Phdr = Elf64_Phdr;
using Nhdr = Elf64_Nhdr;
};
template <> struct ElfTypeTraits<quint32>
{
static constexpr unsigned char Class = ELFCLASS32;
// integer types
using Half = Elf32_Half;
using Word = Elf32_Word;
using Addr = Elf32_Addr;
using Off = Elf32_Off;
// structure types
using Ehdr = Elf32_Ehdr;
using Shdr = Elf32_Shdr;
using Phdr = Elf32_Phdr;
using Nhdr = Elf32_Nhdr;
};
struct ElfMachineCheck
{
static const Elf32_Half ExpectedMachine =
#if 0
// nothing
#elif defined(Q_PROCESSOR_ARM_32)
EM_ARM
#elif defined(Q_PROCESSOR_ARM_64)
EM_AARCH64
#elif defined(Q_PROCESSOR_BLACKFIN)
EM_BLACKFIN
#elif defined(Q_PROCESSOR_IA64)
EM_IA_64
#elif defined(Q_PROCESSOR_MIPS)
EM_MIPS
#elif defined(Q_PROCESSOR_POWER_32)
EM_PPC
#elif defined(Q_PROCESSOR_POWER_64)
EM_PPC64
#elif defined(Q_PROCESSOR_RISCV)
EM_RISCV
#elif defined(Q_PROCESSOR_S390)
EM_S390
#elif defined(Q_PROCESSOR_SH)
EM_SH
#elif defined(Q_PROCESSOR_SPARC_V9)
# warning "Please confirm that this is correct for Linux and Solaris"
EM_SPARCV9
#elif defined(Q_PROCESSOR_SPARC_64)
# warning "Please confirm that this is correct for Linux and Solaris"
EM_SPARCV9
#elif defined(Q_PROCESSOR_SPARC)
EM_SPARC
#elif defined(Q_PROCESSOR_WASM)
#elif defined(Q_PROCESSOR_X86_32)
EM_386
#elif defined(Q_PROCESSOR_X86_64)
EM_X86_64
#else
# error "Unknown Q_PROCESSOR_xxx macro, please update."
EM_NONE
#endif
;
};
struct ElfHeaderCommonCheck
{
static_assert(std::is_same_v<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
"e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
// bytes 0-3
static bool checkElfMagic(const uchar *ident)
{
return memcmp(ident, ELFMAG, SELFMAG) == 0;
}
// byte 6
static bool checkElfVersion(const uchar *ident)
{
uchar elfversion = ident[EI_VERSION];
return elfversion == EV_CURRENT;
}
struct CommonHeader {
Elf32_Half type;
Elf32_Half machine;
Elf32_Word version;
};
};
template <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
struct ElfHeaderCheck : public ElfHeaderCommonCheck
{
using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
using EndianTraits = ElfEndianTraits<Order>;
using Ehdr = typename TypeTraits::Ehdr;
// byte 4
static bool checkClass(const uchar *ident)
{
uchar klass = ident[EI_CLASS];
return klass == TypeTraits::Class;
}
// byte 5
static bool checkDataOrder(const uchar *ident)
{
uchar data = ident[EI_DATA];
return data == EndianTraits::DataOrder;
}
// byte 7
static bool checkOsAbi(const uchar *ident)
{
uchar osabi = ident[EI_OSABI];
// we don't check
Q_UNUSED(osabi);
return true;
}
// byte 8
static bool checkAbiVersion(const uchar *ident)
{
uchar abiversion = ident[EI_ABIVERSION];
// we don't check (and I don't know anyone who uses this)
Q_UNUSED(abiversion);
return true;
}
// bytes 9-16
static bool checkPadding(const uchar *ident)
{
// why would we check this?
Q_UNUSED(ident);
return true;
}
static bool checkIdent(const Ehdr &header)
{
return checkElfMagic(header.e_ident)
&& checkClass(header.e_ident)
&& checkDataOrder(header.e_ident)
&& checkElfVersion(header.e_ident)
&& checkOsAbi(header.e_ident)
&& checkAbiVersion(header.e_ident)
&& checkPadding(header.e_ident);
}
static bool checkType(const Ehdr &header)
{
return header.e_type == ET_DYN;
}
static bool checkMachine(const Ehdr &header)
{
return header.e_machine == ElfMachineCheck::ExpectedMachine;
}
static bool checkFileVersion(const Ehdr &header)
{
return header.e_version == EV_CURRENT;
}
static bool checkHeader(const Ehdr &header)
{
if (!checkIdent(header))
return false;
if (!IncludeValidityChecks)
return true;
return checkType(header)
&& checkMachine(header)
&& checkFileVersion(header);
}
Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header)
{
if (!checkElfMagic(header.e_ident))
return QLibrary::tr("invalid signature");
if (!checkClass(header.e_ident))
return QLibrary::tr("file is for a different word size");
if (!checkDataOrder(header.e_ident))
return QLibrary::tr("file is for the wrong endianness");
if (!checkElfVersion(header.e_ident) || !checkFileVersion(header))
return QLibrary::tr("file has an unknown ELF version");
if (!checkOsAbi(header.e_ident) || !checkAbiVersion(header.e_ident))
return QLibrary::tr("file has an unexpected ABI");
if (!checkType(header))
return QLibrary::tr("file is not a shared object");
if (!checkMachine(header))
return QLibrary::tr("file is for a different processor");
return QString();
}
static CommonHeader extractCommonHeader(const uchar *data)
{
auto header = reinterpret_cast<const Ehdr *>(data);
CommonHeader r;
r.type = EndianTraits::fromEndian(header->e_type);
r.machine = EndianTraits::fromEndian(header->e_machine);
r.version = EndianTraits::fromEndian(header->e_version);
return r;
}
};
struct ElfHeaderDebug { const uchar *e_ident; };
Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeaderDebug h)
{
const uchar *e_ident = h.e_ident;
if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) {
d << "Not an ELF file (invalid signature)";
return d;
}
QDebugStateSaver saver(d);
d.nospace();
quint8 elfclass = e_ident[EI_CLASS];
switch (elfclass) {
case ELFCLASSNONE:
default:
d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), ";
break;
case ELFCLASS32:
d << "ELF 32-bit ";
break;
case ELFCLASS64:
d << "ELF 64-bit ";
break;
}
quint8 dataorder = e_ident[EI_DATA];
switch (dataorder) {
case ELFDATANONE:
default:
d << "invalid endianness (" << e_ident[EI_DATA] << ')';
break;
case ELFDATA2LSB:
d << "LSB";
break;
case ELFDATA2MSB:
d << "MSB";
break;
}
switch (e_ident[EI_OSABI]) {
case ELFOSABI_SYSV: d << " (SYSV"; break;
case ELFOSABI_HPUX: d << " (HP-UX"; break;
case ELFOSABI_NETBSD: d << " (NetBSD"; break;
case ELFOSABI_GNU: d << " (GNU/Linux"; break;
case ELFOSABI_SOLARIS: d << " (Solaris"; break;
case ELFOSABI_AIX: d << " (AIX"; break;
case ELFOSABI_IRIX: d << " (IRIX"; break;
case ELFOSABI_FREEBSD: d << " (FreeBSD"; break;
case ELFOSABI_OPENBSD: d << " (OpenBSD"; break;
default: d << " (OS ABI " << e_ident[EI_VERSION]; break;
}
if (e_ident[EI_ABIVERSION])
d << " v" << e_ident[EI_ABIVERSION];
d << ')';
if (e_ident[EI_VERSION] != 1) {
d << ", file version " << e_ident[EI_VERSION];
return d;
}
ElfHeaderCommonCheck::CommonHeader r;
if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
else
return d;
d << ", version " << r.version;
switch (r.type) {
case ET_NONE: d << ", no type"; break;
case ET_REL: d << ", relocatable"; break;
case ET_EXEC: d << ", executable"; break;
case ET_DYN: d << ", shared library or PIC executable"; break;
case ET_CORE: d << ", core dump"; break;
default: d << ", unknown type " << r.type; break;
}
switch (r.machine) {
// list definitely not exhaustive!
case EM_NONE: d << ", no machine"; break;
case EM_ARM: d << ", ARM"; break;
case EM_AARCH64: d << ", AArch64"; break;
case EM_BLACKFIN: d << ", Blackfin"; break;
case EM_IA_64: d << ", IA-64"; break;
case EM_MIPS: d << ", MIPS"; break;
case EM_PPC: d << ", PowerPC"; break;
case EM_PPC64: d << ", PowerPC 64-bit"; break;
case EM_RISCV: d << ", RISC-V"; break;
case EM_S390: d << ", S/390"; break;
case EM_SH: d << ", SuperH"; break;
case EM_SPARC: d << ", SPARC"; break;
case EM_SPARCV9: d << ", SPARCv9"; break;
case EM_386: d << ", i386"; break;
case EM_X86_64: d << ", x86-64"; break;
default: d << ", other machine type " << r.machine; break;
}
return d;
} }
QLibraryScanResult QElfParser::parse(const char *dataStart, ulong fdlen, QString *errMsg) struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
{ {
#if defined(QELFPARSER_DEBUG) // not exhaustive, just a few common things
qDebug() << "QElfParser::parse " << library; QDebugStateSaver saver(d);
#endif d << Qt::hex << Qt::showbase;
d << "type";
switch (s.shdr->sh_type) {
case SHT_NULL: d << "NULL"; break;
case SHT_PROGBITS: d << "PROGBITS"; break;
case SHT_SYMTAB: d << "SYMTAB"; break;
case SHT_STRTAB: d << "STRTAB"; break;
case SHT_RELA: d << "RELA"; break;
case SHT_HASH: d << "HASH"; break;
case SHT_DYNAMIC: d << "DYNAMIC"; break;
case SHT_NOTE: d << "NOTE"; break;
case SHT_NOBITS: d << "NOBITS"; break;
case SHT_DYNSYM: d << "DYNSYM"; break;
case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break;
case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break;
default: d << s.shdr->sh_type;
}
if (fdlen < 64) { d << "flags";
*errMsg = QLibrary::tr("'%1' is not an ELF object (%2)").arg(*errMsg, QLibrary::tr("file too small")); d.nospace();
return {}; if (s.shdr->sh_flags & SHF_WRITE)
} d << 'W';
const char *data = dataStart; if (s.shdr->sh_flags & SHF_ALLOC)
if (qstrncmp(data, "\177ELF", 4) != 0) { d << 'A';
*errMsg = QLibrary::tr("'%1' is not an ELF object").arg(*errMsg); if (s.shdr->sh_flags & SHF_EXECINSTR)
return {}; d << 'X';
} if (s.shdr->sh_flags & SHF_STRINGS)
// 32 or 64 bit d << 'S';
if (data[4] != 1 && data[4] != 2) { if (s.shdr->sh_flags & SHF_TLS)
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd cpu architecture")); d << 'T';
d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size;
return d;
}
struct ErrorMaker
{
QString *errMsg;
constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const
{
*errMsg = QLibrary::tr("'%1' is not a valid ELF object (%2)")
.arg(*errMsg, std::move(text));
return {}; return {};
} }
/* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs QLibraryScanResult notfound() const
to match the _plugin_ architecture. {
*/ *errMsg = QLibrary::tr("'%1' is not a Qt plugin (.qtmetadata section not found)")
constexpr int ExpectedClass = (sizeof(void *) == 4) ? 1 : 2; .arg(*errMsg);
if (data[4] != ExpectedClass) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("wrong cpu architecture"));
return {}; return {};
} }
};
} // unnamed namespace
// endian QT_WARNING_POP
constexpr int ExpectedEndianness = (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) ? 1 : 2;
if (data[5] != ExpectedEndianness) { using T = ElfHeaderCheck<>::TypeTraits;
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd endianness"));
return {}; static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
{
auto header = reinterpret_cast<const T::Ehdr *>(data.data());
// in order to find the .qtmetadata section, we need to:
// a) find the section table
// it's located at offset header->e_shoff
// validate it
T::Word e_shnum = header->e_shnum;
T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half
if (qAddOverflow(offset, header->e_shoff, &offset) || offset > size_t(data.size()))
return error(QLibrary::tr("section table extends past the end of the file"));
// b) find the section entry for the section header string table (shstrab)
// it's a section whose entry is pointed by e_shstrndx
auto sections = reinterpret_cast<const T::Shdr *>(data.data() + header->e_shoff);
auto sections_end = sections + e_shnum;
auto shdr = sections + header->e_shstrndx;
// validate the shstrtab
offset = shdr->sh_offset;
T::Off shstrtab_size = shdr->sh_size;
qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size;
if (T::Off end; qAddOverflow<T::Off>(offset, shstrtab_size, &end)
|| end > size_t(data.size()))
return error(QLibrary::tr("section header string table extends past the end of the file"));
// c) iterate over the sections to find .qtmetadata
const char *shstrtab_start = data.data() + offset;
shdr = sections;
for (int section = 0; shdr != sections_end; ++section, ++shdr) {
QLatin1String name;
if (shdr->sh_name < shstrtab_size) {
const char *namestart = shstrtab_start + shdr->sh_name;
size_t len = qstrnlen(namestart, shstrtab_size - shdr->sh_name);
name = QLatin1String(namestart, len);
}
qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr};
// sanity check the section
if (name.isNull())
return error(QLibrary::tr("a section name extends past the end of the file"));
// sections aren't allowed to extend past the end of the file, unless
// they are NOBITS sections
if (shdr->sh_type == SHT_NOBITS)
continue;;
if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end)
|| end > size_t(data.size())) {
return error(QLibrary::tr("a section data extends past the end of the file"));
} }
data += 16 // e_ident if (name != QLatin1String(".qtmetadata"))
+ sizeof(qelfhalf_t) // e_type
+ sizeof(qelfhalf_t) // e_machine
+ sizeof(qelfword_t) // e_version
+ sizeof(qelfaddr_t) // e_entry
+ sizeof(qelfoff_t); // e_phoff
qelfoff_t e_shoff = qFromUnaligned<qelfoff_t> (data);
data += sizeof(qelfoff_t) // e_shoff
+ sizeof(qelfword_t); // e_flags
qelfhalf_t e_shsize = qFromUnaligned<qelfhalf_t> (data);
if (e_shsize > fdlen) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shsize"));
return {};
}
data += sizeof(qelfhalf_t) // e_ehsize
+ sizeof(qelfhalf_t) // e_phentsize
+ sizeof(qelfhalf_t); // e_phnum
qelfhalf_t e_shentsize = qFromUnaligned<qelfhalf_t> (data);
if (e_shentsize % 4) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shentsize"));
return {};
}
data += sizeof(qelfhalf_t); // e_shentsize
qelfhalf_t e_shnum = qFromUnaligned<qelfhalf_t> (data);
data += sizeof(qelfhalf_t); // e_shnum
qelfhalf_t e_shtrndx = qFromUnaligned<qelfhalf_t> (data);
data += sizeof(qelfhalf_t); // e_shtrndx
if ((quint32)(e_shnum * e_shentsize) > fdlen) {
const QString message =
QLibrary::tr("announced %n section(s), each %1 byte(s), exceed file size",
nullptr, int(e_shnum)).arg(e_shentsize);
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, message);
return {};
}
#if defined(QELFPARSER_DEBUG)
qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes";
#endif
ElfSectionHeader strtab;
qulonglong soff = e_shoff + qelfword_t(e_shentsize) * qelfword_t(e_shtrndx);
if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("shstrtab section header seems to be at %1")
.arg(QString::number(soff, 16)));
return {};
}
parseSectionHeader(dataStart + soff, &strtab);
m_stringTableFileOffset = strtab.offset;
if ((quint32)(strtab.offset + strtab.size) > fdlen || strtab.offset == 0) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("string table seems to be at %1")
.arg(QString::number(strtab.offset, 16)));
return {};
}
#if defined(QELFPARSER_DEBUG)
qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data());
#endif
const char *s = dataStart + e_shoff;
for (int i = 0; i < e_shnum; ++i) {
ElfSectionHeader sh;
parseSectionHeader(s, &sh);
if (sh.name == 0) {
s += e_shentsize;
continue; continue;
qEDebug << "found .qtmetadata section";
if (IncludeValidityChecks && shdr->sh_flags & (SHF_WRITE | SHF_EXECINSTR)) {
if (shdr->sh_flags & SHF_WRITE)
return error(QLibrary::tr(".qtmetadata section is writable"));
return error(QLibrary::tr(".qtmetadata section is executable"));
} }
const char *shnam = dataStart + m_stringTableFileOffset + sh.name; if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
return error(QLibrary::tr("section .qtmetadata is too small"));
if (m_stringTableFileOffset + sh.name > fdlen) { return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)") qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
.arg(*errMsg, QLibrary::tr("section name %1 of %2 behind end of file")
.arg(i).arg(e_shnum));
return {};
} }
#if defined(QELFPARSER_DEBUG) // section .qtmetadata not found
qDebug() << "++++" << i << shnam; return error.notfound();
#endif }
if (qstrcmp(shnam, ".qtmetadata") == 0 ) { QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg)
if (!(sh.type & 0x1)) { {
if (shnam[1] == 'r') { ErrorMaker error(errMsg);
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)") if (size_t(data.size()) < sizeof(T::Ehdr)) {
.arg(*errMsg, QLibrary::tr("empty .rodata. not a library.")); qEDebug << "file too small:" << size_t(data.size());
return {}; return error(QLibrary::tr("file too small"));
}
#if defined(QELFPARSER_DEBUG)
qDebug()<<"section is not program data. skipped.";
#endif
s += e_shentsize;
continue;
} }
if (sh.offset == 0 || (sh.offset + sh.size) > fdlen || sh.size < 1) { qEDebug << ElfHeaderDebug{ reinterpret_cast<const uchar *>(data.data()) };
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("missing section data. This is not a library.")); auto header = reinterpret_cast<const T::Ehdr *>(data.data());
return {}; if (!ElfHeaderCheck<>::checkHeader(*header))
return error(ElfHeaderCheck<>::explainCheckFailure(*header));
qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize
<< "bytes at offset" << header->e_shoff
<< "; section header string table (shstrtab) is entry" << header->e_shstrndx;
// some sanity checks
if constexpr (IncludeValidityChecks) {
if (header->e_shentsize != sizeof(T::Shdr))
return error(QLibrary::tr("unexpected section entry size (%1)")
.arg(header->e_shentsize));
} }
if (sh.size < sizeof(QPluginMetaData::MagicHeader)) { if (header->e_shoff == 0 || header->e_shnum == 0) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)") // this is still a valid ELF file but we don't have a section table
.arg(*errMsg, QLibrary::tr("section .qtmetadata is too small")); qEDebug << "no section table present, not able to find Qt metadata";
return {}; return error.notfound();
} }
sh.offset += sizeof(QPluginMetaData::MagicString);
sh.size -= sizeof(QPluginMetaData::MagicString); if (header->e_shnum && header->e_shstrndx >= header->e_shnum)
return { qsizetype(sh.offset), qsizetype(sh.size) }; return error(QLibrary::tr("e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
} .arg(header->e_shstrndx).arg(header->e_shnum));
s += e_shentsize;
} return scanSections(data, error);
return {};
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -56,35 +56,13 @@
QT_REQUIRE_CONFIG(library); QT_REQUIRE_CONFIG(library);
#if defined(Q_OF_ELF) && defined(Q_CC_GNU) #if defined(Q_OF_ELF) && __has_include(<elf.h>)
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QString; struct QElfParser
class QLibraryPrivate;
typedef quint16 qelfhalf_t;
typedef quint32 qelfword_t;
typedef quintptr qelfoff_t;
typedef quintptr qelfaddr_t;
class QElfParser
{ {
public: static QLibraryScanResult parse(QByteArrayView data, QString *errMsg);
enum { ElfLittleEndian = 0, ElfBigEndian = 1 };
struct ElfSectionHeader
{
qelfword_t name;
qelfword_t type;
qelfoff_t offset;
qelfoff_t size;
};
qelfoff_t m_stringTableFileOffset;
const char *parseSectionHeader(const char* s, ElfSectionHeader *sh);
QLibraryScanResult parse(const char *m_s, ulong fdlen, QString *errMsg);
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -193,8 +193,8 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin
More importantly, the pattern string may exist in the debug information due More importantly, the pattern string may exist in the debug information due
to it being used in the plugin in the first place. to it being used in the plugin in the first place.
*/ */
#if defined (Q_OF_ELF) #if defined (Q_OF_ELF) && __has_include(<elf.h>)
return QElfParser().parse(s, s_len, errMsg); return QElfParser::parse({s, s_len}, errMsg);
#elif defined(Q_OF_MACH_O) #elif defined(Q_OF_MACH_O)
return QMachOParser::parse(s, s_len, errMsg); return QMachOParser::parse(s, s_len, errMsg);
#endif #endif

View File

@ -1,4 +0,0 @@
p¶¤Ðã¨ø±ÕÛcdL+ôúæî&‘¿&÷ýe<C3BD>ü¥=კ²
•o°ÃÊŽI §Ù<C2A7>Ïmgƒ]!ÀZ
L'Ž)t±
ÙN»¸(e©× P)Y8öG ˆ6ß-yÈÏÀ ñŸ§÷—“"ôZÖÿ?Ë^<5E>náÁÇß5$žôY=£ð#y

View File

@ -1 +0,0 @@
£Çv.³Y<59>¨tKëW3

View File

@ -1 +0,0 @@
£ÝÈÈ‚åžT-õ«´ÊôÚ¥ Àä¸ï¨ì¾œÀi<C380>8¼_ñxÓõª¾I±Ð×®ÝxÎ=úØ4@þñ[¨<>—úBàKS$ú

View File

@ -1 +0,0 @@
<EFBFBD> !<21>\~هU­u<C2AD><75>:9<><39> <><1B>T<EFBFBD>+91<39>E<>ما<D985>x<EFBFBD>ng5سك<D8B3>zh<13><><EFBFBD><EFBFBD>ئ^t<><1D><><EFBFBD><EFBFBD>'ئخmm*ث<>dXH;vw+<2B>G<EFBFBD><47>أـظ <0A>9L<>0!

View File

@ -1,2 +0,0 @@
яЬє<EFBFBD>QВ
уЕ-Ђ9в

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation. ** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of the test suite of the Qt Toolkit. ** This file is part of the test suite of the Qt Toolkit.
@ -31,7 +31,10 @@
#include <QSignalSpy> #include <QSignalSpy>
#include <QJsonArray> #include <QJsonArray>
#include <qdir.h> #include <qdir.h>
#include <qendian.h>
#include <qpluginloader.h> #include <qpluginloader.h>
#include <qprocess.h>
#include <qtemporaryfile.h>
#include "theplugin/plugininterface.h" #include "theplugin/plugininterface.h"
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O) #if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
@ -91,6 +94,84 @@
# define PREFIX "lib" # define PREFIX "lib"
#endif #endif
#if defined(Q_OF_ELF)
# include <elf.h>
# include <memory>
# include <functional>
# ifdef _LP64
using ElfHeader = Elf64_Ehdr;
using ElfShdr = Elf64_Shdr;
# else
using ElfHeader = Elf32_Ehdr;
using ElfShdr = Elf32_Shdr;
# endif
struct ElfPatcher
{
using FullPatcher = void(ElfHeader *, QFile *);
FullPatcher *f;
ElfPatcher(FullPatcher *f = nullptr) : f(f) {}
template <typename T> using IsSingleArg = std::is_invocable<T, ElfHeader *>;
template <typename T> static std::enable_if_t<IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
{
using WithoutQFile = void(*)(ElfHeader *);
static const WithoutQFile f = t;
return { [](ElfHeader *h, QFile *) { f(h);} };
}
template <typename T> static std::enable_if_t<!IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
{
return { t };
}
};
Q_DECLARE_METATYPE(ElfPatcher)
static std::unique_ptr<QTemporaryFile> patchElf(const QString &source, ElfPatcher patcher)
{
std::unique_ptr<QTemporaryFile> tmplib;
bool ok = false;
[&]() {
QFile srclib(source);
QVERIFY2(srclib.open(QIODevice::ReadOnly), qPrintable(srclib.errorString()));
qint64 srcsize = srclib.size();
const uchar *srcdata = srclib.map(0, srcsize, QFile::MapPrivateOption);
QVERIFY2(srcdata, qPrintable(srclib.errorString()));
// copy our source plugin so we can modify it
tmplib.reset(new QTemporaryFile(QTest::currentDataTag() + QString(".XXXXXX" SUFFIX)));
QVERIFY2(tmplib->open(), qPrintable(tmplib->errorString()));
// sanity-check
QByteArray magic = QByteArray::fromRawData(reinterpret_cast<const char *>(srcdata), SELFMAG);
QCOMPARE(magic, QByteArray(ELFMAG));
// copy everything via mmap()
QVERIFY2(tmplib->resize(srcsize), qPrintable(tmplib->errorString()));
uchar *dstdata = tmplib->map(0, srcsize);
memcpy(dstdata, srcdata, srcsize);
// now patch the file
patcher.f(reinterpret_cast<ElfHeader *>(dstdata), tmplib.get());
ok = true;
}();
if (!ok)
tmplib.reset();
return tmplib;
}
// All ELF systems are expected to support GCC expression statements
#define patchElf(source, patcher) __extension__({ \
auto r = patchElf(source, patcher); \
if (QTest::currentTestFailed()) return; \
std::move(r); \
})
#endif
static QString sys_qualifiedLibraryName(const QString &fileName) static QString sys_qualifiedLibraryName(const QString &fileName)
{ {
QString name = QLatin1String("bin/") + QLatin1String(PREFIX) + fileName + QLatin1String(SUFFIX); QString name = QLatin1String("bin/") + QLatin1String(PREFIX) + fileName + QLatin1String(SUFFIX);
@ -111,13 +192,13 @@ private slots:
void errorString(); void errorString();
void loadHints(); void loadHints();
void deleteinstanceOnUnload(); void deleteinstanceOnUnload();
#if defined (__ELF__)
void loadDebugObj(); void loadDebugObj();
void loadCorruptElf_data();
void loadCorruptElf(); void loadCorruptElf();
#endif
void loadMachO_data(); void loadMachO_data();
void loadMachO(); void loadMachO();
#if defined (Q_OS_UNIX)
void loadGarbage();
#endif
void relativePath(); void relativePath();
void absolutePath(); void absolutePath();
void reloadPlugin(); void reloadPlugin();
@ -289,48 +370,253 @@ void tst_QPluginLoader::deleteinstanceOnUnload()
} }
} }
#if defined (__ELF__)
void tst_QPluginLoader::loadDebugObj() void tst_QPluginLoader::loadDebugObj()
{ {
#if !defined(QT_SHARED) #if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif #endif
#if defined (__ELF__)
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/debugobj.so"))); QVERIFY(QFile::exists(QFINDTESTDATA("elftest/debugobj.so")));
QPluginLoader lib1(QFINDTESTDATA("elftest/debugobj.so")); QPluginLoader lib1(QFINDTESTDATA("elftest/debugobj.so"));
QCOMPARE(lib1.load(), false); QCOMPARE(lib1.load(), false);
#endif
} }
void tst_QPluginLoader::loadCorruptElf() void tst_QPluginLoader::loadCorruptElf_data()
{ {
#if !defined(QT_SHARED) #if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds"); QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif #endif
#if defined (__ELF__) QTest::addColumn<QString>("snippet");
if (sizeof(void*) == 8) { QTest::addColumn<ElfPatcher>("patcher");
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/corrupt1.elf64.so"))); auto newRow = [](const char *rowname, QString &&snippet, auto patcher) {
QTest::newRow(rowname) << std::move(snippet) << ElfPatcher::fromLambda(patcher);
};
QPluginLoader lib1(QFINDTESTDATA("elftest/corrupt1.elf64.so")); using H = ElfHeader *; // because I'm lazy
QCOMPARE(lib1.load(), false); newRow("not-elf", "invalid signature", [](H h) {
QVERIFY2(lib1.errorString().contains("not an ELF object"), qPrintable(lib1.errorString())); h->e_ident[EI_MAG0] = 'Q';
h->e_ident[EI_MAG1] = 't';
});
QPluginLoader lib2(QFINDTESTDATA("elftest/corrupt2.elf64.so")); newRow("wrong-word-size", "file is for a different word size", [](H h) {
QCOMPARE(lib2.load(), false); h->e_ident[EI_CLASS] = sizeof(void *) == 8 ? ELFCLASS32 : ELFCLASS64;
QVERIFY2(lib2.errorString().contains("invalid"), qPrintable(lib2.errorString()));
QPluginLoader lib3(QFINDTESTDATA("elftest/corrupt3.elf64.so")); // unnecessary, but we're doing it anyway
QCOMPARE(lib3.load(), false); # ifdef _LP64
QVERIFY2(lib3.errorString().contains("invalid"), qPrintable(lib3.errorString())); Elf32_Ehdr o;
} else if (sizeof(void*) == 4) { o.e_phentsize = sizeof(Elf32_Phdr);
QPluginLoader libW(QFINDTESTDATA("elftest/corrupt3.elf64.so")); o.e_shentsize = sizeof(Elf32_Shdr);
QCOMPARE(libW.load(), false); # else
QVERIFY2(libW.errorString().contains("architecture"), qPrintable(libW.errorString())); Elf64_Ehdr o;
} else { o.e_phentsize = sizeof(Elf64_Phdr);
QFAIL("Please port QElfParser to this platform or blacklist this test."); o.e_shentsize = sizeof(Elf64_Shdr);
# endif
memcpy(o.e_ident, h->e_ident, EI_NIDENT);
o.e_type = h->e_type;
o.e_machine = h->e_machine;
o.e_version = h->e_version;
o.e_entry = h->e_entry;
o.e_phoff = h->e_phoff;
o.e_shoff = h->e_shoff;
o.e_flags = h->e_flags;
o.e_ehsize = sizeof(o);
o.e_phnum = h->e_phnum;
o.e_shnum = h->e_shnum;
o.e_shstrndx = h->e_shstrndx;
memcpy(h, &o, sizeof(o));
});
newRow("invalid-word-size", "file is for a different word size", [](H h) {
h->e_ident[EI_CLASS] = ELFCLASSNONE;;
});
newRow("unknown-word-size", "file is for a different word size", [](H h) {
h->e_ident[EI_CLASS] |= 0x40;
});
newRow("wrong-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] = QSysInfo::ByteOrder == QSysInfo::LittleEndian ? ELFDATA2MSB : ELFDATA2LSB;
// unnecessary, but we're doing it anyway
h->e_type = qbswap(h->e_type);
h->e_machine = qbswap(h->e_machine);
h->e_version = qbswap(h->e_version);
h->e_entry = qbswap(h->e_entry);
h->e_phoff = qbswap(h->e_phoff);
h->e_shoff = qbswap(h->e_shoff);
h->e_flags = qbswap(h->e_flags);
h->e_ehsize = qbswap(h->e_ehsize);
h->e_phnum = qbswap(h->e_phnum);
h->e_phentsize = qbswap(h->e_phentsize);
h->e_shnum = qbswap(h->e_shnum);
h->e_shentsize = qbswap(h->e_shentsize);
h->e_shstrndx = qbswap(h->e_shstrndx);
});
newRow("invalid-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] = ELFDATANONE;
});
newRow("unknown-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] |= 0x40;
});
newRow("elf-version-0", "file has an unknown ELF version", [](H h) {
--h->e_ident[EI_VERSION];
});
newRow("elf-version-2", "file has an unknown ELF version", [](H h) {
++h->e_ident[EI_VERSION];
});
newRow("executable", "file is not a shared object", [](H h) {
h->e_type = ET_EXEC;
});
newRow("relocatable", "file is not a shared object", [](H h) {
h->e_type = ET_REL;
});
newRow("core-file", "file is not a shared object", [](H h) {
h->e_type = ET_CORE;
});
newRow("invalid-type", "file is not a shared object", [](H h) {
h->e_type |= 0x100;
});
newRow("wrong-arch", "file is for a different processor", [](H h) {
// could just ++h->e_machine...
# if defined(Q_PROCESSOR_X86_64)
h->e_machine = EM_AARCH64;
# elif defined(Q_PROCESSOR_ARM_64)
h->e_machine = EM_X86_64;
# elif defined(Q_PROCESSOR_X86_32)
h->e_machine = EM_ARM;
# elif defined(Q_PROCESSOR_ARM)
h->e_machine = EM_386;
# elif defined(Q_PROCESSOR_MIPS_64)
h->e_machine = EM_PPC64;
# elif defined(Q_PROCESSOR_MIPS_32)
h->e_machine = EM_PPC;
# elif defined(Q_PROCESSOR_POWER_64)
h->e_machine = EM_S390;
# elif defined(Q_PROCESSOR_POWER_32)
h->e_machine = EM_MIPS;
# endif
});
newRow("file-version-0", "file has an unknown ELF version", [](H h) {
--h->e_version;
});
newRow("file-version-2", "file has an unknown ELF version", [](H h) {
++h->e_version;
});
newRow("section-entry-size-zero", "unexpected section entry size", [](H h) {
h->e_shentsize = 0;
});
newRow("section-entry-small", "unexpected section entry size", [](H h) {
h->e_shentsize = alignof(ElfShdr);
});
newRow("section-entry-misaligned", "unexpected section entry size", [](H h) {
++h->e_shentsize;
});
newRow("no-sections", "is not a Qt plugin (.qtmetadata section not found)", [](H h){
h->e_shnum = h->e_shoff = h->e_shstrndx = 0;
});
// section table tests
newRow("section-table-starts-past-eof", "section table extends past the end of the file",
[](H h, QFile *f) {
h->e_shoff = f->size();
});
newRow("section-table-ends-past-eof", "section table extends past the end of the file",
[](H h, QFile *f) {
h->e_shoff = f->size() + 1 - h->e_shentsize * h->e_shnum;
});
static auto getSection = +[](H h, int index) {
auto sections = reinterpret_cast<ElfShdr *>(h->e_shoff + reinterpret_cast<uchar *>(h));
return sections + index;
};
// arbitrary section bounds checks
// section index = 0 is usually a NULL section, so we try 1
newRow("section1-starts-past-eof", "a section data extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, 1);
s->sh_offset = f->size();
});
newRow("section1-ends-past-eof", "a section data extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, 1);
s->sh_size = f->size() + 1 - s->sh_offset;
});
newRow("section1-bounds-overflow", "a section data extends past the end of the file", [](H h) {
ElfShdr *s = getSection(h, 1);
s->sh_size = -sizeof(*s);
});
// section header string table tests
newRow("shstrndx-invalid", "e_shstrndx greater than the number of sections", [](H h) {
h->e_shstrndx = h->e_shnum;
});
newRow("shstrtab-starts-past-eof", "section header string table extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_offset = f->size();
});
newRow("shstrtab-ends-past-eof", "section header string table extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_size = f->size() + 1 - s->sh_offset;
});
newRow("shstrtab-bounds-overflow", "section header string table extends past the end of the file", [](H h) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_size = -sizeof(*s);
});
newRow("section-name-past-eof", "section name extends past the end of the file", [](H h, QFile *f) {
ElfShdr *section1 = getSection(h, 1);
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
section1->sh_name = f->size() - shstrtab->sh_offset;
});
newRow("section-name-past-end-of-shstrtab", "section name extends past the end of the file", [](H h) {
ElfShdr *section1 = getSection(h, 1);
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
section1->sh_name = shstrtab->sh_size;
});
newRow("debug-symbols", ".qtmetadata section not found", [](H h) {
// attempt to make it look like extracted debug info
for (int i = 1; i < h->e_shnum; ++i) {
ElfShdr *s = getSection(h, i);
if (s->sh_type == SHT_NOBITS)
break;
if (s->sh_type != SHT_NOTE && s->sh_flags & SHF_ALLOC)
s->sh_type = SHT_NOBITS;
} }
#endif });
// we don't know which section is .qtmetadata, so we just apply to all of them
static auto applyToAllSectionFlags = +[](H h, int flag) {
for (int i = 0; i < h->e_shnum; ++i)
getSection(h, i)->sh_flags |= flag;
};
newRow("qtmetadata-executable", ".qtmetadata section is executable", [](H h) {
applyToAllSectionFlags(h, SHF_EXECINSTR);
});
newRow("qtmetadata-writable", ".qtmetadata section is writable", [](H h) {
applyToAllSectionFlags(h, SHF_WRITE);
});
} }
void tst_QPluginLoader::loadCorruptElf()
{
QFETCH(QString, snippet);
QFETCH(ElfPatcher, patcher);
std::unique_ptr<QTemporaryFile> tmplib =
patchElf(sys_qualifiedLibraryName("theplugin"), patcher);
QPluginLoader lib(tmplib->fileName());
QVERIFY(!lib.load());
QVERIFY2(lib.errorString().contains(snippet), qPrintable(lib.errorString()));
}
#endif // __ELF__
void tst_QPluginLoader::loadMachO_data() void tst_QPluginLoader::loadMachO_data()
{ {
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O) #if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
@ -404,21 +690,6 @@ void tst_QPluginLoader::loadMachO()
#endif #endif
} }
#if defined (Q_OS_UNIX)
void tst_QPluginLoader::loadGarbage()
{
#if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif
for (int i=0; i<5; i++) {
const QString name = QLatin1String("elftest/garbage") + QString::number(i + 1) + QLatin1String(".so");
QPluginLoader lib(QFINDTESTDATA(name));
QCOMPARE(lib.load(), false);
QVERIFY(lib.errorString() != QString("Unknown error"));
}
}
#endif
void tst_QPluginLoader::relativePath() void tst_QPluginLoader::relativePath()
{ {
#if !defined(QT_SHARED) #if !defined(QT_SHARED)