[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