5763: Fix png error handling by setting jmpbuf r=def- a=Jupeyy

else libpng will kill the thread on an error bcs it's a fatal error.

broken skin can be found in discord bugs channel weeb_okayu

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: Jupeyy <jupjopjap@gmail.com>
This commit is contained in:
bors[bot] 2022-08-31 08:57:02 +00:00 committed by GitHub
commit 59db32e9c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,6 +1,7 @@
#include "image_loader.h" #include "image_loader.h"
#include <base/log.h> #include <base/log.h>
#include <base/system.h> #include <base/system.h>
#include <csetjmp>
#include <cstdlib> #include <cstdlib>
#include <png.h> #include <png.h>
@ -8,20 +9,22 @@
struct SLibPNGWarningItem struct SLibPNGWarningItem
{ {
SImageByteBuffer *m_pByteLoader; SImageByteBuffer *m_pByteLoader;
const char *pFileName; const char *m_pFileName;
std::jmp_buf m_Buf;
}; };
static void LibPNGError(png_structp png_ptr, png_const_charp error_msg) [[noreturn]] static void LibPNGError(png_structp png_ptr, png_const_charp error_msg)
{ {
SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr);
pUserStruct->m_pByteLoader->m_Err = -1; pUserStruct->m_pByteLoader->m_Err = -1;
dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->pFileName, error_msg); dbg_msg("libpng", "error for file \"%s\": %s", pUserStruct->m_pFileName, error_msg);
std::longjmp(pUserStruct->m_Buf, 1);
} }
static void LibPNGWarning(png_structp png_ptr, png_const_charp warning_msg) static void LibPNGWarning(png_structp png_ptr, png_const_charp warning_msg)
{ {
SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr); SLibPNGWarningItem *pUserStruct = (SLibPNGWarningItem *)png_get_error_ptr(png_ptr);
dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->pFileName, warning_msg); dbg_msg("libpng", "warning for file \"%s\": %s", pUserStruct->m_pFileName, warning_msg);
} }
static bool FileMatchesImageType(SImageByteBuffer &ByteLoader) static bool FileMatchesImageType(SImageByteBuffer &ByteLoader)
@ -75,7 +78,8 @@ static void LibPNGSetImageFormat(EImageFormat &ImageFormat, int LibPNGColorType)
static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo) static void LibPNGDeleteReadStruct(png_structp pPNGStruct, png_infop pPNGInfo)
{ {
png_destroy_info_struct(pPNGStruct, &pPNGInfo); if(pPNGInfo != nullptr)
png_destroy_info_struct(pPNGStruct, &pPNGInfo);
png_destroy_read_struct(&pPNGStruct, NULL, NULL); png_destroy_read_struct(&pPNGStruct, NULL, NULL);
} }
@ -127,6 +131,15 @@ static int PngliteIncompatibility(png_structp pPNGStruct, png_infop pPNGInfo)
bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat) bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIncompatible, int &Width, int &Height, uint8_t *&pImageBuff, EImageFormat &ImageFormat)
{ {
png_infop pPNGInfo = nullptr;
int ColorType;
png_byte BitDepth;
int ColorChannelCount;
int BytesInRow;
Height = 0;
png_bytepp pRowPointers = nullptr;
SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName, {}};
png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_structp pPNGStruct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(pPNGStruct == NULL) if(pPNGStruct == NULL)
@ -135,7 +148,22 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn
return false; return false;
} }
png_infop pPNGInfo = png_create_info_struct(pPNGStruct); if(setjmp(UserErrorStruct.m_Buf))
{
if(pRowPointers != nullptr)
{
for(int i = 0; i < Height; ++i)
{
delete[] pRowPointers[i];
}
}
delete[] pRowPointers;
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
}
png_set_error_fn(pPNGStruct, &UserErrorStruct, LibPNGError, LibPNGWarning);
pPNGInfo = png_create_info_struct(pPNGStruct);
if(pPNGInfo == NULL) if(pPNGInfo == NULL)
{ {
@ -144,9 +172,6 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn
return false; return false;
} }
SLibPNGWarningItem UserErrorStruct = {&ByteLoader, pFileName};
png_set_error_fn(pPNGStruct, &UserErrorStruct, LibPNGError, LibPNGWarning);
if(!FileMatchesImageType(ByteLoader)) if(!FileMatchesImageType(ByteLoader))
{ {
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo); LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
@ -169,12 +194,10 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn
Width = png_get_image_width(pPNGStruct, pPNGInfo); Width = png_get_image_width(pPNGStruct, pPNGInfo);
Height = png_get_image_height(pPNGStruct, pPNGInfo); Height = png_get_image_height(pPNGStruct, pPNGInfo);
int ColorType = png_get_color_type(pPNGStruct, pPNGInfo); ColorType = png_get_color_type(pPNGStruct, pPNGInfo);
png_byte BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo); BitDepth = png_get_bit_depth(pPNGStruct, pPNGInfo);
PngliteIncompatible = PngliteIncompatibility(pPNGStruct, pPNGInfo); PngliteIncompatible = PngliteIncompatibility(pPNGStruct, pPNGInfo);
bool PNGErr = false;
if(BitDepth == 16) if(BitDepth == 16)
{ {
png_set_strip_16(pPNGStruct); png_set_strip_16(pPNGStruct);
@ -182,64 +205,71 @@ bool LoadPNG(SImageByteBuffer &ByteLoader, const char *pFileName, int &PngliteIn
else if(BitDepth > 8) else if(BitDepth > 8)
{ {
dbg_msg("png", "non supported bit depth."); dbg_msg("png", "non supported bit depth.");
PNGErr = true; LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
} }
if(Width == 0 || Height == 0 || BitDepth == 0) if(Width == 0 || Height == 0 || BitDepth == 0)
{ {
dbg_msg("png", "image had width, height or bit depth of 0."); dbg_msg("png", "image had width, height or bit depth of 0.");
PNGErr = true; LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
} }
if(!PNGErr) if(ColorType == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(pPNGStruct);
if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8)
png_set_expand_gray_1_2_4_to_8(pPNGStruct);
if(png_get_valid(pPNGStruct, pPNGInfo, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pPNGStruct);
png_read_update_info(pPNGStruct, pPNGInfo);
ColorChannelCount = LibPNGGetColorChannelCount(ColorType);
BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo);
if(BytesInRow == Width * ColorChannelCount)
{ {
if(ColorType == PNG_COLOR_TYPE_PALETTE) pRowPointers = new png_bytep[Height];
png_set_palette_to_rgb(pPNGStruct); for(int y = 0; y < Height; ++y)
if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8)
png_set_expand_gray_1_2_4_to_8(pPNGStruct);
if(png_get_valid(pPNGStruct, pPNGInfo, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pPNGStruct);
png_read_update_info(pPNGStruct, pPNGInfo);
int ColorChannelCount = LibPNGGetColorChannelCount(ColorType);
int BytesInRow = png_get_rowbytes(pPNGStruct, pPNGInfo);
if(BytesInRow == Width * ColorChannelCount)
{ {
png_bytepp pRowPointers = new png_bytep[Height]; pRowPointers[y] = new png_byte[BytesInRow];
for(int y = 0; y < Height; ++y)
{
pRowPointers[y] = new png_byte[BytesInRow];
}
png_read_image(pPNGStruct, pRowPointers);
if(ByteLoader.m_Err == 0)
pImageBuff = (uint8_t *)malloc((size_t)Height * (size_t)Width * (size_t)ColorChannelCount * sizeof(uint8_t));
else
PNGErr = true;
for(int i = 0; i < Height; ++i)
{
if(ByteLoader.m_Err == 0)
mem_copy(&pImageBuff[i * BytesInRow], pRowPointers[i], BytesInRow);
delete[] pRowPointers[i];
}
delete[] pRowPointers;
LibPNGSetImageFormat(ImageFormat, ColorType);
} }
else
PNGErr = true; png_read_image(pPNGStruct, pRowPointers);
if(ByteLoader.m_Err == 0)
pImageBuff = (uint8_t *)malloc((size_t)Height * (size_t)Width * (size_t)ColorChannelCount * sizeof(uint8_t));
for(int i = 0; i < Height; ++i)
{
if(ByteLoader.m_Err == 0)
mem_copy(&pImageBuff[i * BytesInRow], pRowPointers[i], BytesInRow);
delete[] pRowPointers[i];
}
delete[] pRowPointers;
pRowPointers = nullptr;
if(ByteLoader.m_Err != 0)
{
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
}
LibPNGSetImageFormat(ImageFormat, ColorType);
}
else
{
LibPNGDeleteReadStruct(pPNGStruct, pPNGInfo);
return false;
} }
png_destroy_info_struct(pPNGStruct, &pPNGInfo); png_destroy_info_struct(pPNGStruct, &pPNGInfo);
png_destroy_read_struct(&pPNGStruct, NULL, NULL); png_destroy_read_struct(&pPNGStruct, NULL, NULL);
return !PNGErr; return true;
} }
static void WriteDataFromLoadedBytes(png_structp pPNGStruct, png_bytep pOutBytes, png_size_t ByteCountToWrite) static void WriteDataFromLoadedBytes(png_structp pPNGStruct, png_bytep pOutBytes, png_size_t ByteCountToWrite)