viewer-private#226 Unhandled PngError throws application into a loop

png_read_info triggered a PngError, LLAppViewer::frame() handled it
instead of LLPngWrapper::readPng, and since status didn't
change viewer tried to decode image again and again and again.
master
Andrey Kleshchev 2024-04-12 02:03:49 +03:00 committed by Andrey Kleshchev
parent 00e09ddcad
commit f5a7fba76a
5 changed files with 161 additions and 122 deletions

View File

@ -27,6 +27,7 @@
#include "linden_common.h"
#include "stdtypes.h"
#include "llerror.h"
#include "llexception.h"
#include "llimage.h"
#include "llpngwrapper.h"
@ -51,29 +52,44 @@ bool LLImagePNG::updateData()
{
resetLastError();
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
try
{
setLastError("Uninitialized instance of LLImagePNG");
// Check to make sure that this instance has been initialized with data
if (!getData() || (0 == getDataSize()))
{
setLastError("Uninitialized instance of LLImagePNG");
return false;
}
// Decode the PNG data and extract sizing information
LLPngWrapper pngWrapper;
if (!pngWrapper.isValidPng(getData()))
{
setLastError("LLImagePNG data does not have a valid PNG header!");
return false;
}
LLPngWrapper::ImageInfo infop;
if (!pngWrapper.readPng(getData(), getDataSize(), NULL, &infop))
{
setLastError(pngWrapper.getErrorMessage());
return false;
}
setSize(infop.mWidth, infop.mHeight, infop.mComponents);
}
catch (const LLContinueError& msg)
{
setLastError(msg.what());
LOG_UNHANDLED_EXCEPTION("");
return false;
}
catch (...)
{
setLastError("LLImagePNG");
LOG_UNHANDLED_EXCEPTION("");
return false;
}
// Decode the PNG data and extract sizing information
LLPngWrapper pngWrapper;
if (!pngWrapper.isValidPng(getData()))
{
setLastError("LLImagePNG data does not have a valid PNG header!");
return false;
}
LLPngWrapper::ImageInfo infop;
if (! pngWrapper.readPng(getData(), getDataSize(), NULL, &infop))
{
setLastError(pngWrapper.getErrorMessage());
return false;
}
setSize(infop.mWidth, infop.mHeight, infop.mComponents);
return true;
}

View File

@ -216,6 +216,13 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf
releaseResources();
return (FALSE);
}
catch (...)
{
mErrorMessage = "LLPngWrapper";
releaseResources();
LOG_UNHANDLED_EXCEPTION("");
return (FALSE);
}
// Clean up and return
releaseResources();

View File

@ -332,54 +332,62 @@ void LLFloaterImagePreview::draw()
//-----------------------------------------------------------------------------
bool LLFloaterImagePreview::loadImage(const std::string& src_filename)
{
std::string exten = gDirUtilp->getExtension(src_filename);
U32 codec = LLImageBase::getCodecFromExtension(exten);
try
{
std::string exten = gDirUtilp->getExtension(src_filename);
U32 codec = LLImageBase::getCodecFromExtension(exten);
LLImageDimensionsInfo image_info;
if (!image_info.load(src_filename,codec))
{
mImageLoadError = image_info.getLastError();
return false;
}
LLImageDimensionsInfo image_info;
if (!image_info.load(src_filename, codec))
{
mImageLoadError = image_info.getLastError();
return false;
}
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
S32 max_width = gSavedSettings.getS32("max_texture_dimension_X");
S32 max_height = gSavedSettings.getS32("max_texture_dimension_Y");
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
LLStringUtil::format_map_t args;
args["WIDTH"] = llformat("%d", max_width);
args["HEIGHT"] = llformat("%d", max_height);
if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height))
{
LLStringUtil::format_map_t args;
args["WIDTH"] = llformat("%d", max_width);
args["HEIGHT"] = llformat("%d", max_height);
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
return false;
}
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
return false;
}
if (!image->load(src_filename))
{
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
raw_image->biasedScaleToPowerOfTwo(1024);
mRawImagep = raw_image;
mImageLoadError = LLTrans::getString("texture_load_dimensions_error", args);
return false;
}
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
return false;
}
if (!image->load(src_filename))
{
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
raw_image->biasedScaleToPowerOfTwo(1024);
mRawImagep = raw_image;
}
catch (...)
{
LOG_UNHANDLED_EXCEPTION("");
return false;
}
return true;
}

View File

@ -1297,7 +1297,7 @@ bool LLViewerTextureList::createUploadFile(LLPointer<LLImageRaw> raw_image,
return true;
}
BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
bool LLViewerTextureList::createUploadFile(const std::string& filename,
const std::string& out_filename,
const U8 codec,
const S32 max_image_dimentions,
@ -1305,64 +1305,72 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename,
bool force_square)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE;
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return FALSE;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return FALSE;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return FALSE;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return FALSE;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
try
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return FALSE;
// Load the image
LLPointer<LLImageFormatted> image = LLImageFormatted::createFromType(codec);
if (image.isNull())
{
LL_WARNS() << "Couldn't open the image to be uploaded." << LL_ENDL;
return false;
}
if (!image->load(filename))
{
image->setLastError("Couldn't load the image to be uploaded.");
return false;
}
// Decompress or expand it in a raw image structure
LLPointer<LLImageRaw> raw_image = new LLImageRaw;
if (!image->decode(raw_image, 0.0f))
{
image->setLastError("Couldn't decode the image to be uploaded.");
return false;
}
// Check the image constraints
if ((image->getComponents() != 3) && (image->getComponents() != 4))
{
image->setLastError("Image files with less than 3 or more than 4 components are not supported.");
return false;
}
if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions)
{
std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx",
min_image_dimentions,
min_image_dimentions,
image->getWidth(),
image->getHeight());
image->setLastError(reason);
return false;
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return false;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return false;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate(out_filename))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return false;
}
}
// Convert to j2c (JPEG2000) and save the file locally
LLPointer<LLImageJ2C> compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square);
if (compressedImage.isNull())
{
image->setLastError("Couldn't convert the image to jpeg2000.");
LL_INFOS() << "Couldn't convert to j2c, file : " << filename << LL_ENDL;
return FALSE;
}
if (!compressedImage->save(out_filename))
{
image->setLastError("Couldn't create the jpeg2000 image for upload.");
LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL;
return FALSE;
}
// Test to see if the encode and save worked
LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
if (!integrity_test->loadAndValidate( out_filename ))
{
image->setLastError("The created jpeg2000 image is corrupt.");
LL_INFOS() << "Image file : " << out_filename << " is corrupt" << LL_ENDL;
return FALSE;
}
return TRUE;
catch (...)
{
LOG_UNHANDLED_EXCEPTION("");
return false;
}
return true;
}
// note: modifies the argument raw_image!!!!

View File

@ -96,7 +96,7 @@ public:
const std::string& out_filename,
const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,
const S32 min_image_dimentions = 0);
static BOOL createUploadFile(const std::string& filename,
static bool createUploadFile(const std::string& filename,
const std::string& out_filename,
const U8 codec,
const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT,