mirror of
https://github.com/luanti-org/luanti.git
synced 2025-10-23 20:55:43 +02:00
Fix overly strict bounds check in tiniergltf (#16590)
This makes sure that models exported by Goxel are not falsely rejected. It applies to exporters using strides more broadly. A workaround is to add padding to the buffer and buffer view.
This commit is contained in:
@@ -31,6 +31,22 @@ static inline void check(bool cond) {
|
|||||||
throw std::runtime_error("invalid glTF");
|
throw std::runtime_error("invalid glTF");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unsigned arithmetic helpers with wraparound checks
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline T checkedAdd(T a, T b) {
|
||||||
|
T c = std::numeric_limits<T>::max() - a;
|
||||||
|
check(b <= c);
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static inline T checkedMul(T a, T b) {
|
||||||
|
T prod = a * b;
|
||||||
|
check(a == 0 || prod / a == b);
|
||||||
|
return prod;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline void checkIndex(const std::optional<std::vector<T>> &vec,
|
static inline void checkIndex(const std::optional<std::vector<T>> &vec,
|
||||||
const std::optional<std::size_t> &i) {
|
const std::optional<std::size_t> &i) {
|
||||||
@@ -1241,10 +1257,8 @@ struct GlTF {
|
|||||||
checkForall(bufferViews, [&](const BufferView &view) {
|
checkForall(bufferViews, [&](const BufferView &view) {
|
||||||
check(buffers.has_value());
|
check(buffers.has_value());
|
||||||
const Buffer &buf = buffers->at(view.buffer);
|
const Buffer &buf = buffers->at(view.buffer);
|
||||||
// Be careful because of possible integer overflows.
|
// View must fit into the buffer
|
||||||
check(view.byteOffset < buf.byteLength);
|
check(checkedAdd(view.byteOffset, view.byteLength) <= buf.byteLength);
|
||||||
check(view.byteLength <= buf.byteLength);
|
|
||||||
check(view.byteOffset <= buf.byteLength - view.byteLength);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto checkAccessor = [&](const auto &accessor,
|
const auto checkAccessor = [&](const auto &accessor,
|
||||||
@@ -1252,10 +1266,13 @@ struct GlTF {
|
|||||||
const BufferView &view = bufferViews->at(bufferView);
|
const BufferView &view = bufferViews->at(bufferView);
|
||||||
if (view.byteStride.has_value())
|
if (view.byteStride.has_value())
|
||||||
check(*view.byteStride % accessor.componentSize() == 0);
|
check(*view.byteStride % accessor.componentSize() == 0);
|
||||||
check(byteOffset < view.byteLength);
|
|
||||||
// Use division to avoid overflows.
|
const std::size_t effective_byte_stride = view.byteStride.value_or(accessor.elementSize());
|
||||||
const auto effective_byte_stride = view.byteStride.value_or(accessor.elementSize());
|
// Accessor must fit into the buffer view: The last element must be fully in bounds.
|
||||||
check(count <= (view.byteLength - byteOffset) / effective_byte_stride);
|
// Want: (count-1) * effective_byte_stride + accessor.elementSize() + byteOffset <= view.byteLength
|
||||||
|
// Be careful to avoid wraparound.
|
||||||
|
check(checkedAdd(checkedMul(count - 1, effective_byte_stride),
|
||||||
|
checkedAdd(accessor.elementSize(), byteOffset)) <= view.byteLength);
|
||||||
};
|
};
|
||||||
checkForall(accessors, [&](const Accessor &accessor) {
|
checkForall(accessors, [&](const Accessor &accessor) {
|
||||||
if (accessor.bufferView.has_value())
|
if (accessor.bufferView.has_value())
|
||||||
|
Reference in New Issue
Block a user