CVE-2026-58854 AI-generated analysis
platform/frameworks/av, services/tuner (mediatuner daemon, AIDL and HIDL tuner wrappers)
Adds isRemote() (and, where missing, nullptr) validation before static_cast downcasts of caller-supplied ITunerFilter/ITunerDemux/ITunerLnb interface pointers in the mediatuner service, across both the AIDL and HIDL backends, rejecting remote binder proxies with Result::INVALID_ARGUMENT.
Root cause
Several mediatuner service methods accept an interface object from the binder caller (shared_ptr<ITunerFilter>, shared_ptr<ITunerDemux>, shared_ptr<ITunerLnb>) and static_cast it to the service-local implementation class (TunerFilter, TunerDemux, TunerLnb, or the TunerHidl* equivalents) to call getHalFilter() or getId(). When the caller passes a remote binder proxy or custom stub, the object is not of the local class, so the cast is invalid and, per the commit message, member reads go out-of-bounds relative to the proxy allocation. Before the fix, TunerDvr::attachFilter/detachFilter, TunerFilter::setDataSource, TunerFrontend::setLnb and their HIDL twins only checked for nullptr; TunerDemux::getAvSyncHwId and TunerDescrambler::setDemuxSource (and HIDL twins) had no check at all; TunerDescrambler::addPid/removePid only handled the nullptr-optional case.
How the fix works
- Single commit (f8520e699d18 on android-security-14.0.0_r29) covering 10 files; each hunk inserts a guard that returns ::ndk::ScopedAStatus::fromServiceSpecificError(Result::INVALID_ARGUMENT) before the static_cast.
- services/tuner/TunerDemux.cpp:TunerDemux::getAvSyncHwId and services/tuner/hidl/TunerHidlDemux.cpp:TunerHidlDemux::getAvSyncHwId: new check `tunerFilter == nullptr || tunerFilter->isRemote()` (no check existed before). TunerHidlDemux.cpp also gains `#include <aidl/android/hardware/tv/tuner/Result.h>` and `using ::aidl::android::hardware::tv::tuner::Result;`.
- services/tuner/TunerDescrambler.cpp and services/tuner/hidl/TunerHidlDescrambler.cpp: setDemuxSource gains `in_tunerDemux == nullptr || in_tunerDemux->isRemote()`; addPid and removePid gain `in_optionalSourceFilter != nullptr && in_optionalSourceFilter->isRemote()` so that the optional-null case still passes through as a nullptr halFilter.
- services/tuner/TunerDvr.cpp and services/tuner/hidl/TunerHidlDvr.cpp: attachFilter and detachFilter extend the existing `in_filter == nullptr` test to `|| in_filter->isRemote()`.
- services/tuner/TunerFilter.cpp and services/tuner/hidl/TunerHidlFilter.cpp: setDataSource extends `filter == nullptr` to `|| filter->isRemote()` (under mLock, before the mShared check).
- services/tuner/TunerFrontend.cpp and services/tuner/hidl/TunerHidlFrontend.cpp: setLnb extends `lnb == nullptr` to `|| lnb->isRemote()`.
- Rationale: in NDK AIDL, an object implemented in-process reports isRemote() false while a binder object from another process is a proxy reporting isRemote() true (general platform knowledge); the fix uses this to reject proxies before the downcast.
Completeness
- Covers the static_cast downcasts of caller-supplied interfaces visible in the diff and context: getAvSyncHwId, setDemuxSource, addPid, removePid, attachFilter, detachFilter, setDataSource, setLnb, in both the AIDL (Tuner*) and HIDL (TunerHidl*) backends.
- Also closes a null dereference on TunerDemux::getAvSyncHwId / TunerHidlDemux::getAvSyncHwId and TunerDescrambler::setDemuxSource / TunerHidlDescrambler::setDemuxSource, which previously cast and called a member function without any nullptr check.
- The guard only distinguishes local vs remote; it does not verify that a local object is of the specific expected class. The context shows the service creating its local objects via SharedRefBase::make (TunerDemux::openFilter, openTimeFilter, openDvr), but does not show every creation site.
- Other methods on the same interfaces that take interface arguments are not shown in the material (e.g. ITunerDemux::setFrontendDataSource(ITunerFrontend) that DemuxClient.cpp calls per index search); the context only shows setFrontendDataSourceById(int), so whether a sibling downcast exists there cannot be judged from the material.
Behavior changes
- Passing a remote ITunerFilter/ITunerDemux/ITunerLnb object to the listed methods now returns service-specific error Result::INVALID_ARGUMENT instead of proceeding.
- TunerDemux::getAvSyncHwId and TunerDescrambler::setDemuxSource (AIDL and HIDL) now return Result::INVALID_ARGUMENT for a null argument instead of dereferencing a null pointer.
- addPid/removePid keep accepting a null optional source filter; only non-null remote objects are newly rejected.
- Legitimate framework clients appear to pass back objects obtained from the service (per index search: Tuner.java -> JNI -> FilterClient, and DemuxClient using getAidlFrontend()), so no regression is expected for them; this is an inference, not tested in the material.
Per major
- Android 14: same fix: platform/frameworks/av f8520e699d18 in android-security-14.0.0_r29, 10 files; 8/8 patched lines present at android-security-14.0.0_r30.
- Android 15: same fix: 7ccb7033d428 in android-security-15.0.0_r17, identical hunks (AIDL file line offsets differ by +2); 8/8 present at android-security-15.0.0_r18.
- Android 16: same fix: 183c2e3a4198 in android-security-16.0.0_r8, identical hunks; 8/8 present at android-security-16.0.0_r9.
- Android 17: same fix: 073ae498a43f in android-security-17.0.0_r1, identical hunks; 8/8 present at android-security-17.0.0_r2.
Unknowns
- Whether other interface-argument methods in services/tuner (for example ITunerDemux::setFrontendDataSource(ITunerFrontend) invoked by frameworks/base DemuxClient.cpp per index search) perform a similar downcast; their implementations are not in the material.
- Which specific member reads in the proxy allocation were reported in the bug; the commit message only states out-of-bounds reads relative to the proxy allocation.
- The exact binder access policy that lets unprivileged apps reach mediatuner is not shown; only asserted by the commit message.
- Under which device configurations (tuner HAL presence) mediatuner runs is not shown in the material.