public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/assimp] f44: Add fixes for CVE-2026-14610, CVE-2026-19968, CVE-2026-19999, CVE-2026-14604, CVE-2025-15666
@ 2026-09-18 21:39 Sandro Mani
0 siblings, 0 replies; only message in thread
From: Sandro Mani @ 2026-09-18 21:39 UTC (permalink / raw)
To: git-commits
A new commit has been pushed.
Repo : rpms/assimp
Branch : f44
Commit : 15d8e33f5f4eb4689c74f59aa8c06637bb319542
Author : Sandro Mani <manisandro@gmail.com>
Date : 2026-09-18T23:38:20+02:00
Stats : +728/-16 in 11 file(s)
URL : https://src.fedoraproject.org/rpms/assimp/c/15d8e33f5f4eb4689c74f59aa8c06637bb319542?branch=f44
Log:
Add fixes for CVE-2026-14610, CVE-2026-19968, CVE-2026-19999, CVE-2026-14604, CVE-2025-15666
---
diff --git a/CVE-2025-15666.patch b/CVE-2025-15666.patch
new file mode 100644
index 0000000..af1a309
--- /dev/null
+++ b/CVE-2025-15666.patch
@@ -0,0 +1,18 @@
+diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/MDL/MDLMaterialLoader.cpp assimp-6.0.5-new/code/AssetLib/MDL/MDLMaterialLoader.cpp
+--- assimp-6.0.5/code/AssetLib/MDL/MDLMaterialLoader.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/code/AssetLib/MDL/MDLMaterialLoader.cpp 2026-09-18 23:26:08.756983551 +0200
+@@ -221,6 +221,14 @@ void MDLImporter::ParseTextureColorData(
+
+ // allocate storage for the texture image
+ if (do_read) {
++ // a zero-height texture must be a compressed blob of mWidth bytes,
++ // but this path produces an mWidth*mHeight pixel array - the
++ // resulting object would violate the aiTexture contract and read
++ // out of bounds when the scene is copied (e.g. on export)
++ if (pcNew->mHeight == 0 && pcNew->mWidth != 0) {
++ throw DeadlyImportError("Invalid MDL file. A texture has zero height.");
++ }
++
+ // check for max texture sizes
+ if (pcNew->mWidth > MaxTextureSize || pcNew->mHeight > MaxTextureSize) {
+ throw DeadlyImportError("Invalid MDL file. A texture is too big.");
diff --git a/CVE-2026-14604.patch b/CVE-2026-14604.patch
new file mode 100644
index 0000000..e93fa41
--- /dev/null
+++ b/CVE-2026-14604.patch
@@ -0,0 +1,249 @@
+diff -rupN --no-dereference assimp-6.0.5/code/Common/SceneCombiner.cpp assimp-6.0.5-new/code/Common/SceneCombiner.cpp
+--- assimp-6.0.5/code/Common/SceneCombiner.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/code/Common/SceneCombiner.cpp 2026-09-18 23:26:08.380948016 +0200
+@@ -68,8 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ namespace Assimp {
+
+ #if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
+-#pragma GCC diagnostic push
+-#pragma GCC diagnostic ignored "-Wclass-memaccess"
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wclass-memaccess"
+ #endif
+
+ // ------------------------------------------------------------------------------------------------
+@@ -80,8 +80,7 @@ inline void PrefixString(aiString &strin
+ return;
+
+ if (len + string.length >= AI_MAXLEN - 1) {
+- ASSIMP_LOG_VERBOSE_DEBUG("Can't add an unique prefix because the string is too long");
+- ai_assert(false);
++ ASSIMP_LOG_ERROR("Can't add an unique prefix because the string is too long");
+ return;
+ }
+
+@@ -116,7 +115,10 @@ void SceneCombiner::AddNodeHashes(aiNode
+ // ------------------------------------------------------------------------------------------------
+ // Add a name prefix to all nodes in a hierarchy
+ void SceneCombiner::AddNodePrefixes(aiNode *node, const char *prefix, unsigned int len) {
+- ai_assert(nullptr != prefix);
++ if (prefix == nullptr) {
++ ASSIMP_LOG_ERROR("Pointer to prefix is nullptr.");
++ return;
++ }
+
+ PrefixString(node->mName, prefix, len);
+
+@@ -144,7 +146,10 @@ bool SceneCombiner::FindNameMatch(const
+ // Add a name prefix to all nodes in a hierarchy if a hash match is found
+ void SceneCombiner::AddNodePrefixesChecked(aiNode *node, const char *prefix, unsigned int len,
+ std::vector<SceneHelper> &input, unsigned int cur) {
+- ai_assert(nullptr != prefix);
++ if (prefix == nullptr) {
++ ASSIMP_LOG_ERROR("Pointer to prefix is nullptr.");
++ return;
++ }
+
+ const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length));
+
+@@ -165,8 +170,9 @@ void SceneCombiner::AddNodePrefixesCheck
+ // ------------------------------------------------------------------------------------------------
+ // Add an offset to all mesh indices in a node graph
+ void SceneCombiner::OffsetNodeMeshIndices(aiNode *node, unsigned int offset) {
+- for (unsigned int i = 0; i < node->mNumMeshes; ++i)
++ for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+ node->mMeshes[i] += offset;
++ }
+
+ for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+ OffsetNodeMeshIndices(node->mChildren[i], offset);
+@@ -177,6 +183,7 @@ void SceneCombiner::OffsetNodeMeshIndice
+ // Merges two scenes. Currently only used by the LWS loader.
+ void SceneCombiner::MergeScenes(aiScene **_dest, std::vector<aiScene *> &src, unsigned int flags) {
+ if (nullptr == _dest) {
++ ASSIMP_LOG_ERROR("Pointer to destination scene is nullptr.");
+ return;
+ }
+
+@@ -211,7 +218,7 @@ void SceneCombiner::MergeScenes(aiScene
+
+ // ------------------------------------------------------------------------------------------------
+ void SceneCombiner::AttachToGraph(aiNode *attach, std::vector<NodeAttachmentInfo> &srcList) {
+- unsigned int cnt;
++ unsigned int cnt{0};
+ for (cnt = 0; cnt < attach->mNumChildren; ++cnt) {
+ AttachToGraph(attach->mChildren[cnt], srcList);
+ }
+@@ -219,8 +226,9 @@ void SceneCombiner::AttachToGraph(aiNode
+ cnt = 0;
+ for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin();
+ it != srcList.end(); ++it) {
+- if ((*it).attachToNode == attach && !(*it).resolved)
++ if ((*it).attachToNode == attach && !(*it).resolved) {
+ ++cnt;
++ }
+ }
+
+ if (cnt) {
+@@ -314,12 +322,6 @@ void SceneCombiner::MergeScenes(aiScene
+
+ // Generate unique names for all named stuff?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
+-#if 0
+- // Construct a proper random number generator
+- boost::mt19937 rng( );
+- boost::uniform_int<> dist(1u,1 << 24u);
+- boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist);
+-#endif
+ for (unsigned int i = 1; i < src.size(); ++i) {
+ src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_", i);
+
+@@ -371,13 +373,14 @@ void SceneCombiner::MergeScenes(aiScene
+ SceneHelper *cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumTextures; ++i) {
+ if (n != duplicates[n]) {
+- if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
++ if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
+ Copy(pip, (*cur)->mTextures[i]);
+-
+- else
++ } else {
+ continue;
+- } else
++ }
++ } else {
+ *pip = (*cur)->mTextures[i];
++ }
+ ++pip;
+ }
+
+@@ -394,13 +397,14 @@ void SceneCombiner::MergeScenes(aiScene
+ SceneHelper *cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumMaterials; ++i) {
+ if (n != duplicates[n]) {
+- if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
++ if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
+ Copy(pip, (*cur)->mMaterials[i]);
+-
+- else
++ } else {
+ continue;
+- } else
++ }
++ } else {
+ *pip = (*cur)->mMaterials[i];
++ }
+
+ if ((*cur)->mNumTextures != dest->mNumTextures) {
+ // We need to update all texture indices of the mesh. So we need to search for
+@@ -457,16 +461,21 @@ void SceneCombiner::MergeScenes(aiScene
+ SceneHelper *cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) {
+ if (n != duplicates[n]) {
+- if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
++ if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
+ Copy(pip, (*cur)->mMeshes[i]);
+-
+- else
++ } else {
+ continue;
+- } else
++ }
++ } else {
+ *pip = (*cur)->mMeshes[i];
++ }
+
+ // update the material index of the mesh
+- (*pip)->mMaterialIndex += offset[n];
++ if ((*pip) != nullptr) {
++ (*pip)->mMaterialIndex += offset[n];
++ } else {
++ ASSIMP_LOG_ERROR("CopyMeshes: Missing mesh instance found, skipped.");
++ }
+ ++pip;
+ }
+
+@@ -1085,14 +1094,19 @@ void SceneCombiner::Copy(aiMesh **_dest,
+ GetArrayCopy(dest->mTangents, dest->mNumVertices);
+ GetArrayCopy(dest->mBitangents, dest->mNumVertices);
+
+- unsigned int n = 0;
+- while (dest->HasTextureCoords(n)) {
+- GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
++ // Reallocate every populated UV and color channel. The destructor frees
++ // all AI_MAX_NUMBER_OF_* channels, so the copy must own an independent
++ // buffer for each non-null one. Iterating with HasTextureCoords()/
++ // HasVertexColors() would stop at the first empty channel (leaving any
++ // later channel aliased with the source) and also skip channels of a mesh
++ // with mNumVertices == 0, which then double-frees when both meshes are
++ // destroyed.
++ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
++ GetArrayCopy(dest->mTextureCoords[n], dest->mNumVertices);
+ }
+
+- n = 0;
+- while (dest->HasVertexColors(n)) {
+- GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
++ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
++ GetArrayCopy(dest->mColors[n], dest->mNumVertices);
+ }
+
+ // make a deep copy of all bones
+diff -rupN --no-dereference assimp-6.0.5/test/unit/utSceneCombiner.cpp assimp-6.0.5-new/test/unit/utSceneCombiner.cpp
+--- assimp-6.0.5/test/unit/utSceneCombiner.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/test/unit/utSceneCombiner.cpp 2026-09-18 23:26:08.381813693 +0200
+@@ -75,3 +75,54 @@ TEST_F(utSceneCombiner, CopySceneWithNul
+ EXPECT_NO_THROW(SceneCombiner::CopyScene(nullptr, nullptr));
+ EXPECT_NO_THROW(SceneCombiner::CopySceneFlat(nullptr, nullptr));
+ }
++
++// Copying a mesh must give the copy its own UV and color buffers for every
++// populated channel, even when the channel is not the first one or when the
++// mesh reports zero vertices. Otherwise the copy aliases the source arrays and
++// destroying both double-frees them (assimp/assimp#6620).
++TEST_F(utSceneCombiner, CopyMeshDoesNotAliasSparseOrEmptyChannels) {
++ auto makeMesh = []() {
++ aiMesh *mesh = new aiMesh;
++ // A populated UV channel that is not channel 0.
++ mesh->mNumVertices = 2;
++ mesh->mVertices = new aiVector3D[2];
++ mesh->mTextureCoords[1] = new aiVector3D[2]{};
++ mesh->mNumUVComponents[1] = 2;
++ // A populated color channel that is not channel 0.
++ mesh->mColors[1] = new aiColor4D[2]{};
++ return mesh;
++ };
++
++ // Non-contiguous channels: HasTextureCoords(0) is false, so the old loop
++ // stopped before copying channel 1.
++ {
++ aiMesh *src = makeMesh();
++ aiMesh *dst = nullptr;
++ SceneCombiner::Copy(&dst, src);
++ ASSERT_NE(dst, nullptr);
++ EXPECT_NE(dst->mTextureCoords[1], src->mTextureCoords[1]);
++ EXPECT_NE(dst->mColors[1], src->mColors[1]);
++ delete dst;
++ delete src;
++ }
++
++ // A populated channel on a zero-vertex mesh: HasTextureCoords() requires
++ // mNumVertices > 0, so the old loop skipped it.
++ {
++ aiMesh *src = makeMesh();
++ src->mNumVertices = 0;
++ delete[] src->mVertices;
++ src->mVertices = nullptr;
++ // Move the populated channels to index 0 to exercise the mNumVertices
++ // guard specifically.
++ std::swap(src->mTextureCoords[0], src->mTextureCoords[1]);
++ std::swap(src->mColors[0], src->mColors[1]);
++ aiMesh *dst = nullptr;
++ SceneCombiner::Copy(&dst, src);
++ ASSERT_NE(dst, nullptr);
++ EXPECT_NE(dst->mTextureCoords[0], src->mTextureCoords[0]);
++ EXPECT_NE(dst->mColors[0], src->mColors[0]);
++ delete dst;
++ delete src;
++ }
++}
diff --git a/CVE-2026-14610.patch b/CVE-2026-14610.patch
new file mode 100644
index 0000000..0e38a30
--- /dev/null
+++ b/CVE-2026-14610.patch
@@ -0,0 +1,35 @@
+diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/CSM/CSMLoader.cpp assimp-6.0.5-new/code/AssetLib/CSM/CSMLoader.cpp
+--- assimp-6.0.5/code/AssetLib/CSM/CSMLoader.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/code/AssetLib/CSM/CSMLoader.cpp 2026-09-18 23:26:07.280504748 +0200
+@@ -175,7 +175,7 @@ void CSMImporter::InternReadFile( const
+
+ // If we know how many frames we'll read, we can preallocate some storage
+ unsigned int alloc = 100;
+- if (last != 0x00ffffff) {
++ if (last != 0x00ffffff && last > first) {
+ // re-init if the file has last frame data
+ alloc = last-first;
+ alloc += alloc>>2u; // + 25%
+diff -rupN --no-dereference assimp-6.0.5/test/models/CSM/malformed_zero_framerange.csm assimp-6.0.5-new/test/models/CSM/malformed_zero_framerange.csm
+--- assimp-6.0.5/test/models/CSM/malformed_zero_framerange.csm 1970-01-01 01:00:00.000000000 +0100
++++ assimp-6.0.5-new/test/models/CSM/malformed_zero_framerange.csm 2026-09-18 23:26:07.280928082 +0200
+@@ -0,0 +1,6 @@
++$FirstFrame 0
++$LastFrame 0
++$Order
++A
++$Points
++0 1.0 2.0 3.0
+diff -rupN --no-dereference assimp-6.0.5/test/unit/utCSMImportExport.cpp assimp-6.0.5-new/test/unit/utCSMImportExport.cpp
+--- assimp-6.0.5/test/unit/utCSMImportExport.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/test/unit/utCSMImportExport.cpp 2026-09-18 23:26:07.281108428 +0200
+@@ -58,3 +58,9 @@ public:
+ TEST_F(utCSMImportExport, importBlenFromFileTest) {
+ EXPECT_TRUE(importerTest());
+ }
++
++TEST_F(utCSMImportExport, importMalformedZeroFrameRange) {
++ Assimp::Importer importer;
++ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/CSM/malformed_zero_framerange.csm", 0);
++ EXPECT_NE(nullptr, scene);
++}
diff --git a/CVE-2026-19968.patch b/CVE-2026-19968.patch
new file mode 100644
index 0000000..13441fc
--- /dev/null
+++ b/CVE-2026-19968.patch
@@ -0,0 +1,382 @@
+diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/LWO/LWOLoader.h assimp-6.0.5-new/code/AssetLib/LWO/LWOLoader.h
+--- assimp-6.0.5/code/AssetLib/LWO/LWOLoader.h 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/code/AssetLib/LWO/LWOLoader.h 2026-09-18 23:26:07.647125475 +0200
+@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+
+ #include "LWOFileData.h"
+ #include <assimp/BaseImporter.h>
++#include <assimp/Exceptional.h>
+ #include <assimp/material.h>
+ #include <assimp/DefaultLogger.hpp>
+
+@@ -71,28 +72,10 @@ using namespace LWO;
+ // ---------------------------------------------------------------------------
+ class LWOImporter final : public BaseImporter {
+ public:
+- /**
+- * @brief The class constructor.
+- */
+ LWOImporter() = default;
+-
+- /**
+- * @brief The class destructor.
+- */
+ ~LWOImporter() override = default;
+-
+- // -------------------------------------------------------------------
+- /** Returns whether the class can handle the format of the given file.
+- * See BaseImporter::CanRead() for details.
+- */
+ bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
+ bool checkSig) const override;
+-
+- // -------------------------------------------------------------------
+- /** Called prior to ReadFile().
+- * The function is a request to the importer to update its configuration
+- * basing on the Importer's configuration property list.
+- */
+ void SetupProperties(const Importer *pImp) override;
+
+ protected:
+@@ -109,18 +92,15 @@ protected:
+
+ private:
+ // -------------------------------------------------------------------
+- /** Loads a LWO file in the older LWOB format (LW < 6)
+- */
++ /// Loads a LWO file in the older LWOB format (LW < 6)
+ void LoadLWOBFile();
+
+ // -------------------------------------------------------------------
+- /** Loads a LWO file in the newer LWO2 format (LW >= 6)
+- */
++ /// Loads a LWO file in the newer LWO2 format (LW >= 6)
+ void LoadLWO2File();
+
+ // -------------------------------------------------------------------
+- /** Parsing functions used for all file format versions
+- */
++ /// Parsing functions used for all file format versions
+ void GetS0(std::string &out, unsigned int max);
+ float GetF4();
+ float GetF8();
+@@ -130,39 +110,34 @@ private:
+ uint8_t GetU1();
+
+ // -------------------------------------------------------------------
+- /** Loads a surface chunk from an LWOB file
+- * @param size Maximum size to be read, in bytes.
+- */
++ /// Loads a surface chunk from an LWOB file
++ /// @param size Maximum size to be read, in bytes.
+ void LoadLWOBSurface(unsigned int size);
+
+ // -------------------------------------------------------------------
+- /** Loads a surface chunk from an LWO2 file
+- * @param size Maximum size to be read, in bytes.
+- */
++ /// Loads a surface chunk from an LWO2 file
++ /// @param size Maximum size to be read, in bytes.
+ void LoadLWO2Surface(unsigned int size);
+ void LoadLWO3Surface(unsigned int size);
+
+ // -------------------------------------------------------------------
+- /** Loads a texture block from a LWO2 file.
+- * @param size Maximum size to be read, in bytes.
+- * @param head Header of the SUF.BLOK header
+- */
++ /// Loads a texture block from a LWO2 file.
++ /// @param size Maximum size to be read, in bytes.
++ /// @param head Header of the SUF.BLOK header
+ void LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader *head,
+ unsigned int size);
+
+ // -------------------------------------------------------------------
+- /** Loads a shader block from a LWO2 file.
+- * @param size Maximum size to be read, in bytes.
+- * @param head Header of the SUF.BLOK header
+- */
++ /// Loads a shader block from a LWO2 file.
++ /// @param size Maximum size to be read, in bytes.
++ /// @param head Header of the SUF.BLOK header
+ void LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader *head,
+ unsigned int size);
+
+ // -------------------------------------------------------------------
+- /** Loads an image map from a LWO2 file
+- * @param size Maximum size to be read, in bytes.
+- * @param tex Texture object to be filled
+- */
++ /// Loads an image map from a LWO2 file
++ /// @param size Maximum size to be read, in bytes.
++ /// @param tex Texture object to be filled
+ void LoadLWO2ImageMap(unsigned int size, LWO::Texture &tex);
+ void LoadLWO2Gradient(unsigned int size, LWO::Texture &tex);
+ void LoadLWO2Procedural(unsigned int size, LWO::Texture &tex);
+@@ -171,48 +146,41 @@ private:
+ void LoadLWO2TextureHeader(unsigned int size, LWO::Texture &tex);
+
+ // -------------------------------------------------------------------
+- /** Loads the LWO tag list from the file
+- * @param size Maximum size to be read, in bytes.
+- */
++ /// Loads the LWO tag list from the file
++ /// @param size Maximum size to be read, in bytes.
+ void LoadLWOTags(unsigned int size);
+
+ // -------------------------------------------------------------------
+- /** Load polygons from a POLS chunk
+- * @param length Size of the chunk
+- */
++ /// Load polygons from a POLS chunk
++ /// @param length Size of the chunk
+ void LoadLWO2Polygons(unsigned int length);
+ void LoadLWOBPolygons(unsigned int length);
+
+ // -------------------------------------------------------------------
+- /** Load polygon tags from a PTAG chunk
+- * @param length Size of the chunk
+- */
++ /// Load polygon tags from a PTAG chunk
++ /// @param length Size of the chunk
+ void LoadLWO2PolygonTags(unsigned int length);
+
+ // -------------------------------------------------------------------
+- /** Load a vertex map from a VMAP/VMAD chunk
+- * @param length Size of the chunk
+- * @param perPoly Operate on per-polygon base?
+- */
++ /// Load a vertex map from a VMAP/VMAD chunk
++ /// @param length Size of the chunk
++ /// @param perPoly Operate on per-polygon base?
+ void LoadLWO2VertexMap(unsigned int length, bool perPoly);
+
+ // -------------------------------------------------------------------
+- /** Load polygons from a PNTS chunk
+- * @param length Size of the chunk
+- */
++ /// Load polygons from a PNTS chunk
++ /// @param length Size of the chunk
+ void LoadLWOPoints(unsigned int length);
+
+ // -------------------------------------------------------------------
+- /** Load a clip from a CLIP chunk
+- * @param length Size of the chunk
+- */
++ /// Load a clip from a CLIP chunk
++ /// @param length Size of the chunk
+ void LoadLWO2Clip(unsigned int length);
+ void LoadLWO3Clip(unsigned int length);
+
+ // -------------------------------------------------------------------
+- /** Load an envelope from an EVL chunk
+- * @param length Size of the chunk
+- */
++ /// Load an envelope from an EVL chunk
++ /// @param length Size of the chunk
+ void LoadLWO2Envelope(unsigned int length);
+ void LoadLWO3Envelope(unsigned int length);
+
+@@ -226,8 +194,7 @@ private:
+ void LoadNodeData(unsigned int length);
+
+ // -------------------------------------------------------------------
+- /** Count vertices and faces in a LWOB/LWO2 file
+- */
++ /// Count vertices and faces in a LWOB/LWO2 file
+ void CountVertsAndFacesLWO2(unsigned int &verts,
+ unsigned int &faces,
+ uint16_t *&cursor,
+@@ -241,8 +208,7 @@ private:
+ unsigned int max = UINT_MAX);
+
+ // -------------------------------------------------------------------
+- /** Read vertices and faces in a LWOB/LWO2 file
+- */
++ /// Read vertices and faces in a LWOB/LWO2 file
+ void CopyFaceIndicesLWO2(LWO::FaceList::iterator &it,
+ uint16_t *&cursor,
+ const uint16_t *const end);
+@@ -254,45 +220,36 @@ private:
+ unsigned int max = UINT_MAX);
+
+ // -------------------------------------------------------------------
+- /** Resolve the tag and surface lists that have been loaded.
+- * Generates the mMapping table.
+- */
++ /// Resolve the tag and surface lists that have been loaded.
++ /// Generates the mMapping table.
+ void ResolveTags();
+
+ // -------------------------------------------------------------------
+- /** Resolve the clip list that has been loaded.
+- * Replaces clip references with real clips.
+- */
++ /// Resolve the clip list that has been loaded.
++ /// Replaces clip references with real clips.
+ void ResolveClips();
+
+ // -------------------------------------------------------------------
+- /** Add a texture list to an output material description.
+- *
+- * @param pcMat Output material
+- * @param in Input texture list
+- * @param type Type identifier of the texture list
+- */
++ /// Add a texture list to an output material description.
++ /// @param pcMat Output material
++ /// @param in Input texture list
++ /// @param type Type identifier of the texture list
+ bool HandleTextures(aiMaterial *pcMat, const TextureList &in,
+ aiTextureType type);
+
+ // -------------------------------------------------------------------
+- /** Adjust a texture path
+- */
++ /// Adjust a texture path
+ void AdjustTexturePath(std::string &out);
+
+ // -------------------------------------------------------------------
+- /** Convert a LWO surface description to an ASSIMP material
+- */
++ /// Convert a LWO surface description to an ASSIMP material
+ void ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat);
+
+ // -------------------------------------------------------------------
+- /** Get a list of all UV/VC channels required by a specific surface.
+- *
+- * @param surf Working surface
+- * @param layer Working layer
+- * @param out Output list. The members are indices into the
+- * UV/VC channel lists of the layer
+- */
++ /// Get a list of all UV/VC channels required by a specific surface.
++ /// @param surf Working surface
++ /// @param layer Working layer
++ /// @param out Output list. The members are indices into the UV/VC channel lists of the layer
+ void FindUVChannels(/*const*/ LWO::Surface &surf,
+ LWO::SortedRep &sorted,
+ /*const*/ LWO::Layer &layer,
+@@ -309,57 +266,47 @@ private:
+ unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
+
+ // -------------------------------------------------------------------
+- /** Generate the final node graph
+- * Unused nodes are deleted.
+- * @param apcNodes Flat list of nodes
+- */
++ /// Generate the final node graph
++ /// Unused nodes are deleted.
++ /// @param apcNodes Flat list of nodes
+ void GenerateNodeGraph(std::map<uint16_t, aiNode *> &apcNodes);
+
+ // -------------------------------------------------------------------
+- /** Add children to a node
+- * @param node Node to become a father
+- * @param parent Index of the node
+- * @param apcNodes Flat list of nodes - used nodes are set to nullptr.
+- */
+- void AddChildren(aiNode *node, uint16_t parent,
+- std::vector<aiNode *> &apcNodes);
++ /// Add children to a node
++ /// @param node Node to become a father
++ /// @param parent Index of the node
++ /// @param apcNodes Flat list of nodes - used nodes are set to nullptr.
++ void AddChildren(aiNode *node, uint16_t parent, std::vector<aiNode *> &apcNodes);
+
+ // -------------------------------------------------------------------
+- /** Read a variable sized integer
+- * @param inout Input and output buffer
+- */
++ /// Read a variable sized integer
++ /// @param inout Input and output buffer
+ int ReadVSizedIntLWO2(uint8_t *&inout);
+
+ // -------------------------------------------------------------------
+- /** Assign a value from a VMAP to a vertex and all vertices
+- * attached to it.
+- * @param base VMAP destination data
+- * @param numRead Number of float's to be read
+- * @param idx Absolute index of the first vertex
+- * @param data Value of the VMAP to be assigned - read numRead
+- * floats from this array.
+- */
++ /// Assign a value from a VMAP to a vertex and all vertices attached to it.
++ /// @param base VMAP destination data
++ /// @param numRead Number of float's to be read
++ /// @param idx Absolute index of the first vertex
++ /// @param data Value of the VMAP to be assigned - read numRead
++ /// floats from this array.
+ void DoRecursiveVMAPAssignment(VMapEntry *base, unsigned int numRead,
+ unsigned int idx, float *data);
+
+ // -------------------------------------------------------------------
+- /** Compute normal vectors for a mesh
+- * @param mesh Input mesh
+- * @param smoothingGroups Smoothing-groups-per-face array
+- * @param surface Surface for the mesh
+- */
++ /// Compute normal vectors for a mesh
++ /// @param mesh Input mesh
++ /// @param smoothingGroups Smoothing-groups-per-face array
++ /// @param surface Surface for the mesh
+ void ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &smoothingGroups,
+ const LWO::Surface &surface);
+
+ // -------------------------------------------------------------------
+- /** Setup a new texture after the corresponding chunk was
+- * encountered in the file.
+- * @param list Texture list
+- * @param size Maximum number of bytes to be read
+- * @return Pointer to new texture
+- */
+- LWO::Texture *SetupNewTextureLWOB(LWO::TextureList &list,
+- unsigned int size);
++ /// Setup a new texture after the corresponding chunk was encountered in the file.
++ /// @param list Texture list
++ /// @param size Maximum number of bytes to be read
++ /// @return Pointer to new texture
++ LWO::Texture *SetupNewTextureLWOB(LWO::TextureList &list, unsigned int size);
+
+ private:
+ /// true if the file is a LWO2 file
+@@ -453,24 +400,24 @@ inline uint8_t LWOImporter::GetU1() {
+
+ // ------------------------------------------------------------------------------------------------
+ inline int LWOImporter::ReadVSizedIntLWO2(uint8_t *&inout) {
++ // A variable-sized index is 2 bytes long, or 4 bytes when the first byte
++ // is 0xFF. Guard every byte read against the end of the file buffer so a
++ // truncated or malformed chunk cannot trigger an out-of-bounds read.
++ auto readByte = [&]() -> int {
++ if (inout >= mFileBufferEnd) {
++ throw DeadlyImportError("LWO2: Unexpected end of file while reading a variable-sized index");
++ }
++ return *inout++;
++ };
++
+ int i;
+- int c = *inout;
+- inout++;
+- if (c != 0xFF) {
++ if (int c = readByte(); c != 0xFF) {
+ i = c << 8;
+- c = *inout;
+- inout++;
+- i |= c;
++ i |= readByte();
+ } else {
+- c = *inout;
+- inout++;
+- i = c << 16;
+- c = *inout;
+- inout++;
+- i |= c << 8;
+- c = *inout;
+- inout++;
+- i |= c;
++ i = readByte() << 16;
++ i |= readByte() << 8;
++ i |= readByte();
+ }
+ return i;
+ }
diff --git a/CVE-2026-19999.patch b/CVE-2026-19999.patch
new file mode 100644
index 0000000..ef2f818
--- /dev/null
+++ b/CVE-2026-19999.patch
@@ -0,0 +1,14 @@
+diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/MDL/MDLLoader.cpp assimp-6.0.5-new/code/AssetLib/MDL/MDLLoader.cpp
+--- assimp-6.0.5/code/AssetLib/MDL/MDLLoader.cpp 2026-04-30 11:15:09.000000000 +0200
++++ assimp-6.0.5-new/code/AssetLib/MDL/MDLLoader.cpp 2026-09-18 23:26:08.013856717 +0200
+@@ -1685,6 +1685,10 @@ void MDLImporter::ParseBoneTrafoKeys_3DG
+ // skip all frames vertices. We can't support them
+ const MDL::BoneTransform_MDL7 *pcBoneTransforms = (const MDL::BoneTransform_MDL7 *)(((const char *)frame.pcFrame) + pcHeader->frame_stc_size +
+ frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size);
++ // Confirm that the bone transformation matrices are within the file.
++ const size_t boneTransformSpan = sizeof(MDL::BoneTransform_MDL7) +
++ static_cast<size_t>(frame.pcFrame->transmatrix_count - 1u) * pcHeader->bonetrans_stc_size;
++ VALIDATE_FILE_SIZE(reinterpret_cast<const char *>(pcBoneTransforms) + boneTransformSpan);
+
+ // read all transformation matrices
+ for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count; ++iTrafo) {
diff --git a/assimp-docs.patch b/assimp-docs.patch
index 4423564..28a9679 100644
--- a/assimp-docs.patch
+++ b/assimp-docs.patch
@@ -1,6 +1,6 @@
diff -rupN --no-dereference assimp-6.0.5/doc/CMakeLists.txt assimp-6.0.5-new/doc/CMakeLists.txt
--- assimp-6.0.5/doc/CMakeLists.txt 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/doc/CMakeLists.txt 2026-05-01 23:05:57.259286432 +0200
++++ assimp-6.0.5-new/doc/CMakeLists.txt 2026-09-18 23:26:06.540784961 +0200
@@ -33,9 +33,9 @@ if( DEFINED CMAKE_INSTALL_DOCDIR )
DESTINATION ${CMAKE_INSTALL_DOCDIR}
)
@@ -16,7 +16,7 @@ diff -rupN --no-dereference assimp-6.0.5/doc/CMakeLists.txt assimp-6.0.5-new/doc
endif()
diff -rupN --no-dereference assimp-6.0.5/doc/Doxyfile.in assimp-6.0.5-new/doc/Doxyfile.in
--- assimp-6.0.5/doc/Doxyfile.in 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/doc/Doxyfile.in 2026-05-01 23:05:57.259665538 +0200
++++ assimp-6.0.5-new/doc/Doxyfile.in 2026-09-18 23:26:06.541359541 +0200
@@ -952,7 +952,7 @@ RECURSIVE = NO
# Note that relative paths are relative to the directory from which doxygen is
# run.
diff --git a/assimp-nozlib.patch b/assimp-nozlib.patch
index 58662a2..ffac19c 100644
--- a/assimp-nozlib.patch
+++ b/assimp-nozlib.patch
@@ -1,6 +1,6 @@
diff -rupN --no-dereference assimp-6.0.5/contrib/zlib/CMakeLists.txt assimp-6.0.5-new/contrib/zlib/CMakeLists.txt
--- assimp-6.0.5/contrib/zlib/CMakeLists.txt 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/contrib/zlib/CMakeLists.txt 2026-05-01 23:05:56.966699588 +0200
++++ assimp-6.0.5-new/contrib/zlib/CMakeLists.txt 2026-09-18 23:26:06.173453961 +0200
@@ -196,7 +196,7 @@ if(MINGW)
set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
endif(MINGW)
diff --git a/assimp-pythonpath.patch b/assimp-pythonpath.patch
index f5ed073..8691ab0 100644
--- a/assimp-pythonpath.patch
+++ b/assimp-pythonpath.patch
@@ -1,6 +1,6 @@
diff -rupN --no-dereference assimp-6.0.5/port/PyAssimp/pyassimp/helper.py assimp-6.0.5-new/port/PyAssimp/pyassimp/helper.py
--- assimp-6.0.5/port/PyAssimp/pyassimp/helper.py 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/port/PyAssimp/pyassimp/helper.py 2026-05-01 23:05:56.685129855 +0200
++++ assimp-6.0.5-new/port/PyAssimp/pyassimp/helper.py 2026-09-18 23:26:05.780671245 +0200
@@ -29,6 +29,7 @@ additional_dirs, ext_whitelist = [],[]
# depending on the platform we're running on.
if os.name=='posix':
diff --git a/assimp-tests.patch b/assimp-tests.patch
index 31b7dc5..4051ffd 100644
--- a/assimp-tests.patch
+++ b/assimp-tests.patch
@@ -1,6 +1,6 @@
diff -rupN --no-dereference assimp-6.0.5/CMakeLists.txt assimp-6.0.5-new/CMakeLists.txt
--- assimp-6.0.5/CMakeLists.txt 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/CMakeLists.txt 2026-05-01 23:05:57.545660372 +0200
++++ assimp-6.0.5-new/CMakeLists.txt 2026-09-18 23:26:06.905806878 +0200
@@ -823,6 +823,7 @@ IF ( ASSIMP_BUILD_SAMPLES )
ENDIF ()
@@ -10,8 +10,8 @@ diff -rupN --no-dereference assimp-6.0.5/CMakeLists.txt assimp-6.0.5-new/CMakeLi
ENDIF ()
diff -rupN --no-dereference assimp-6.0.5/test/CMakeLists.txt assimp-6.0.5-new/test/CMakeLists.txt
---- assimp-6.0.5/test/CMakeLists.txt 2026-05-01 23:05:56.680512653 +0200
-+++ assimp-6.0.5-new/test/CMakeLists.txt 2026-05-01 23:05:57.545987970 +0200
+--- assimp-6.0.5/test/CMakeLists.txt 2026-09-18 23:26:05.775095451 +0200
++++ assimp-6.0.5-new/test/CMakeLists.txt 2026-09-18 23:26:06.906991748 +0200
@@ -36,6 +36,7 @@
#
#----------------------------------------------------------------------
diff --git a/assimp-unbundle.patch b/assimp-unbundle.patch
index 2ce0ffd..581c457 100644
--- a/assimp-unbundle.patch
+++ b/assimp-unbundle.patch
@@ -1,6 +1,6 @@
diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/Blender/BlenderTessellator.h assimp-6.0.5-new/code/AssetLib/Blender/BlenderTessellator.h
--- assimp-6.0.5/code/AssetLib/Blender/BlenderTessellator.h 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/code/AssetLib/Blender/BlenderTessellator.h 2026-05-01 23:05:56.394994265 +0200
++++ assimp-6.0.5-new/code/AssetLib/Blender/BlenderTessellator.h 2026-09-18 23:26:05.393829413 +0200
@@ -143,7 +143,7 @@ namespace Assimp
#if ASSIMP_BLEND_WITH_POLY_2_TRI
@@ -12,7 +12,7 @@ diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/Blender/BlenderTessellato
{
diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/IFC/IFCGeometry.cpp assimp-6.0.5-new/code/AssetLib/IFC/IFCGeometry.cpp
--- assimp-6.0.5/code/AssetLib/IFC/IFCGeometry.cpp 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/code/AssetLib/IFC/IFCGeometry.cpp 2026-05-01 23:05:56.395488723 +0200
++++ assimp-6.0.5-new/code/AssetLib/IFC/IFCGeometry.cpp 2026-09-18 23:26:05.394977618 +0200
@@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#include "IFCUtil.h"
#include "Common/PolyTools.h"
@@ -24,7 +24,7 @@ diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/IFC/IFCGeometry.cpp assim
#include <iterator>
diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/IFC/IFCOpenings.cpp assimp-6.0.5-new/code/AssetLib/IFC/IFCOpenings.cpp
--- assimp-6.0.5/code/AssetLib/IFC/IFCOpenings.cpp 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/code/AssetLib/IFC/IFCOpenings.cpp 2026-05-01 23:05:56.395944757 +0200
++++ assimp-6.0.5-new/code/AssetLib/IFC/IFCOpenings.cpp 2026-09-18 23:26:05.395528345 +0200
@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#include "IFCUtil.h"
#include "Common/PolyTools.h"
@@ -36,7 +36,7 @@ diff -rupN --no-dereference assimp-6.0.5/code/AssetLib/IFC/IFCOpenings.cpp assim
#include <deque>
diff -rupN --no-dereference assimp-6.0.5/code/CMakeLists.txt assimp-6.0.5-new/code/CMakeLists.txt
--- assimp-6.0.5/code/CMakeLists.txt 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/code/CMakeLists.txt 2026-05-01 23:05:56.396488774 +0200
++++ assimp-6.0.5-new/code/CMakeLists.txt 2026-09-18 23:26:05.396059099 +0200
@@ -1117,13 +1117,7 @@ IF(ASSIMP_HUNTER_ENABLED)
hunter_add_package(pugixml)
find_package(pugixml CONFIG REQUIRED)
@@ -164,7 +164,7 @@ diff -rupN --no-dereference assimp-6.0.5/code/CMakeLists.txt assimp-6.0.5-new/co
if(ASSIMP_ANDROID_JNIIOSYSTEM)
diff -rupN --no-dereference assimp-6.0.5/code/PostProcessing/TriangulateProcess.cpp assimp-6.0.5-new/code/PostProcessing/TriangulateProcess.cpp
--- assimp-6.0.5/code/PostProcessing/TriangulateProcess.cpp 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/code/PostProcessing/TriangulateProcess.cpp 2026-05-01 23:05:56.396804235 +0200
++++ assimp-6.0.5-new/code/PostProcessing/TriangulateProcess.cpp 2026-09-18 23:26:05.396790193 +0200
@@ -62,7 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#include "PostProcessing/TriangulateProcess.h"
#include "PostProcessing/ProcessHelper.h"
@@ -176,7 +176,7 @@ diff -rupN --no-dereference assimp-6.0.5/code/PostProcessing/TriangulateProcess.
#include <cstdint>
diff -rupN --no-dereference assimp-6.0.5/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp assimp-6.0.5-new/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp
--- assimp-6.0.5/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp 2026-05-01 23:05:56.397018289 +0200
++++ assimp-6.0.5-new/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp 2026-09-18 23:26:05.397206421 +0200
@@ -24,7 +24,7 @@
#endif // _MSC_VER
@@ -188,7 +188,7 @@ diff -rupN --no-dereference assimp-6.0.5/samples/SimpleTexturedOpenGL/SimpleText
#pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter'
diff -rupN --no-dereference assimp-6.0.5/test/CMakeLists.txt assimp-6.0.5-new/test/CMakeLists.txt
--- assimp-6.0.5/test/CMakeLists.txt 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/test/CMakeLists.txt 2026-05-01 23:05:56.397240462 +0200
++++ assimp-6.0.5-new/test/CMakeLists.txt 2026-09-18 23:26:05.397648670 +0200
@@ -43,14 +43,6 @@ INCLUDE_DIRECTORIES(
${Assimp_SOURCE_DIR}/code
)
@@ -236,7 +236,7 @@ diff -rupN --no-dereference assimp-6.0.5/test/CMakeLists.txt assimp-6.0.5-new/te
ADD_DEFINITIONS( -DASSIMP_ENABLE_DRACO )
diff -rupN --no-dereference assimp-6.0.5/test/unit/utglTF2ImportExport.cpp assimp-6.0.5-new/test/unit/utglTF2ImportExport.cpp
--- assimp-6.0.5/test/unit/utglTF2ImportExport.cpp 2026-04-30 11:15:09.000000000 +0200
-+++ assimp-6.0.5-new/test/unit/utglTF2ImportExport.cpp 2026-05-01 23:05:56.397488826 +0200
++++ assimp-6.0.5-new/test/unit/utglTF2ImportExport.cpp 2026-09-18 23:26:05.398112178 +0200
@@ -987,7 +987,7 @@ namespace {
rapidjson::Document schemaDoc;
schemaDoc.Parse(R"==({"properties":{"scene" : { "type" : "integer" }}, "required": [ "scene" ]})==");
diff --git a/assimp.spec b/assimp.spec
index e4b5c3c..51bbe79 100644
--- a/assimp.spec
+++ b/assimp.spec
@@ -2,7 +2,7 @@
Name: assimp
Version: 6.0.5
-Release: 4%{?dist}
+Release: 5%{?dist}
Summary: Library to import various 3D model formats into applications
# Assimp is BSD
@@ -33,6 +33,16 @@ Patch2: %{name}-nozlib.patch
Patch3: %{name}-docs.patch
# Enable ctest
Patch4: %{name}-tests.patch
+# https://github.com/assimp/assimp/commit/eb84eec580d3f4ba2f0fd87409b7d0744620f11e
+Patch5: CVE-2026-14610.patch
+# https://github.com/assimp/assimp/commit/c39d8c15dbbe03174af61d8eedbbf90120f4eb9f
+Patch6: CVE-2026-19968.patch
+# https://github.com/assimp/assimp/commit/50d767984e78d51b53e2020fdf0967fd624bc377
+Patch7: CVE-2026-19999.patch
+# https://github.com/DerDoktorX/assimp/commit/a07a25d9a348152f2eb7f3359909a2cb9d0b2702
+Patch8: CVE-2026-14604.patch
+# https://github.com/assimp/assimp/pull/6869
+Patch9: CVE-2025-15666.patch
BuildRequires: boost-devel
@@ -178,6 +188,10 @@ exclude="utMD5Importer.importBoarMan|utMD5Importer.importBob|utMD2Importer.impor
%changelog
+* Fri Sep 18 2026 Sandro Mani <manisandro@gmail.com> - 6.0.5-5
+- Add fixes for CVE-2026-14610, CVE-2026-19968, CVE-2026-19999, CVE-2026-14604,
+ CVE-2025-15666
+
* Wed Jul 15 2026 Fedora Release Engineering <releng@fedoraproject.org> - 6.0.5-4
- Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-18 21:39 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-18 21:39 [rpms/assimp] f44: Add fixes for CVE-2026-14610, CVE-2026-19968, CVE-2026-19999, CVE-2026-14604, CVE-2025-15666 Sandro Mani
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox