[Git][debian-gis-team/lerc][master] 4 commits: New upstream version 4.3.0+ds

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sat Oct 31 11:04:00 GMT 2026



Antonio Valentino pushed to branch master at Debian GIS Project / lerc


Commits:
937ecd60 by Antonio Valentino at 2026-10-31T10:17:55+00:00
New upstream version 4.3.0+ds
- - - - -
28b8feda by Antonio Valentino at 2026-10-31T10:17:55+00:00
Update upstream source from tag 'upstream/4.3.0+ds'

Update to upstream version '4.3.0+ds'
with Debian dir 5c62fbc974efb6079b64c2ef9e9365f054719271
- - - - -
88a0e4d2 by Antonio Valentino at 2026-10-31T10:18:32+00:00
New upstream release

- - - - -
43c1173b by Antonio Valentino at 2026-10-31T10:23:31+00:00
Set distribution to unstable

- - - - -


26 changed files:

- + .github/workflows/build_js.yml
- .github/workflows/build_wheels.yml
- CHANGELOG.md
- CMakeLists.txt
- OtherLanguages/Python/lerc/__init__.py
- OtherLanguages/Python/setup.py
- README.md
- debian/changelog
- src/LercLib/BitMask.cpp
- src/LercLib/BitMask.h
- src/LercLib/BitStuffer2.cpp
- src/LercLib/BitStuffer2.h
- src/LercLib/Huffman.cpp
- src/LercLib/Lerc.cpp
- src/LercLib/Lerc1Decode/BitStuffer.cpp
- src/LercLib/Lerc1Decode/BitStuffer.h
- src/LercLib/Lerc1Decode/CntZImage.cpp
- src/LercLib/Lerc1Decode/CntZImage.h
- src/LercLib/Lerc2.cpp
- src/LercLib/RLE.cpp
- src/LercLib/RLE.h
- src/LercLib/fpl_Compression.cpp
- src/LercLib/fpl_EsriHuffman.cpp
- src/LercLib/fpl_EsriHuffman.h
- src/LercLib/fpl_Lerc2Ext.cpp
- src/LercLib/include/Lerc_c_api.h


Changes:

=====================================
.github/workflows/build_js.yml
=====================================
@@ -0,0 +1,43 @@
+name: Build LERC JS
+
+on:
+  push:
+    branches: [ main, master ]
+    paths:
+      - 'OtherLanguages/js/**'
+      - '.github/workflows/build_js.yml'
+  pull_request:
+    paths:
+      - 'OtherLanguages/js/**'
+      - '.github/workflows/build_js.yml'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  build_js:
+    name: Build and Test LERC JS
+    runs-on: ubuntu-latest
+    defaults:
+      run:
+        working-directory: OtherLanguages/js
+    steps:
+      - name: Checkout Code
+        uses: actions/checkout at v5
+
+      - name: Set up Node.js
+        uses: actions/setup-node at v5
+        with:
+          node-version: '24'
+          cache: npm
+          cache-dependency-path: OtherLanguages/js/package-lock.json
+
+      - name: Install Dependencies
+        run: npm ci
+
+      - name: Build JavaScript Distribution
+        run: npm run build
+
+      - name: Test UMD Distribution
+        run: npm run tests:sanity:umd
\ No newline at end of file


=====================================
.github/workflows/build_wheels.yml
=====================================
@@ -6,6 +6,8 @@ on:
     paths:
       - 'OtherLanguages/Python/**'
       - 'src/**'
+      - 'CMakeLists.txt'
+      - 'build/windows/**'
       - 'build/conda/**'
       - '.github/workflows/build_wheels.yml'
     tags:
@@ -14,6 +16,8 @@ on:
     paths:
       - 'OtherLanguages/Python/**'
       - 'src/**'
+      - 'CMakeLists.txt'
+      - 'build/windows/**'
       - 'build/conda/**'
       - '.github/workflows/build_wheels.yml'
   workflow_dispatch:
@@ -31,11 +35,11 @@ jobs:
 
     steps:
       - name: Checkout Code
-        uses: actions/checkout at v4
+        uses: actions/checkout at v5
 
       - name: Build C++ Binaries (Linux Container Compatibility)
         if: matrix.os == 'ubuntu-latest'
-        uses: addnab/docker-run-action at v3
+        uses: addnab/docker-run-action at 4f65fabd2431ebc8d299f8e5a018d79a769ae185 # v3
         with:
           image: rockylinux:8
           options: -v ${{ github.workspace }}:/workspace
@@ -79,7 +83,7 @@ jobs:
           fi
 
       - name: Set up Python
-        uses: actions/setup-python at v5
+        uses: actions/setup-python at v6
         with:
           python-version: '3.11'
 
@@ -92,6 +96,33 @@ jobs:
           python setup.py build
           python -m build --wheel
 
+      - name: Verify Windows DLL Version Info
+        if: matrix.os == 'windows-latest'
+        shell: pwsh
+        run: |
+          $version = Select-String -Path cmake_build/CMakeCache.txt -Pattern '^CMAKE_PROJECT_VERSION:STATIC=(.+)$'
+          if (-not $version) { throw "CMake project version not found" }
+          $expected = "$($version.Matches[0].Groups[1].Value).0"
+
+          $wheels = @(Get-ChildItem OtherLanguages/Python/dist -Filter '*win_amd64.whl')
+          if ($wheels.Count -ne 1) { throw "Expected exactly one Windows wheel" }
+          $unpacked = Join-Path $env:RUNNER_TEMP 'pylerc-wheel-version-check'
+          Add-Type -AssemblyName System.IO.Compression.FileSystem
+          [System.IO.Compression.ZipFile]::ExtractToDirectory($wheels[0].FullName, $unpacked)
+          $dlls = @(Get-ChildItem $unpacked -Filter Lerc.dll -Recurse)
+          if ($dlls.Count -ne 1) { throw "Expected exactly one Lerc.dll in the wheel" }
+
+          foreach ($path in @('bin/windows/Lerc.dll', $dlls[0].FullName)) {
+            $info = (Get-Item $path).VersionInfo
+            Write-Host "${path}: FileVersion=$($info.FileVersion), ProductVersion=$($info.ProductVersion)"
+            if ($info.FileVersion -ne $expected -or $info.ProductVersion -ne $expected) {
+              throw "Expected Windows DLL version $expected"
+            }
+          }
+          if ((Get-FileHash bin/windows/Lerc.dll).Hash -ne (Get-FileHash $dlls[0].FullName).Hash) {
+            throw "The wheel does not contain the staged Windows DLL"
+          }
+
       - name: Standardize Wheel Platform Labels
         shell: bash
         run: |
@@ -101,13 +132,13 @@ jobs:
           fi
 
       - name: Upload Platform Wheel Artifacts
-        uses: actions/upload-artifact at v4
+        uses: actions/upload-artifact at v6
         with:
           name: pylerc-wheels-${{ matrix.os }}
           path: OtherLanguages/Python/dist/*.whl
 
       - name: Upload Binary Snippets for Conda
-        uses: actions/upload-artifact at v4
+        uses: actions/upload-artifact at v6
         with:
           name: compiled-binaries-${{ matrix.os }}
           path: bin/
@@ -118,28 +149,28 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout Code
-        uses: actions/checkout at v4
+        uses: actions/checkout at v5
 
       - name: Download Windows Binaries
-        uses: actions/download-artifact at v4
+        uses: actions/download-artifact at v7
         with:
           name: compiled-binaries-windows-latest
           path: bin/
 
       - name: Download MacOS Binaries
-        uses: actions/download-artifact at v4
+        uses: actions/download-artifact at v7
         with:
           name: compiled-binaries-macos-latest
           path: bin/
 
       - name: Download Linux Binaries
-        uses: actions/download-artifact at v4
+        uses: actions/download-artifact at v7
         with:
           name: compiled-binaries-ubuntu-latest
           path: bin/
 
       - name: Setup Miniconda Environment
-        uses: conda-incubator/setup-miniconda at v4
+        uses: conda-incubator/setup-miniconda at be893c923ea9cf1cf7cd510fbdde27c7e18cbdcb # v4
         with:
           auto-update-conda: true
           auto-activate: true
@@ -159,7 +190,7 @@ jobs:
           conda build build/conda/lerc/ --output-folder conda-out/
 
       - name: Upload Conda Distribution Artifacts
-        uses: actions/upload-artifact at v4
+        uses: actions/upload-artifact at v6
         with:
           name: pylerc-conda-package
           path: conda-out/noarch/*.conda


=====================================
CHANGELOG.md
=====================================
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [Unreleased][unreleased]
 
+## [4.3.0](https://github.com/Esri/lerc/releases/tag/v4.3.0) - 2026-10-30
+
+* Added more security fixes. Most of them found by AI. Added or fixed byte counting for the decoder to prevent a possible buffer overrun.
+
 ## [4.2.0](https://github.com/Esri/lerc/releases/tag/v4.2.0) - 2026-07-23
 
 * Added explicit size checks for the input data volume and the output compressed binary Lerc blob. The maximum data volume to encode is 2 GB per band. The maximum size of a compressed binary Lerc blob is set also to 2 GB per band, and 4 GB over all bands. The data volume over all bands is not limited as long as it can be compressed into 4 GB or less.


=====================================
CMakeLists.txt
=====================================
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.12)
 project(Lerc
         DESCRIPTION "Limited Error Raster Compression"
         HOMEPAGE_URL "https://github.com/Esri/lerc"
-        VERSION 4.2.0) # Keep in sync with Lerc_c_api.h
+        VERSION 4.3.0) # Keep in sync with Lerc_c_api.h
 
 include(GNUInstallDirs)
 
@@ -18,6 +18,11 @@ file(GLOB SOURCES
 # Make an option, defaulting to shared libs, but allow -DBUILD_SHARED_LIBS=OFF
 option (BUILD_SHARED_LIBS "Build shared libraries (set to OFF to build static libs)" ON)
 
+if(WIN32 AND BUILD_SHARED_LIBS)
+    enable_language(RC)
+    list(APPEND SOURCES "build/windows/MS_VS2026/Lerc/Lerc.rc")
+endif()
+
 # If no SHARED or STATIC is specified explicitly, add_library will honor BUILD_SHARED_LIBS
 add_library(Lerc ${SOURCES})
 


=====================================
OtherLanguages/Python/lerc/__init__.py
=====================================
@@ -1,3 +1,3 @@
-__version__ = "4.2.0"
+__version__ = "4.3.0"
 
 from ._lerc import *


=====================================
OtherLanguages/Python/setup.py
=====================================
@@ -40,7 +40,7 @@ for platform in PLATFORMS:
 
 setuptools.setup(
     name="pylerc",
-    version="4.2.0",
+    version="4.3.0",
     author="esri",
     author_email="python at esri.com",
     description="Limited Error Raster Compression",
@@ -56,6 +56,7 @@ setuptools.setup(
         "Programming Language :: Python :: 3.12",
         "Programming Language :: Python :: 3.13",
         "Programming Language :: Python :: 3.14",
+        "Programming Language :: Python :: 3.15",
         "License :: OSI Approved :: Apache Software License",
     ],
     package_data={"lerc": BINARY_TYPES},


=====================================
README.md
=====================================
@@ -69,7 +69,7 @@ For the most common platforms you can find alternative project files under `buil
 
 #### Windows
 
-- Open `build/Windows/MS_VS2022/Lerc.sln` with Microsoft Visual Studio. 
+- Open `build/Windows/MS_VS2026/Lerc.sln` with Microsoft Visual Studio.
 - Build and run.
 
 #### Linux


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+lerc (4.3.0+ds-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Sat, 31 Oct 2026 10:23:19 +0000
+
 lerc (4.2.0+ds-1) unstable; urgency=medium
 
   * Update symbol file.


=====================================
src/LercLib/BitMask.cpp
=====================================
@@ -23,7 +23,7 @@ Contributors:  Thomas Maurer
 
 #include "Defines.h"
 #include "BitMask.h"
-#include <cstring>
+#include <new>
 
 USING_NAMESPACE_LERC
 
@@ -51,19 +51,7 @@ BitMask& BitMask::operator= (const BitMask& src)
 
 // -------------------------------------------------------------------------- ;
 
-void BitMask::SetAllValid() const
-{
-  memset(m_pBits, 255, Size());
-}
-
-void BitMask::SetAllInvalid() const
-{
-  memset(m_pBits, 0, Size());
-}
-
-// -------------------------------------------------------------------------- ;
-
-bool BitMask::SetSize(int nCols, int nRows)
+bool BitMask::SetSize(int nCols, int nRows) noexcept
 {
   if (nCols <= 0 || nRows <= 0)
   {
@@ -75,19 +63,13 @@ bool BitMask::SetSize(int nCols, int nRows)
   {
     Clear();
 
-    try
-    {
-      size_t nPix = (size_t)nCols * nRows;
-      m_pBits = new Byte[(nPix + 7) >> 3];
-    }
-    catch (...)
-    {
-      return false;
-    }
-
+    size_t nPix = (size_t)nCols * nRows;
+    m_pBits = new(std::nothrow) Byte[(nPix + 7) >> 3];
     if (!m_pBits)
       return false;
 
+    memset(m_pBits, 0, Size());  // init
+
     m_nCols = nCols;
     m_nRows = nRows;
   }


=====================================
src/LercLib/BitMask.h
=====================================
@@ -27,6 +27,7 @@ Contributors:  Thomas Maurer
 #include "Defines.h"
 #include <cstddef>
 #include <cstdint>
+#include <cstring>
 
 NAMESPACE_LERC_START
 
@@ -37,7 +38,7 @@ NAMESPACE_LERC_START
 class BitMask
 {
 public:
-  BitMask() : m_pBits(nullptr), m_nCols(0), m_nRows(0)  {}
+  BitMask() : m_pBits(nullptr), m_nCols(0), m_nRows(0) {}
   BitMask(int nCols, int nRows) : m_pBits(nullptr), m_nCols(0), m_nRows(0) { SetSize(nCols, nRows); }
   BitMask(const BitMask& src);
   ~BitMask()                                { Clear(); }
@@ -54,10 +55,10 @@ public:
   void SetInvalid(int64_t k) const          { m_pBits[k >> 3] &= ~Bit(k); }
   void SetInvalid(int row, int col) const   { SetInvalid((int64_t)row * m_nCols + col); }
 
-  void SetAllValid() const;
-  void SetAllInvalid() const;
+  void SetAllValid() const                  { memset(m_pBits, 255, Size()); }
+  void SetAllInvalid() const                { memset(m_pBits, 0, Size()); }
 
-  bool SetSize(int nCols, int nRows);
+  bool SetSize(int nCols, int nRows) noexcept;
 
   int GetWidth() const                      { return m_nCols; }
   int GetHeight() const                     { return m_nRows; }


=====================================
src/LercLib/BitStuffer2.cpp
=====================================
@@ -173,30 +173,23 @@ bool BitStuffer2::Decode(const Byte** ppByte, size_t& nBytesRemaining, vector<un
   int numBits = numBitsByte;
 
   unsigned int numElements = 0;
-  if (!DecodeUInt(ppByte, nBytesRemaining, numElements, nb))
-    return false;
-  if (numElements > maxElementCount)
+  if (!DecodeUInt(ppByte, nBytesRemaining, numElements, nb) || numElements > maxElementCount)
     return false;
 
   if (!doLut)
   {
     if (numBits > 0)    // numBits can be 0
     {
-      if (lerc2Version >= 3)
-      {
-        if (!BitUnStuff(ppByte, nBytesRemaining, dataVec, numElements, numBits))
-          return false;
-      }
-      else
-      {
-        if (!BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, dataVec, numElements, numBits))
-          return false;
-      }
+      bool rv = (lerc2Version >= 3) ? BitUnStuff(ppByte, nBytesRemaining, dataVec, numElements, numBits)
+        : BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, dataVec, numElements, numBits);
+
+      if (!rv)
+        return false;
     }
   }
   else
   {
-    if (numBits == 0)  // fail gracefully in case of corrupted blob for old version <= 2 which had no checksum
+    if (numBits == 0)
       return false;
     if (nBytesRemaining < 1)
       return false;
@@ -208,49 +201,36 @@ bool BitStuffer2::Decode(const Byte** ppByte, size_t& nBytesRemaining, vector<un
     int nLut = nLutByte - 1;
 
     // unstuff lut w/o the 0
-    if (lerc2Version >= 3)
-    {
-      if (!BitUnStuff(ppByte, nBytesRemaining, m_tmpLutVec, nLut, numBits))
-        return false;
-    }
-    else
-    {
-      if (!BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, m_tmpLutVec, nLut, numBits))
-        return false;
-    }
+    bool rv = (lerc2Version >= 3) ? BitUnStuff(ppByte, nBytesRemaining, m_tmpLutVec, nLut, numBits)
+      : BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, m_tmpLutVec, nLut, numBits);
+
+    if (!rv)
+      return false;
 
     int nBitsLut = 0;
     while (nLut >> nBitsLut)
       nBitsLut++;
+
     if (nBitsLut == 0)
       return false;
 
-    if (lerc2Version >= 3)
-    {
-      // unstuff indexes
-      if (!BitUnStuff(ppByte, nBytesRemaining, dataVec, numElements, nBitsLut))
-        return false;
+    // unstuff indexes
+    rv = (lerc2Version >= 3) ? BitUnStuff(ppByte, nBytesRemaining, dataVec, numElements, nBitsLut)
+      : BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, dataVec, numElements, nBitsLut);
 
-      // replace indexes by values
-      m_tmpLutVec.insert(m_tmpLutVec.begin(), 0);    // put back in the 0
-      for (unsigned int i = 0; i < numElements; i++)
-        dataVec[i] = m_tmpLutVec[dataVec[i]];
-    }
-    else
+    if (!rv)
+      return false;
+
+    // replace indexes by values
+    m_tmpLutVec.insert(m_tmpLutVec.begin(), 0);    // put back in the 0
+    const size_t tmpLutSize = m_tmpLutVec.size();
+
+    for (unsigned int i = 0; i < numElements; i++)
     {
-      // unstuff indexes
-      if (!BitUnStuff_Before_Lerc2v3(ppByte, nBytesRemaining, dataVec, numElements, nBitsLut))
+      if (dataVec[i] >= tmpLutSize)
         return false;
 
-      // replace indexes by values
-      m_tmpLutVec.insert(m_tmpLutVec.begin(), 0);    // put back in the 0
-      for (unsigned int i = 0; i < numElements; i++)
-      {
-        if (dataVec[i] >= m_tmpLutVec.size())
-          return false;
-
-        dataVec[i] = m_tmpLutVec[dataVec[i]];
-      }
+      dataVec[i] = m_tmpLutVec[dataVec[i]];
     }
   }
 
@@ -349,21 +329,21 @@ void BitStuffer2::BitStuff_Before_Lerc2v3(Byte** ppByte, const vector<unsigned i
 
 // -------------------------------------------------------------------------- ;
 
-bool BitStuffer2::BitUnStuff_Before_Lerc2v3(const Byte** ppByte, size_t& nBytesRemaining, 
+bool BitStuffer2::BitUnStuff_Before_Lerc2v3(const Byte** ppByte, size_t& nBytesRemaining,
     vector<unsigned int>& dataVec, unsigned int numElements, int numBits) const
 {
-  if (numElements == 0 || numBits >= 32)
+  if (numElements == 0 || numBits <= 0 || numBits >= 32)
     return false;
 
-  unsigned long long numUIntsLL = ((unsigned long long)numElements * numBits + 31) / 32;
-  unsigned long long numBytesLL = numUIntsLL * sizeof(unsigned int);
+  uint64_t numUIntsLL = ((uint64_t)numElements * numBits + 31) / 32;
+  uint64_t numBytesLL = numUIntsLL * sizeof(unsigned int);
 
   size_t numUInts = (size_t)numUIntsLL;
   size_t numBytes = (size_t)numBytesLL;    // could theoretically overflow on 32 bit system
 
   const unsigned int ntbnn = NumTailBytesNotNeeded(numElements, numBits);
 
-  if (numBytes != numBytesLL || nBytesRemaining + ntbnn < numBytes)
+  if (numBytes != numBytesLL || numBytes > nBytesRemaining + ntbnn)
     return false;
 
   try
@@ -476,11 +456,11 @@ void BitStuffer2::BitStuff(Byte** ppByte, const vector<unsigned int>& dataVec, i
 bool BitStuffer2::BitUnStuff(const Byte** ppByte, size_t& nBytesRemaining, vector<unsigned int>& dataVec,
   unsigned int numElements, int numBits) const
 {
-  if (numElements == 0 || numBits >= 32)
+  if (numElements == 0 || numBits <= 0 || numBits >= 32)
     return false;
 
-  unsigned long long numUIntsLL = ((unsigned long long)numElements * numBits + 31) / 32;
-  unsigned long long numBytesLL = numUIntsLL * sizeof(unsigned int);
+  uint64_t numUIntsLL = ((uint64_t)numElements * numBits + 31) / 32;
+  uint64_t numBytesLL = numUIntsLL * sizeof(unsigned int);
 
   size_t numUInts = (size_t)numUIntsLL;
   size_t numBytes = (size_t)numBytesLL;    // could theoretically overflow on 32 bit system


=====================================
src/LercLib/BitStuffer2.h
=====================================
@@ -26,6 +26,7 @@ Contributors:  Thomas Maurer
 
 #include <vector>
 #include <cstring>
+#include <cstdint>
 #include <utility>
 #include "Defines.h"
 
@@ -38,7 +39,7 @@ NAMESPACE_LERC_START
 class BitStuffer2
 {
 public:
-  BitStuffer2()           {}
+  BitStuffer2()  {}
   ~BitStuffer2()  {}
 
   // dst buffer is already allocated. byte ptr is moved like a file pointer.
@@ -99,7 +100,7 @@ inline bool BitStuffer2::EncodeUInt(Byte** ppByte, unsigned int k, int numBytes)
 
 inline bool BitStuffer2::DecodeUInt(const Byte** ppByte, size_t& nBytesRemaining, unsigned int& k, int numBytes)
 {
-  if (nBytesRemaining < (size_t)numBytes)
+  if (numBytes <= 0 || nBytesRemaining < (size_t)numBytes)
     return false;
 
   const Byte* ptr = *ppByte;
@@ -126,7 +127,7 @@ inline bool BitStuffer2::DecodeUInt(const Byte** ppByte, size_t& nBytesRemaining
 
 inline unsigned int BitStuffer2::NumTailBytesNotNeeded(unsigned int numElem, int numBits)
 {
-  int numBitsTail = ((unsigned long long)numElem * numBits) & 31;
+  int numBitsTail = ((uint64_t)numElem * numBits) & 31;  // numBits in [0..31]
   int numBytesTail = (numBitsTail + 7) >> 3;
   return (numBytesTail > 0) ? 4 - numBytesTail : 0;
 }


=====================================
src/LercLib/Huffman.cpp
=====================================
@@ -23,6 +23,7 @@ Contributors:  Thomas Maurer
 
 #include <algorithm>
 #include <queue>
+#include <climits>
 #include "Defines.h"
 #include "Huffman.h"
 #include "BitStuffer2.h"
@@ -91,21 +92,26 @@ bool Huffman::ComputeCompressedSize(const std::vector<int>& histo, int& numBytes
   if (!ComputeNumBytesCodeTable(numBytes))    // header and code table
     return false;
 
-  int numBits = 0, numElem = 0;
-  int size = (int)histo.size();
-  for (int i = 0; i < size; i++)
+  int64_t numBits = 0, numElem = 0;
+  size_t size = histo.size();
+  for (size_t i = 0; i < size; i++)
     if (histo[i] > 0)
     {
-      numBits += histo[i] * m_codeTable[i].first;
-      numElem += histo[i];
+      numBits += (int64_t)histo[i] * m_codeTable[i].first;
+      numElem += (int64_t)histo[i];
     }
 
   if (numElem == 0)
     return false;
 
-  int numUInts = ((((numBits + 7) >> 3) + 3) >> 2) + 1;    // add one more as the decode LUT can read ahead
-  numBytes += 4 * numUInts;    // data huffman coded
-  avgBpp = 8 * numBytes / (double)numElem;
+  int64_t numUInts = ((((numBits + 7) >> 3) + 3) >> 2) + 1;  // add one more as the decode LUT can read ahead
+  int64_t nBytes = 4 * numUInts + numBytes;  // data huffman coded
+
+  if (nBytes > (int64_t)INT_MAX)
+    return false;
+
+  numBytes = (int)nBytes;
+  avgBpp = 8 * nBytes / (double)numElem;
 
   return true;
 }
@@ -460,8 +466,7 @@ bool Huffman::BitStuffCodes(Byte** ppByte, int i0, int i1) const
     }
   }
 
-  size_t numUInts = (bitPos > 0 ? 1 : 0);
-  *ppByte += numUInts * sizeof(unsigned int);
+  *ppByte += bitPos > 0 ? sizeof(unsigned int) : 0;
 
   return true;
 }


=====================================
src/LercLib/Lerc.cpp
=====================================
@@ -256,6 +256,9 @@ ErrCode Lerc::GetLercInfo(const Byte* pLercBlob, unsigned int numBytesBlob, stru
 
       if (pMins && pMaxs)
       {
+        if ((size_t)lercInfo.nBands >= nElem)
+          return ErrCode::BufferTooSmall;
+
         pMins[lercInfo.nBands] = zMin;
         pMaxs[lercInfo.nBands] = zMax;
       }


=====================================
src/LercLib/Lerc1Decode/BitStuffer.cpp
=====================================
@@ -22,6 +22,7 @@ Contributors:  Thomas Maurer
 */
 
 #include <cstring>
+#include <cstdint>
 #include "BitStuffer.h"
 
 using namespace std;
@@ -29,40 +30,61 @@ using namespace LercNS;
 
 // -------------------------------------------------------------------------- ;
 
-bool BitStuffer::read(const Byte** ppByte, vector<unsigned int>& dataVec) const
+bool BitStuffer::read(const Byte** ppByte, size_t& nBytesRemaining, vector<unsigned int>& dataVec, size_t maxElementCount) const
 {
-  if (!ppByte)
+  if (!ppByte || nBytesRemaining < 1)
     return false;
 
   Byte numBitsByte = **ppByte;
   (*ppByte)++;
+  nBytesRemaining--;
 
   int bits67 = numBitsByte >> 6;
   int n = (bits67 == 0) ? 4 : 3 - bits67;
 
   numBitsByte &= 63;    // bits 0-5;
 
-  unsigned int numElements = 0;
-  if (!readUInt(ppByte, numElements, n))
+  if (numBitsByte >= 32)
     return false;
 
-  if (numBitsByte >= 32)
+  unsigned int numElements = 0;
+  if (!readUInt(ppByte, nBytesRemaining, numElements, n) || numElements > maxElementCount)
     return false;
 
   int numBits = numBitsByte;
-  unsigned int numUInts = (numElements * numBits + 31) / 32;
+  uint64_t numUIntsLL = ((uint64_t)numElements * numBits + 31) / 32;
+  uint64_t numBytesLL = ((uint64_t)numElements * numBits + 7) / 8;
+
+  size_t numUInts = (size_t)numUIntsLL;
+  size_t numBytes = (size_t)numBytesLL;    // could theoretically overflow on 32 bit system
+
+  if (numBytes != numBytesLL)
+    return false;
+
   dataVec.resize(numElements, 0);    // init with 0
 
   if (numUInts > 0)    // numBits can be 0
   {
-    m_tmpBitStuffVec.resize(numUInts);
-    m_tmpBitStuffVec[numUInts - 1] = 0;    // set last uint to 0
+    if (nBytesRemaining < numBytes)
+      return false;
+
+    try
+    {
+      m_tmpBitStuffVec.resize(numUInts);
+      m_tmpBitStuffVec[numUInts - 1] = 0;  // set last uint to 0
+    }
+    catch (const std::exception&)
+    {
+      return false;
+    }
+
+    if (!memcpy(&m_tmpBitStuffVec[0], *ppByte, numBytes))
+      return false;
 
-    unsigned int nBytesToCopy = (numElements * numBits + 7) / 8;
-    memcpy(&m_tmpBitStuffVec[0], *ppByte, nBytesToCopy);
 
     unsigned int* arr = &m_tmpBitStuffVec[0];
     unsigned int* srcPtr = arr;
+
     for (unsigned int i = 0; i < numUInts; i++)
     {
       SWAP_4(*srcPtr);
@@ -74,6 +96,9 @@ bool BitStuffer::read(const Byte** ppByte, vector<unsigned int>& dataVec) const
     while (n--)
       *pLastULong <<= 8;
 
+    if (dataVec.empty())
+      return false;
+
     // do the un-stuffing
     srcPtr = arr;
     unsigned int* dstPtr = &dataVec[0];
@@ -108,7 +133,8 @@ bool BitStuffer::read(const Byte** ppByte, vector<unsigned int>& dataVec) const
       }
     }
 
-    *ppByte += nBytesToCopy;
+    *ppByte += numBytes;
+    nBytesRemaining -= numBytes;
   }
 
   return true;
