Reach QTEE: credentials, client env and the app loader, with no QCBOR

fingerprintd's own code now talks to QTEE. On the phone:

    root object on /dev/tee0
    client env obtained (uid 0, 13-byte credentials)
    QSEECOM-compat app loader (UID 122) opened

The credentials object is ours rather than libqcomtee's. Upstream's exists only
to build a thirteen-byte CBOR map and drags in QCBOR to do it, so
packaging/make-libqcomtee.sh compiles the two sources that matter and drops
credentials_obj.c entirely -- nothing else references it, and the library then
has no dependency beyond libc. The map is built in Fingerprintd:Tee where it is
pinned byte-for-byte against the string verified on-device, and the object's
two-op read protocol is served here.

Three interop details, all of which cost a build cycle:

  * libqcomtee's headers carry no extern "C" guard, having only ever been
    consumed from C, so everything came out C++-mangled. They also pull in
    <stdatomic.h> and <stdio.h>, which under libc++ drag in templates that may
    not appear inside extern "C" -- so those are included first.
  * tee_call_t's second parameter is unsigned long on glibc and int on musl.
    The native build is glibc and the phone is musl; both forms are compiled.
  * On the callback path a UBUF_OUTPUT param arrives with addr = NULL. The
    dispatcher supplies the buffer, so a handler POINTS the param at its own
    storage rather than writing through the incoming address. Doing the latter
    is a null dereference that takes the supplicant thread with it, which is
    how the first run against real QTEE ended -- with the correct behaviour
    already spelled out in the module comment above the code that ignored it.
    That comment now says so in as many words.
This commit is contained in:
Jorijn van der Graaf 2026-09-02 18:02:28 +02:00
commit a91fb2ff58
6 changed files with 708 additions and 25 deletions

View file

@ -8,6 +8,34 @@ import Crafter.Build;
namespace fs = std::filesystem;
using namespace Crafter;
// libqcomtee — Qualcomm's BSD-3 userspace client for QTEE, built by
// packaging/make-libqcomtee.sh into a per-target cache dir. It is not vendored
// here: it is upstream code we pin, and the script builds it WITHOUT QCBOR
// (see the script for why that dependency is avoidable).
static void ApplyQcomteeFlags(Configuration& cfg) {
fs::path base = fs::path(std::getenv("HOME") ? std::getenv("HOME") : ".")
/ ".cache" / "fingerprintd";
// make-libqcomtee.sh names its output dir after --target, and plain
// "libqcomtee" when built for the host. cfg.target is always populated
// (it defaults to the host triple), so try the target-specific dir first
// and fall back to the host one.
std::error_code ec;
fs::path root = base / ("libqcomtee-" + cfg.target);
if (!fs::exists(root / "libqcomtee.a", ec))
root = base / "libqcomtee";
if (!fs::exists(root / "libqcomtee.a", ec)) {
std::println(std::cerr,
"libqcomtee not built for '{}'. Run:\n"
" packaging/make-libqcomtee.sh{}{}",
cfg.target.empty() ? std::string("native") : cfg.target,
cfg.target.empty() ? std::string() : " --target=" + cfg.target,
cfg.sysroot.empty() ? std::string() : " --sysroot=" + cfg.sysroot);
}
cfg.compileFlags.push_back("-I" + (root / "include").string());
cfg.linkFlags.push_back((root / "libqcomtee.a").string());
}
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
// fingerprintd-core — the wire formats and state machines as a static
// library of pure C++ modules. Deliberately free of GLib, libqcomtee and
@ -21,13 +49,14 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
ApplyStandardArgs(*Core, args);
Core->type = ConfigurationType::LibraryStatic;
{
std::array<fs::path, 6> ifaces = {
std::array<fs::path, 7> ifaces = {
"interfaces/Fingerprintd",
"interfaces/Fingerprintd-Sfs",
"interfaces/Fingerprintd-Rpmb",
"interfaces/Fingerprintd-Ta",
"interfaces/Fingerprintd-Engine",
"interfaces/Fingerprintd-Store",
"interfaces/Fingerprintd-Tee",
};
std::array<fs::path, 0> impls = {};
Core->GetInterfacesAndImplementations(ifaces, impls);
@ -48,11 +77,15 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
cfg.GetInterfacesAndImplementations(ifaces, impls);
}
ApplyQcomteeFlags(cfg);
cfg.linkFlags.push_back("-lpthread"); // the supplicant thread
cfg.AddTest("Sfs").Dependencies({ Core.get() });
cfg.AddTest("Rpmb").Dependencies({ Core.get() });
cfg.AddTest("Ta").Dependencies({ Core.get() });
cfg.AddTest("Engine").Dependencies({ Core.get() });
cfg.AddTest("Store").Dependencies({ Core.get() });
cfg.AddTest("Tee").Dependencies({ Core.get() });
ProjectLint::AddProjectLintRules(cfg);