fix use-after-free dereference risk

Texture handles were resolved to pointers when updating a SamplerGroup,
as that point the handle was checked for use-after-free. However, the
texture could be destroyed later while still active in the SamplerGroup,
this would result in using the pointer which now contains garbage.

We now keep the handle and resolve the texture when binding samplers
to the program; which will also perform the use-after-free check.
This commit is contained in:
Mathias Agopian
2024-06-24 16:16:12 -07:00
committed by Mathias Agopian
parent 2c1044e812
commit fe743523bf
4 changed files with 31 additions and 10 deletions
@@ -181,6 +181,18 @@ public:
return static_cast<Dp>(p);
}
template<typename B>
bool is_valid(Handle<B>& handle) {
if (handle && isPoolHandle(handle.getId())) {
auto [p, tag] = handleToPointer(handle.getId());
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
return expectedAge == age;
}
return true;
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> &&
+4 -4
View File
@@ -2336,9 +2336,9 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
auto const* const pSamplers = (SamplerDescriptor const*)data.buffer;
for (size_t i = 0, c = sb->textureUnitEntries.size(); i < c; i++) {
GLuint samplerId = 0u;
const GLTexture* t = nullptr;
if (UTILS_LIKELY(pSamplers[i].t)) {
t = handle_cast<const GLTexture*>(pSamplers[i].t);
Handle<HwTexture> th = pSamplers[i].t;
if (UTILS_LIKELY(th)) {
GLTexture const* const t = handle_cast<const GLTexture*>(th);
assert_invariant(t);
SamplerParams params = pSamplers[i].s;
@@ -2394,7 +2394,7 @@ void OpenGLDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
// which is not an error.
}
sb->textureUnitEntries[i] = { t, samplerId };
sb->textureUnitEntries[i] = { th, samplerId };
}
scheduleDestroy(std::move(data));
}
+6 -1
View File
@@ -126,7 +126,7 @@ public:
struct GLSamplerGroup : public HwSamplerGroup {
using HwSamplerGroup::HwSamplerGroup;
struct Entry {
GLTexture const* texture = nullptr;
Handle<HwTexture> th;
GLuint sampler = 0u;
};
utils::FixedCapacityVector<Entry> textureUnitEntries;
@@ -256,6 +256,11 @@ private:
return mHandleAllocator.handle_cast<Dp, B>(handle);
}
template<typename B>
bool is_valid(Handle<B>& handle) {
return mHandleAllocator.is_valid(handle);
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> &&
@@ -20,21 +20,24 @@
#include "OpenGLDriver.h"
#include "ShaderCompilerService.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/Handle.h>
#include <private/backend/BackendUtils.h>
#include <utils/debug.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/Systrace.h>
#include <algorithm>
#include <array>
#include <string_view>
#include <utility>
#include <new>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
@@ -241,8 +244,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept {
assert_invariant(sb);
if (!sb) continue; // should never happen, this would be a user error.
for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here
const GLTexture* const t = sb->textureUnitEntries[j].texture;
if (t) { // program may not use all samplers of sampler group
Handle<HwTexture> th = sb->textureUnitEntries[j].th;
if (th) { // program may not use all samplers of sampler group
GLTexture const* const t = gld->handle_cast<GLTexture const*>(th);
gld->bindTexture(tmu, t);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_LIKELY(!es2)) {