@@ -117,8 +143,11 @@ bool BitStuffer::read(const Byte** ppByte, vector<unsigned int>& dataVec) const
 // -------------------------------------------------------------------------- ;
 // -------------------------------------------------------------------------- ;
 
-bool BitStuffer::readUInt(const Byte** ppByte, unsigned int& k, int numBytes)
+bool BitStuffer::readUInt(const Byte** ppByte, size_t& nBytesRemaining, unsigned int& k, int numBytes)
 {
+  if (numBytes <= 0 || nBytesRemaining < (size_t)numBytes)
+    return false;
+
   const Byte* ptr = *ppByte;
 
   if (numBytes == 1)
@@ -141,6 +170,7 @@ bool BitStuffer::readUInt(const Byte** ppByte, unsigned int& k, int numBytes)
     return false;
 
   *ppByte = ptr + numBytes;
+  nBytesRemaining -= numBytes;
   return true;
 }
 


=====================================
src/LercLib/Lerc1Decode/BitStuffer.h
=====================================
@@ -39,12 +39,12 @@ public:
   BitStuffer()  {}
   virtual ~BitStuffer()  {}
 
-  bool read(const Byte** ppByte, std::vector<unsigned int>& dataVec) const;
+  bool read(const Byte** ppByte, size_t& nBytesRemaining, std::vector<unsigned int>& dataVec, size_t maxElementCount) const;
 
 protected:
   mutable std::vector<unsigned int>  m_tmpBitStuffVec;
 
-  static bool readUInt(const Byte** ppByte, unsigned int& k, int numBytes);    // numBytes = 1, 2, or 4
+  static bool readUInt(const Byte** ppByte, size_t& nBytesRemaining, unsigned int& k, int numBytes);  // numBytes = 1, 2, or 4
   static unsigned int numTailBytesNotNeeded(unsigned int numElem, int numBits);
 };
 


=====================================
src/LercLib/Lerc1Decode/CntZImage.cpp
=====================================
@@ -160,6 +160,11 @@ bool CntZImage::read(const Byte** ppByte, const Byte* bArr_end, double maxZError
     if (numBytes < 0 || ptr + numBytes > bArr_end)
       return false;
 
+    if (numTilesVert < 0 || numTilesVert > height_
+      || numTilesHori < 0 || numTilesHori > width_
+      || numBytes < 0 || numBytes > (1L << 30))    // limit to 1 GB
+      return false;
+
     if (!zPart && numTilesVert == 0 && numTilesHori == 0)    // no tiling for this cnt part
     {
       if (numBytes == 0)    // cnt part is const
@@ -180,8 +185,15 @@ bool CntZImage::read(const Byte** ppByte, const Byte* bArr_end, double maxZError
       {
         // decompress to bit mask
         BitMask bitMask(width_, height_);
+
+        size_t bitMaskSize = bitMask.Size();
+        if (bitMaskSize == 0)
+          return false;
+
+        size_t nBytesRemaining = (size_t)numBytes;  // checked against blob size above
+
         RLE rle;
-        if (!rle.decompress(bArr, (size_t)width_ * height_ * 2, (Byte*)bitMask.Bits(), bitMask.Size()))
+        if (!rle.decompress(bArr, nBytesRemaining, (Byte*)bitMask.Bits(), bitMaskSize))
           return false;
 
         CntZ* dstPtr = getData();
@@ -190,8 +202,12 @@ bool CntZImage::read(const Byte** ppByte, const Byte* bArr_end, double maxZError
             dstPtr->cnt = bitMask.IsValid(k) ? 1.0f : 0.0f;
       }
     }
-    else if (!readTiles(zPart, maxZErrorInFile, numTilesVert, numTilesHori, maxValInImg, bArr))
-      return false;
+    else
+    {
+      size_t nBytesRemaining = (size_t)numBytes;
+      if (!readTiles(zPart, maxZErrorInFile, numTilesVert, numTilesHori, maxValInImg, bArr, nBytesRemaining) || nBytesRemaining != 0)
+        return false;
+    }
 
     *ppByte += numBytes;
   }
@@ -203,7 +219,7 @@ bool CntZImage::read(const Byte** ppByte, const Byte* bArr_end, double maxZError
 // -------------------------------------------------------------------------- ;
 
 bool CntZImage::readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori,
-  float maxValInImg, const Byte* bArr)
+  float maxValInImg, const Byte* bArr, size_t& nBytesRemaining)
 {
   if (numTilesVert <= 0 || numTilesHori <= 0
     || numTilesVert > height_ || numTilesHori > width_)
@@ -231,8 +247,8 @@ bool CntZImage::readTiles(bool zPart, double maxZErrorInFile, int numTilesVert,
       if (tileW == 0)
         continue;
 
-      bool rv = zPart ? readZTile(  &ptr, i0, i0 + tileH, j0, j0 + tileW, maxZErrorInFile, maxValInImg) :
-                        readCntTile(&ptr, i0, i0 + tileH, j0, j0 + tileW);
+      bool rv = zPart ? readZTile(  &ptr, nBytesRemaining, i0, i0 + tileH, j0, j0 + tileW, maxZErrorInFile, maxValInImg) :
+                        readCntTile(&ptr, nBytesRemaining, i0, i0 + tileH, j0, j0 + tileW);
 
       if (!rv)
         return false;
@@ -244,13 +260,19 @@ bool CntZImage::readTiles(bool zPart, double maxZErrorInFile, int numTilesVert,
 
 // -------------------------------------------------------------------------- ;
 
-bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1)
+bool CntZImage::readCntTile(const Byte** ppByte, size_t& nBytesRemaining, int i0, int i1, int j0, int j1)
 {
   if (i0 >= i1 || j0 >= j1)
     return false;
 
+  const size_t numPixel = (size_t)(i1 - i0) * (j1 - j0);
+
+  if (nBytesRemaining < 1)
+    return false;
+
   const Byte* ptr = *ppByte;
   Byte comprFlag = *ptr++;
+  nBytesRemaining--;
 
   if (comprFlag == 2)    // entire tile is constant 0 (invalid)
   {                      // here we depend on resizeFill0()
@@ -280,6 +302,9 @@ bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1)
 
   if (comprFlag == 0)
   {
+    if (nBytesRemaining < numPixel * 4)
+      return false;
+
     // read cnt's as flt arr uncompressed
     for (int i = i0; i < i1; i++)
     {
@@ -291,6 +316,8 @@ bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1)
         dstPtr++;
       }
     }
+
+    nBytesRemaining -= numPixel * 4;
   }
   else
   {
@@ -298,13 +325,17 @@ bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1)
     int bits67 = comprFlag >> 6;
     int n = (bits67 == 0) ? 4 : 3 - bits67;
 
+    if (nBytesRemaining < (size_t)n)
+      return false;
+
     float offset = 0;
     if (!readFlt(&ptr, offset, n))
       return false;
 
-    int numPixel = (i1 - i0) * (j1 - j0);
+    nBytesRemaining -= n;
+
     BitStuffer bitStuffer;
-    if (!bitStuffer.read(&ptr, m_tmpDataVec) || m_tmpDataVec.size() < (size_t)numPixel)
+    if (!bitStuffer.read(&ptr, nBytesRemaining, m_tmpDataVec, numPixel) || m_tmpDataVec.size() < numPixel)
       return false;
 
     unsigned int* srcPtr = &m_tmpDataVec[0];
@@ -326,10 +357,17 @@ bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1)
 
 // -------------------------------------------------------------------------- ;
 
-bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg)
+bool CntZImage::readZTile(const Byte** ppByte, size_t& nBytesRemaining, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg)
 {
+  const size_t maxNumPixel = (size_t)(i1 - i0) * (j1 - j0);
+
+  if (nBytesRemaining < 1)
+    return false;
+
   const Byte* ptr = *ppByte;
   Byte comprFlag = *ptr++;
+  nBytesRemaining--;
+
   int bits67 = comprFlag >> 6;
   comprFlag &= 63;
 
@@ -363,8 +401,13 @@ bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, d
       {
         if (dstPtr->cnt > 0)
         {
+          if (nBytesRemaining < 4)
+            return false;
+
           memcpy(&dstPtr->z, ptr, 4);  ptr += 4;
           SWAP_4(dstPtr->z);
+
+          nBytesRemaining -= 4;
         }
         dstPtr++;
       }
@@ -374,10 +417,16 @@ bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, d
   {
     // read z's as int arr bit stuffed
     int n = (bits67 == 0) ? 4 : 3 - bits67;
+
+    if (nBytesRemaining < (size_t)n)
+      return false;
+
     float offset = 0;
     if (!readFlt(&ptr, offset, n))
       return false;
 
+    nBytesRemaining -= n;
+
     if (comprFlag == 3)
     {
       for (int i = i0; i < i1; i++)
@@ -393,16 +442,19 @@ bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, d
     }
     else
     {
-      vector<unsigned int>& dataVec = m_tmpDataVec;
       BitStuffer bitStuffer;
-      if (!bitStuffer.read(&ptr, dataVec))
+      if (!bitStuffer.read(&ptr, nBytesRemaining, m_tmpDataVec, maxNumPixel))
         return false;
 
       double invScale = 2 * maxZErrorInFile;
-      unsigned int* srcPtr = &dataVec[0];
+      unsigned int* srcPtr = &m_tmpDataVec[0];
+      size_t nElemLeft = m_tmpDataVec.size();
 
       if (m_bDecoderCanIgnoreMask)
       {
+        if (nElemLeft != maxNumPixel)
+          return false;
+
         for (int i = i0; i < i1; i++)
         {
           CntZ* dstPtr = getData() + i * width_ + j0;
@@ -423,6 +475,9 @@ bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, d
           {
             if (dstPtr->cnt > 0)
             {
+              if (!nElemLeft--)
+                return false;
+
               float z = (float)(offset + *srcPtr++ * invScale);
               dstPtr->z = std::min(z, maxZInImg);    // make sure we stay in the orig range
             }


=====================================
src/LercLib/Lerc1Decode/CntZImage.h
=====================================
@@ -65,10 +65,11 @@ protected:
     float maxZInImg;
   };
 
-  bool readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori, float maxValInImg, const Byte* bArr);
+  bool readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori, float maxValInImg,
+    const Byte* bArr, size_t& nBytesRemaining);
 
-  bool readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1);
-  bool readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg);
+  bool readCntTile(const Byte** ppByte, size_t& nBytesRemaining, int i0, int i1, int j0, int j1);
+  bool readZTile(const Byte** ppByte, size_t& nBytesRemaining, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg);
 
   static int numBytesFlt(float z);    // returns 1, 2, or 4
   static bool readFlt(const Byte** ppByte, float& z, int numBytes);


=====================================
src/LercLib/Lerc2.cpp
=====================================
@@ -319,7 +319,7 @@ unsigned int Lerc2::ComputeNumBytesNeededToWrite(const T* arr, double maxZError,
       if (nBytesHuffman < 0)
         nBytesHuffman = INT_MAX;
 
-      if (nBytesHuffman < nBytesTiling * 0.9)    // demand at least 10% better than not Huffman
+      if (nBytesHuffman < nBytesTiling * 0.9)  // demand at least 10% better than not Huffman
       {
         nBytesData = nBytesHuffman;
         m_imageEncodeMode = IEM_DeltaDeltaHuffman;
@@ -970,7 +970,7 @@ bool Lerc2::ReadMask(const Byte** ppByte, size_t& nBytesRemainingInOut)
   const Byte* ptr = *ppByte;
   size_t nBytesRemaining = nBytesRemainingInOut;
 
-  int numBytesMask;
+  int numBytesMask(0);
   if (nBytesRemaining < sizeof(int) || !memcpy(&numBytesMask, ptr, sizeof(int)) || numBytesMask < 0)
     return false;
 
@@ -2174,6 +2174,8 @@ bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data,
       {
         if (hd.version > 2)
         {
+          size_t nElemLeft = bufferVec.size();
+
           for (int i = i0; i < i1; i++)
           {
             int k = i * nCols + j0;
@@ -2184,6 +2186,9 @@ bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data,
               for (int j = j0; j < j1; j++, k++, m += nDepth)
                 if (m_bitMask.IsValid(k))
                 {
+                  if (!nElemLeft--)
+                    return false;
+
                   double z = offset + *srcPtr++ * invScale;
                   data[m] = (T)std::min(z, zMax);    // make sure we stay in the orig range
                 }
@@ -2193,13 +2198,16 @@ bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data,
               for (int j = j0; j < j1; j++, k++, m += nDepth)
                 if (m_bitMask.IsValid(k))
                 {
+                  if (!nElemLeft--)
+                    return false;
+
                   double z = offset + *srcPtr++ * invScale + data[m - 1];
                   data[m] = (T)std::min(z, zMax);
                 }
             }
           }
         }
-        else  // fail gracefully in case of corrupted blob for old version <= 2 which had no checksum
+        else
         {
           size_t bufferVecIdx = 0;
 
@@ -2211,7 +2219,7 @@ bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data,
             for (int j = j0; j < j1; j++, k++, m += nDepth)
               if (m_bitMask.IsValid(k))
               {
-                if (bufferVecIdx == bufferVec.size())  // fail gracefully in case of corrupted blob for old version <= 2 which had no checksum
+                if (bufferVecIdx == bufferVec.size())
                   return false;
 
                 double z = offset + bufferVec[bufferVecIdx] * invScale;


=====================================
src/LercLib/RLE.cpp
=====================================
@@ -24,6 +24,7 @@ Contributors:  Thomas Maurer
 #include "Defines.h"
 #include "RLE.h"
 #include <cstring>
+#include <new>
 
 USING_NAMESPACE_LERC
 
@@ -121,17 +122,19 @@ size_t RLE::computeNumBytesRLE(const Byte* arr, size_t numBytes) const
 // -------------------------------------------------------------------------- ;
 
 bool RLE::compress(const Byte* arr, size_t numBytes,
-                   Byte** arrRLE, size_t& numBytesRLE, bool verify) const
+                   Byte** arrRLE, size_t& numBytesRLE, bool verify) const noexcept
 {
   if (arr == nullptr || numBytes == 0)
     return false;
 
   numBytesRLE = computeNumBytesRLE(arr, numBytes);
 
-  *arrRLE = new Byte[numBytesRLE];
+  *arrRLE = new(std::nothrow) Byte[numBytesRLE];
   if (!*arrRLE)
     return false;
 
+  std::memset(*arrRLE, 0, numBytesRLE);
+
   const Byte* srcPtr = arr;
   Byte* cntPtr = *arrRLE;
   Byte* dstPtr = cntPtr + 2;
@@ -253,7 +256,7 @@ bool RLE::compress(const Byte* arr, size_t numBytes,
 
 // -------------------------------------------------------------------------- ;
 
-bool RLE::decompress(const Byte* arrRLE, size_t nBytesRemainingIn, Byte** arr, size_t& numBytes)
+bool RLE::decompress(const Byte* arrRLE, size_t nBytesRemainingIn, Byte** arr, size_t& numBytes) noexcept
 {
   if (!arrRLE || nBytesRemainingIn < 2)
     return false;
@@ -286,10 +289,12 @@ bool RLE::decompress(const Byte* arrRLE, size_t nBytesRemainingIn, Byte** arr, s
     return false;
   }
 
-  *arr = new Byte[numBytes];
+  *arr = new(std::nothrow) Byte[numBytes];
   if (!*arr)
     return false;
 
+  memset(*arr, 0, numBytes);
+
   return decompress(arrRLE, nBytesRemainingIn, *arr, numBytes);
 }
 
@@ -308,7 +313,7 @@ bool RLE::decompress(const Byte* arrRLE, size_t nBytesRemaining, Byte* arr, size
   while (cnt != -32768)
   {
     int i = (cnt <= 0) ? -cnt : cnt;
-    size_t m = (cnt <= 0) ? 1 : (size_t)i;  // <= not < to fail gracefully in case of corrupted blob for old version <= 2 which had no checksum
+    size_t m = (cnt <= 0) ? 1 : (size_t)i;
 
     if (nBytesRemaining < m + 2 || arrIdx + i > arrSize)
       return false;


=====================================
src/LercLib/RLE.h
=====================================
@@ -50,11 +50,11 @@ public:
   // when done, call
   // delete[] *arrRLE;
   bool compress(const Byte* arr, size_t numBytes,
-    Byte** arrRLE, size_t& numBytesRLE, bool verify = false) const;
+    Byte** arrRLE, size_t& numBytesRLE, bool verify = false) const noexcept;
 
   // when done, call
   // delete[] *arr;
-  static bool decompress(const Byte* arrRLE, size_t nBytesRemaining, Byte** arr, size_t& numBytes);
+  static bool decompress(const Byte* arrRLE, size_t nBytesRemaining, Byte** arr, size_t& numBytes) noexcept;
 
   // arr already allocated, just fill
   static bool decompress(const Byte* arrRLE, size_t nBytesRemaining, Byte* arr, size_t arrSize);


=====================================
src/LercLib/fpl_Compression.cpp
=====================================
@@ -28,6 +28,7 @@ Original coding 2021 Yuriy Yakimenko
 #include <cmath>
 #include <cstring>
 #include <climits>
+#include <cstdlib>
 
 USING_NAMESPACE_LERC
 
@@ -36,21 +37,22 @@ static const bool use_rle = true;
 
 size_t fpl_Compression::extract_buffer(const char* data, size_t size, size_t uncompressed_size, char** output)
 {
-    if (use_esri_huffman)
-    {
-        size_t expected_size = uncompressed_size;
-
-        bool ret = fpl_EsriHuffman::DecodeHuffman((const unsigned char*)data, size, expected_size, (unsigned char**)output);
+  if (use_esri_huffman)
+  {
+    size_t expected_size = uncompressed_size;
 
-        //if (!ret)
-        //  printf("input size %zu, expected out %zu\n", size, uncompressed_size);
+    if (!fpl_EsriHuffman::DecodeHuffman((const unsigned char*)data, size, expected_size, (unsigned char**)output))
+    {
+      if (*output)
+        free(*output);
 
-        assert(ret);
-        return uncompressed_size;
+      return 0;
     }
 
-    assert(0);
-    return 0;
+    return uncompressed_size;
+  }
+
+  return 0;
 }
 
 size_t fpl_Compression::compress_buffer(const char* data, size_t size, char** output, bool fast)


=====================================
src/LercLib/fpl_EsriHuffman.cpp
=====================================
@@ -34,50 +34,49 @@ USING_NAMESPACE_LERC
 
 void lerc_assert(bool v);
 
-bool decodePackBits (const unsigned char *ptr, const size_t size, size_t expected, unsigned char **output)
+bool decodePackBits(const unsigned char* ptr, const size_t size, size_t expected, unsigned char** output)
 {
-    unsigned char *out = NULL;
+  unsigned char* out = NULL;
 
-    if (*output == NULL)
+  if (*output == NULL)
+  {
+    out = (unsigned char*)malloc(expected);  // max possible
+    *output = out;
+  }
+  else
+  {
+    out = *output;
+  }
+
+  size_t curr = 0;
+
+  for (size_t i = 0; i < size; )
+  {
+    int b = ptr[i++];  // read header byte
+
+    if (b <= 127)
     {
-        out = (unsigned char *)malloc (expected); // max possible
+      if (curr + b >= expected)
+        return false;
 
-        *output = out;
+      int b1 = b + 1;
+      memcpy(&out[curr], &ptr[i], b1);
+      curr += b1;
+      i += b1;
     }
     else
     {
-        out = *output;
-    }
-
-    size_t curr = 0;
+      if (curr + b - 127 >= expected)
+        return false;
 
-    for (size_t i = 0; i < size; )
-    {
-      int b = ptr[i++];  // read header byte
-
-      if (b <= 127)
-      {
-        if (curr + b >= expected)
-          return false;
-
-        int b1 = b + 1;
-        memcpy(&out[curr], &ptr[i], b1);
-        curr += b1;
-        i += b1;
-      }
-      else
-      {
-        if (curr + b - 127 >= expected)
-          return false;
-
-        int b1 = b - 127 + 1;
-        memset(&out[curr], ptr[i], b1);
-        curr += b1;
-        i++;
-      }
+      int b1 = b - 127 + 1;
+      memset(&out[curr], ptr[i], b1);
+      curr += b1;
+      i++;
     }
+  }
 
-    return (curr == expected);
+  return (curr == expected);
 }
 
 long encodePackBits (const unsigned char *ptr, const size_t size, unsigned char **output)
@@ -450,111 +449,85 @@ int fpl_EsriHuffman::EncodeHuffman (const char *input, size_t input_len, unsigne
     return ret;
 }
 
-bool fpl_EsriHuffman::DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t& nBytesRemainingInOut, unsigned char** output)
+bool fpl_EsriHuffman::DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t expected_output_size, unsigned char** output)
 {
-    const unsigned char* ppByte = (const unsigned char *)inBytes;
+  const unsigned char* ppByte = (const unsigned char*)inBytes;
 
-    if (!ppByte)
-    {
-        //fprintf (stderr, "bad input: null pointer\n");
-        return false;
-    }
+  if (!ppByte || inCount < 1)
+    return false;
 
-    // check for RLE flag
+  if (ppByte[0] == HUFFMAN_RLE)  // RLE flag
+  {
+    if (inCount < 6)
+      return false;
 
-    if (ppByte[0] == HUFFMAN_RLE)
-    {
-        unsigned char s = ppByte[1];
-        uint32_t rle_size = 0;
+    unsigned char s = ppByte[1];
+    uint32_t rle_size = 0;
 
-        memcpy (&rle_size, ppByte + 2, 4);
+    memcpy(&rle_size, ppByte + 2, 4);
 
-        if (rle_size != nBytesRemainingInOut)
-        {
-            assert (0);
-        }
-
-        unsigned char *data = (unsigned char *)malloc (nBytesRemainingInOut);
-        *output = data;
-
-        if (data == NULL)
-        {
-            //fprintf (stderr, "malloc() failed\n");
-            return false;
-        }
-
-        memset (data, s, nBytesRemainingInOut);
-
-        return true;
-    }
-
-    if (ppByte[0] == HUFFMAN_NO_ENCODING) // "as is flag"
-    {
-        unsigned char *data = (unsigned char *)malloc (nBytesRemainingInOut);
-        *output = data;
-
-        if (data == NULL)
-        {
-            //printf ("malloc 2 fail\n");
-            return false;
-        }
+    if (rle_size != expected_output_size)
+      return false;
 
-        memcpy (data, ppByte + 1, nBytesRemainingInOut);
-
-        return true;
-    }
-
-    if (ppByte[0] == HUFFMAN_PACKBITS)
-    {
-        unsigned char *unpacked = NULL;
-
-        lerc_assert (true == decodePackBits (ppByte + 1, inCount - 1, nBytesRemainingInOut, &unpacked));
-
-        *output = unpacked;
-
-        return true;
-    }
-
-    assert (ppByte[0] == HUFFMAN_NORMAL); // "normal" huffman flag.
-
-    ppByte++;
-
-    size_t expected_output_len = nBytesRemainingInOut;
-
-    Huffman huffman;
-    if (!huffman.ReadCodeTable(&ppByte, nBytesRemainingInOut, 5))    // header and code table
-    {
-        return false;
-    }
-
-    int numBitsLUT = 0;
-    if (!huffman.BuildTreeFromCodes(numBitsLUT))
-    {
-        return false;
-    }
-
-    unsigned char *data = (unsigned char *)malloc (expected_output_len);
+    unsigned char* data = (unsigned char*)malloc(expected_output_size);
     *output = data;
-
     if (!data)
-    {
-        return false;
-    }
+      return false;
 
-    int offset = 0;
-    int bitPos = 0;
-    size_t nBytesRemaining = nBytesRemainingInOut;
+    memset(data, s, expected_output_size);
+    return true;
+  }
 
-    for (size_t m = 0; m < expected_output_len; m++)
-    {
-        int val = 0;
-        if (!huffman.DecodeOneValue(&ppByte, nBytesRemaining, bitPos, numBitsLUT, val))
-        {
-            return false;
-        }
+  if (ppByte[0] == HUFFMAN_NO_ENCODING)  // "as is flag"
+  {
+    if (inCount < 1 + expected_output_size)
+      return false;
 
-        data[m] = (unsigned char)(val - offset);
-    }
+    unsigned char* data = (unsigned char*)malloc(expected_output_size);
+    *output = data;
+    if (!data)
+      return false;
 
+    memcpy(data, ppByte + 1, expected_output_size);
     return true;
+  }
+
+  if (ppByte[0] == HUFFMAN_PACKBITS)
+  {
+    unsigned char* unpacked = NULL;
+    bool rv = decodePackBits(ppByte + 1, inCount - 1, expected_output_size, &unpacked);
+    lerc_assert(rv);
+    *output = unpacked;
+    return rv;
+  }
+
+  assert(ppByte[0] == HUFFMAN_NORMAL);  // "normal" huffman flag.
+
+  ppByte++;
+  size_t nBytesRemainingLoc = inCount - 1;
+
+  Huffman huffman;
+  if (!huffman.ReadCodeTable(&ppByte, nBytesRemainingLoc, 5))  // header and code table
+    return false;
+
+  int numBitsLUT(0);
+  if (!huffman.BuildTreeFromCodes(numBitsLUT))
+    return false;
+
+  unsigned char* data = (unsigned char*)malloc(expected_output_size);
+  *output = data;
+  if (!data)
+    return false;
+
+  int bitPos(0);
+  for (size_t m = 0; m < expected_output_size; m++)
+  {
+    int val(0);
+    if (!huffman.DecodeOneValue(&ppByte, nBytesRemainingLoc, bitPos, numBitsLUT, val))
+      return false;
+
+    data[m] = (unsigned char)val;
+  }
+
+  return true;
 }


=====================================
src/LercLib/fpl_EsriHuffman.h
=====================================
@@ -35,7 +35,7 @@ class fpl_EsriHuffman
 public:
   static int EncodeHuffman(const char* input, size_t input_len, unsigned char** ppByte, bool use_rle);
 
-  static bool DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t& nBytesRemainingInOut, unsigned char** output);
+  static bool DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t expected_output_size, unsigned char** output);
 
   static int getCompressedSize(const char* input, size_t input_len);
 };


=====================================
src/LercLib/fpl_Lerc2Ext.cpp
=====================================
@@ -25,6 +25,7 @@ Original coding 2021 Yuriy Yakimenko
 #include "fpl_Lerc2Ext.h"
 #include "fpl_Compression.h"
 #include <assert.h>
+#include <climits>
 #include <cmath>
 #include <cstring>
 #include <stdint.h>
@@ -725,19 +726,28 @@ bool restoreByteOrder(std::vector<std::pair<int, char*> >& output_buffers,
 bool LosslessFPCompression::DecodeHuffmanFlt(const unsigned char** ppByte, size_t& nBytesRemainingInOut,
   void* pData, bool bIsDouble, int iWidth, int iHeight, int iDepth)
 {
+  if (!ppByte || !(*ppByte) || !pData || iWidth <= 0 || iHeight <= 0 || iDepth <= 0)
+    return false;
+
   if (iDepth == 1)
   {
-    return DecodeHuffmanFltSlice (ppByte, nBytesRemainingInOut, pData, bIsDouble, iWidth, iHeight);
+    return DecodeHuffmanFltSlice(ppByte, nBytesRemainingInOut, pData, bIsDouble, iWidth, iHeight);
   }
   else
   {
+    if ((size_t)iWidth * iHeight >= (size_t)INT_MAX)
+      return false;
+
     return DecodeHuffmanFltSlice(ppByte, nBytesRemainingInOut, pData, bIsDouble, iDepth, iWidth * iHeight);
   }
 }
 
-bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte, size_t& nBytesRemainingInOut,
-          void * pData, bool bIsDouble, int iWidth, int iHeight)
+bool LosslessFPCompression::DecodeHuffmanFltSlice(const unsigned char** ppByte, size_t& nBytesRemainingInOut,
+          void* pData, bool bIsDouble, int iWidth, int iHeight)
 {
+  if (!ppByte || !*ppByte || nBytesRemainingInOut < 1 || !pData || iWidth <= 0 || iHeight <= 0)
+    return false;
+
   unsigned char* ptr = (unsigned char *)(*ppByte);
 
   UnitType unit_type = bIsDouble ? UNIT_TYPE_DOUBLE : UNIT_TYPE_FLOAT;
@@ -747,10 +757,11 @@ bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte,
   size_t expected_size = ((size_t)iWidth * iHeight);
 
   std::vector<std::pair<int, char*> >output_buffers;
+  std::vector<bool> seen_byte_indices(bytes, false);
 
-  unsigned char pred_code = 0;
-
-  memcpy(&pred_code, ptr, sizeof(pred_code));
+  unsigned char pred_code(0);
+  memcpy(&pred_code, ptr++, 1);
+  nBytesRemainingInOut--;
 
   if (pred_code > 2) // down the road, upper 6 bits can contain compression method.
                      // we support only Huffman in this version for now.
@@ -758,30 +769,24 @@ bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte,
     return false;
   }
 
-  ptr += sizeof(pred_code);
-
-  nBytesRemainingInOut -= sizeof(pred_code);
-
   for (size_t byte = 0; byte < bytes; byte++)
   {
     unsigned char byte_index = 0, best_level = 0;
-
     uint32_t compressed_size = 0;
 
     if (nBytesRemainingInOut < 6)
       return false;
 
-    memcpy(&byte_index, ptr, sizeof(byte_index));
+    memcpy(&byte_index, ptr++, 1);
+    nBytesRemainingInOut--;
 
-    if (byte_index >= bytes)
+    if (byte_index >= bytes || seen_byte_indices[byte_index])
       return false;
 
-    ptr += sizeof(byte_index);
-    nBytesRemainingInOut -= sizeof(byte_index);
+    seen_byte_indices[byte_index] = true;
 
-    memcpy(&best_level, ptr, sizeof(best_level));
-    ptr += sizeof(best_level);
-    nBytesRemainingInOut -= sizeof(best_level);
+    memcpy(&best_level, ptr++, 1);
+    nBytesRemainingInOut--;
 
     if (best_level > MAX_DELTA)
       return false;
@@ -794,24 +799,23 @@ bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte,
       return false;
 
     unsigned char* compressed = (unsigned char *)malloc(compressed_size);
-
     if (!compressed)
       return false;
 
     memcpy(compressed, ptr, compressed_size);
     ptr += compressed_size;
-
     nBytesRemainingInOut -= compressed_size;
 
     char* uncompressed = NULL;
-
     size_t extracted_size = fpl_Compression::extract_buffer((const char *)compressed, compressed_size, expected_size, &uncompressed);
-
     lerc_assert(expected_size == extracted_size);
 
     free(compressed);
     compressed = NULL;
 
+    if (extracted_size == 0 || extracted_size != expected_size)
+      return false;
+
     int byte_delta = best_level;
 
     unsigned char* restored = restoreSequence((unsigned char*)uncompressed, extracted_size, byte_delta, false);
@@ -819,7 +823,6 @@ bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte,
     output_buffers.push_back(std::make_pair(byte_index, (char*)restored));
   }
 
- // nBytesRemainingInOut -= (ptr - *ppByte); => done above.
   *ppByte = ptr;
 
   PredictorType predictor = Predictor::getType(pred_code);
@@ -855,12 +858,11 @@ bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte,
 
   output_buffers.clear();
 
-  if (output_block_data) // ret is already set to false if memory was not allocated.
+  if (output_block_data)  // ret is already set to false if memory was not allocated.
   {
     memcpy(pData, output_block_data, bytes * iWidth * iHeight);
-
     free(output_block_data);
   }
 
-  return (ret);
+  return ret;
 }


=====================================
src/LercLib/include/Lerc_c_api.h
=====================================
@@ -37,7 +37,7 @@ extern "C" {
 /* LERC version numbers and related macros added in 3.0.0 */
 
 #define LERC_VERSION_MAJOR 4
-#define LERC_VERSION_MINOR 2
+#define LERC_VERSION_MINOR 3
 #define LERC_VERSION_PATCH 0
 
 /* Macro to compute a LERC version number from its components */



View it on GitLab: https://salsa.debian.org/debian-gis-team/lerc/-/compare/0c4f50911719ef54d1f2c19b1b63a90aef6e90cc...43c1173b58b367db119064708b61313504759443

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/lerc/-/compare/0c4f50911719ef54d1f2c19b1b63a90aef6e90cc...43c1173b58b367db119064708b61313504759443
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20261031/f085a7dd/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list