Index: src/blitter/32bpp_anim.cpp =================================================================== --- src/blitter/32bpp_anim.cpp (revision 22836) +++ src/blitter/32bpp_anim.cpp (working copy) @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "../core/math_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "../video/video_driver.hpp" #include "32bpp_anim.hpp" @@ -451,7 +452,7 @@ if (_screen.width != this->anim_buf_width || _screen.height != this->anim_buf_height) { /* The size of the screen changed; we can assume we can wipe all data from our buffer */ free(this->anim_buf); - this->anim_buf = CallocT(_screen.width * _screen.height); + this->anim_buf = CallocT(OSI64(_screen.width) * OSI64(_screen.height)); >>> _screen.width is limited to 65535, _screen.height is limited to 65535: result: won't breach. this->anim_buf_width = _screen.width; this->anim_buf_height = _screen.height; } Index: src/blitter/32bpp_optimized.cpp =================================================================== --- src/blitter/32bpp_optimized.cpp (revision 22836) +++ src/blitter/32bpp_optimized.cpp (working copy) @@ -12,6 +12,7 @@ #include "../stdafx.h" #include "../zoom_func.h" #include "../core/math_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "32bpp_optimized.hpp" /** Instantiation of the optimized 32bpp blitter factory. */ @@ -279,8 +280,8 @@ uint size = src_orig->height * src_orig->width; - dst_px_orig[z] = CallocT(size + src_orig->height * 2); - dst_n_orig[z] = CallocT(size * 2 + src_orig->height * 4 * 2); + dst_px_orig[z] = CallocT(OSI64(size) + OSI64(src_orig->height) * 2); + dst_n_orig[z] = CallocT(OSI64(size) * 2 + OSI64(src_orig->height) * 4 * 2); >>> sprites must never be this big. It would be much better to limit the size of the sprite upon loading. >>> sprites from grf files are per definition max 255 high and 65535 wide with a limit of ~65535 bytes compressed. >>> so not even close to breach it. As such, limiting PNGs would do the trick. uint32 *dst_px_ln = (uint32 *)dst_px_orig[z]; uint32 *dst_n_ln = (uint32 *)dst_n_orig[z]; Index: src/blitter/32bpp_simple.cpp =================================================================== --- src/blitter/32bpp_simple.cpp (revision 22836) +++ src/blitter/32bpp_simple.cpp (working copy) @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "../zoom_func.h" +#include "../core/overflowsafe_type.hpp" #include "32bpp_simple.hpp" #include "../table/sprites.h" @@ -95,8 +96,13 @@ Sprite *Blitter_32bppSimple::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator) { Sprite *dest_sprite; + const OverflowSafeInt64 allocation_size = sizeof(*dest_sprite) + OSI64(sprite->height) * OSI64(sprite->width) * sizeof(SpriteLoader::CommonPixel); + + /* Ensure the allocation size does not overflow. */ + if (allocation_size > OSI64(SIZE_MAX)) error("Sprite allocation size overflow"); + SpriteLoader::CommonPixel *dst; - dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sprite->height * sprite->width * sizeof(SpriteLoader::CommonPixel)); + dest_sprite = (Sprite *)allocator(allocation_size); >>> sprites must never be this big. It would be much better to limit the size of the sprite upon loading. dest_sprite->height = sprite->height; dest_sprite->width = sprite->width; Index: src/blitter/8bpp_debug.cpp =================================================================== --- src/blitter/8bpp_debug.cpp (revision 22836) +++ src/blitter/8bpp_debug.cpp (working copy) @@ -12,6 +12,7 @@ #include "../stdafx.h" #include "../zoom_func.h" #include "../core/random_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "8bpp_debug.hpp" /** Instantiation of the 8bpp debug blitter factory. */ @@ -45,8 +46,13 @@ Sprite *Blitter_8bppDebug::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator) { Sprite *dest_sprite; - dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sprite->height * sprite->width); + const OverflowSafeInt64 allocation_size = sizeof(*dest_sprite) + OSI64(sprite->height) * OSI64(sprite->width); >>> these sprites are, per definition, at most 255x65535 + /* Ensure the allocation size does not overflow. */ + if (allocation_size > OSI64(SIZE_MAX)) error("Sprite allocation size overflow"); + + dest_sprite = (Sprite *)allocator(allocation_size); + dest_sprite->height = sprite->height; dest_sprite->width = sprite->width; dest_sprite->x_offs = sprite->x_offs; Index: src/blitter/8bpp_simple.cpp =================================================================== --- src/blitter/8bpp_simple.cpp (revision 22836) +++ src/blitter/8bpp_simple.cpp (working copy) @@ -11,6 +11,7 @@ #include "../stdafx.h" #include "../zoom_func.h" +#include "../core/overflowsafe_type.hpp" #include "8bpp_simple.hpp" /** Instantiation of the simple 8bpp blitter factory. */ @@ -58,8 +59,13 @@ Sprite *Blitter_8bppSimple::Encode(SpriteLoader::Sprite *sprite, AllocatorProc *allocator) { Sprite *dest_sprite; - dest_sprite = (Sprite *)allocator(sizeof(*dest_sprite) + sprite->height * sprite->width); + const OverflowSafeInt64 allocation_size = sizeof(*dest_sprite) + OSI64(sprite->height) * OSI64(sprite->width); >>> these sprites are, per definition, at most 255x65535 + /* Ensure the allocation size does not overflow. */ + if (allocation_size > OSI64(SIZE_MAX)) error("Sprite allocation size overflow"); + + dest_sprite = (Sprite *)allocator(allocation_size); + dest_sprite->height = sprite->height; dest_sprite->width = sprite->width; dest_sprite->x_offs = sprite->x_offs; Index: src/bmp.cpp =================================================================== --- src/bmp.cpp (revision 22836) +++ src/bmp.cpp (working copy) @@ -14,6 +14,7 @@ #include "core/bitmath_func.hpp" #include "core/alloc_func.hpp" #include "core/mem_func.hpp" +#include "core/overflowsafe_type.hpp" void BmpInitializeBuffer(BmpBuffer *buffer, FILE *file) { @@ -360,7 +361,7 @@ { assert(info != NULL && data != NULL); - data->bitmap = CallocT(info->width * info->height * ((info->bpp == 24) ? 3 : 1)); + data->bitmap = CallocT(OSI64(info->width) * OSI64(info->height) * OSI64(info->bpp == 24 ? 3 : 1)); >>> limit the size of images that we assume to be valid: 16384x16384 seems more than big enough to me. /* Load image */ SetStreamOffset(buffer, info->offset); Index: src/core/alloc_func.hpp =================================================================== --- src/core/alloc_func.hpp (revision 22836) +++ src/core/alloc_func.hpp (working copy) @@ -24,17 +24,23 @@ /** * Simplified allocation function that allocates the specified number of - * elements of the given type. It also explicitly casts it to the requested - * type. - * @note throws an error when there is no memory anymore. + * elements of the given type, up to a maximum of SIZE_MAX. It also + * explicitly casts it to the requested type. + * @note throws an error when there is no memory anymore, or more than SIZE_MAX + * elements are requested. * @note the memory contains garbage data (i.e. possibly non-zero values). * @tparam T the type of the variable(s) to allocation. * @param num_elements the number of elements to allocate of the given type. * @return NULL when num_elements == 0, non-NULL otherwise. */ template -static FORCEINLINE T *MallocT(size_t num_elements) +static FORCEINLINE T *MallocT(int64 num_elements_64) { + /* Ensure that we can represent the number of elements as a size_t. */ + if (num_elements_64 < 0LL || num_elements_64 > (int64)SIZE_MAX) MallocError(SIZE_MAX); >>> SIZE_MAX = (2**64)-1, so effectively you compare num_elements_64 to -1 on 64 bits platforms. Breaks compilation! + + const size_t num_elements = num_elements_64; + /* * MorphOS cannot handle 0 elements allocations, or rather that always * returns NULL. So we do that for *all* allocations, thus causing it @@ -42,6 +48,9 @@ */ if (num_elements == 0) return NULL; + /* Ensure the size does not overflow. Division can be optimized away. */ + if (num_elements > SIZE_MAX / sizeof(T)) MallocError(SIZE_MAX); + T *t_ptr = (T*)malloc(num_elements * sizeof(T)); if (t_ptr == NULL) MallocError(num_elements * sizeof(T)); return t_ptr; @@ -49,17 +58,23 @@ /** * Simplified allocation function that allocates the specified number of - * elements of the given type. It also explicitly casts it to the requested - * type. - * @note throws an error when there is no memory anymore. + * elements of the given type, up to a maximum of SIZE_MAX. It also + * explicitly casts it to the requested type. + * @note throws an error when there is no memory anymore, or more than SIZE_MAX + * elements are requested. * @note the memory contains all zero values. * @tparam T the type of the variable(s) to allocation. * @param num_elements the number of elements to allocate of the given type. * @return NULL when num_elements == 0, non-NULL otherwise. */ template -static FORCEINLINE T *CallocT(size_t num_elements) +static FORCEINLINE T *CallocT(int64 num_elements_64) { + /* Ensure that we can represent the number of elements as a size_t. */ + if (num_elements_64 < 0LL || num_elements_64 > (int64)SIZE_MAX) MallocError(SIZE_MAX); >>> SIZE_MAX = (2**64)-1, so effectively you compare num_elements_64 to -1 on 64 bits platforms. Breaks compilation! + + const size_t num_elements = num_elements_64; + /* * MorphOS cannot handle 0 elements allocations, or rather that always * returns NULL. So we do that for *all* allocations, thus causing it @@ -67,16 +82,18 @@ */ if (num_elements == 0) return NULL; - T *t_ptr = (T*)calloc(num_elements, sizeof(T)); + T *t_ptr = (T*)calloc(num_elements, sizeof(T)); /* calloc will check for overflow from multiplication itself */ if (t_ptr == NULL) MallocError(num_elements * sizeof(T)); return t_ptr; } /** * Simplified reallocation function that allocates the specified number of - * elements of the given type. It also explicitly casts it to the requested - * type. It extends/shrinks the memory allocation given in t_ptr. - * @note throws an error when there is no memory anymore. + * elements of the given type, up to a maximum of SIZE_MAX. It also + * explicitly casts it to the requested type. It extends/shrinks the memory + * allocation given in t_ptr. + * @note throws an error when there is no memory anymore, or more than SIZE_MAX + * elements are requested. * @note the pointer to the data may change, but the data will remain valid. * @tparam T the type of the variable(s) to allocation. * @param t_ptr the previous allocation to extend/shrink. @@ -84,8 +101,13 @@ * @return NULL when num_elements == 0, non-NULL otherwise. */ template -static FORCEINLINE T *ReallocT(T *t_ptr, size_t num_elements) +static FORCEINLINE T *ReallocT(T *t_ptr, int64 num_elements_64) { + /* Ensure that we can represent the number of elements as a size_t. */ + if (num_elements_64 < 0LL || num_elements_64 > (int64)SIZE_MAX) MallocError(SIZE_MAX); >>> SIZE_MAX = (2**64)-1, so effectively you compare num_elements_64 to -1 on 64 bits platforms. Breaks compilation! + + const size_t num_elements = num_elements_64; + /* * MorphOS cannot handle 0 elements allocations, or rather that always * returns NULL. So we do that for *all* allocations, thus causing it @@ -96,12 +118,20 @@ return NULL; } + /* Ensure the size does not overflow. Division can be optimized away. */ + if (num_elements > SIZE_MAX / sizeof(T)) MallocError(SIZE_MAX); + t_ptr = (T*)realloc(t_ptr, num_elements * sizeof(T)); if (t_ptr == NULL) ReallocError(num_elements * sizeof(T)); return t_ptr; } /** alloca() has to be called in the parent function, so define AllocaM() as a macro */ -#define AllocaM(T, num_elements) ((T*)alloca((num_elements) * sizeof(T))) +#define AllocaM(T, num_elements_64) \ + /* Ensure that we can represent the number of elements as a size_t. */ \ + (((int64)(num_elements_64) < 0LL || (int64)(num_elements_64) > (int64)SIZE_MAX) && (MallocError(SIZE_MAX), NULL), \ + /* Ensure the size does not overflow. */ \ + (size_t)(num_elements_64) > SIZE_MAX / sizeof(T) && (MallocError(SIZE_MAX), NULL), \ + (T*)alloca((size_t)(num_elements_64) * sizeof(T))) #endif /* ALLOC_FUNC_HPP */ Index: src/core/overflowsafe_type.hpp =================================================================== --- src/core/overflowsafe_type.hpp (revision 22836) +++ src/core/overflowsafe_type.hpp (working copy) @@ -74,7 +74,7 @@ * @note when the multiplication would yield more than T_MAX (or less than T_MIN), * it will be T_MAX (respectively T_MIN). */ - FORCEINLINE OverflowSafeInt& operator *= (const int factor) + FORCEINLINE OverflowSafeInt& operator *= (const int64 factor) { if (factor != 0 && (T_MAX / abs(factor)) < abs(this->m_value)) { this->m_value = ((this->m_value < 0) == (factor < 0)) ? T_MAX : T_MIN ; @@ -85,11 +85,12 @@ } /* Operators for multiplication */ - FORCEINLINE OverflowSafeInt operator * (const int64 factor) const { OverflowSafeInt result = *this; result *= factor; return result; } - FORCEINLINE OverflowSafeInt operator * (const int factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } - FORCEINLINE OverflowSafeInt operator * (const uint factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } - FORCEINLINE OverflowSafeInt operator * (const uint16 factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } - FORCEINLINE OverflowSafeInt operator * (const byte factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const OverflowSafeInt& factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const int64 factor) const { OverflowSafeInt result = *this; result *= factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const int factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const uint factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const uint16 factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } + FORCEINLINE OverflowSafeInt operator * (const byte factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; } /* Operators for division */ FORCEINLINE OverflowSafeInt& operator /= (const int64 divisor) { this->m_value /= divisor; return *this; } @@ -151,5 +152,6 @@ template FORCEINLINE OverflowSafeInt operator / (byte a, OverflowSafeInt b) { return (OverflowSafeInt)a / (int)b; } typedef OverflowSafeInt OverflowSafeInt64; +typedef OverflowSafeInt64 OSI64; /* Define a shorter name */ #endif /* OVERFLOWSAFE_TYPE_HPP */ Index: src/fontcache.cpp =================================================================== --- src/fontcache.cpp (revision 22836) +++ src/fontcache.cpp (working copy) @@ -13,6 +13,7 @@ #include "fontcache.h" #include "blitter/factory.hpp" #include "core/math_func.hpp" +#include "core/overflowsafe_type.hpp" #include "table/sprites.h" #include "table/control_codes.h" @@ -1034,6 +1035,9 @@ width = max(1, slot->bitmap.width + (size == FS_NORMAL)); height = max(1, slot->bitmap.rows + (size == FS_NORMAL)); + /* Ensure the allocation size does not overflow. */ >>> glyphs must never be this big. Just limit them to say 255x255? + if (OSI64(width) * OSI64(height) > OSI64(SIZE_MAX)) error("Font glyph allocation size overflow"); + /* FreeType has rendered the glyph, now we allocate a sprite and copy the image into it */ sprite.AllocateData(width * height); sprite.width = width; Index: src/gamelog.cpp =================================================================== --- src/gamelog.cpp (revision 22836) +++ src/gamelog.cpp (working copy) @@ -11,6 +11,7 @@ #include "stdafx.h" #include "saveload/saveload.h" +#include "core/overflowsafe_type.hpp" #include "string_func.h" #include "settings_type.h" #include "gamelog_internal.h" @@ -684,7 +685,7 @@ if (IsLoggableGrfConfig(g)) n++; } - GRFList *list = (GRFList*)MallocT(sizeof(GRFList) + n * sizeof(GRFConfig*)); >>> OpenTTD limits the number of NewGRFs to ~64, so n won't be more than 64 and I doubt GRFConfig* will be significantly more than 8 bytes + GRFList *list = (GRFList*)MallocT(sizeof(GRFList) + OSI64(n) * sizeof(GRFConfig*)); list->n = 0; for (const GRFConfig *g = grfc; g != NULL; g = g->next) { Index: src/gfx.cpp =================================================================== --- src/gfx.cpp (revision 22836) +++ src/gfx.cpp (working copy) @@ -11,6 +11,7 @@ #include "stdafx.h" #include "gfx_func.h" +#include "core/overflowsafe_type.hpp" #include "fontcache.h" #include "progress.h" #include "zoom_func.h" @@ -1435,7 +1436,7 @@ void ScreenSizeChanged() { _dirty_bytes_per_line = CeilDiv(_screen.width, DIRTY_BLOCK_WIDTH); - _dirty_blocks = ReallocT(_dirty_blocks, _dirty_bytes_per_line * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT)); >>> you're doing screen.width * screen.height here, with the added bonus that both are divided by resp. 64 and 8. >>> however, as said before 65535*65535 won't be too big for a uint32, so this will not either. + _dirty_blocks = ReallocT(_dirty_blocks, OSI64(_dirty_bytes_per_line) * CeilDiv(_screen.height, DIRTY_BLOCK_HEIGHT)); /* check the dirty rect */ if (_invalid_rect.right >= _screen.width) _invalid_rect.right = _screen.width; Index: src/heightmap.cpp =================================================================== --- src/heightmap.cpp (revision 22836) +++ src/heightmap.cpp (working copy) @@ -11,6 +11,7 @@ #include "stdafx.h" #include "heightmap.h" +#include "core/overflowsafe_type.hpp" #include "clear_map.h" #include "void_map.h" #include "gui.h" @@ -142,14 +143,14 @@ return false; } + *x = png_get_image_width(png_ptr, info_ptr); + *y = png_get_image_height(png_ptr, info_ptr); + if (map != NULL) { >>> limit the size of read images to 16384x16384? >>> much better, as more than that is pointless for heightmaps anyway - *map = MallocT(png_get_image_width(png_ptr, info_ptr) * png_get_image_height(png_ptr, info_ptr)); + *map = MallocT(OSI64(*x) * OSI64(*y)); ReadHeightmapPNGImageData(*map, png_ptr, info_ptr); } - *x = png_get_image_width(png_ptr, info_ptr); - *y = png_get_image_height(png_ptr, info_ptr); - fclose(fp); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return true; @@ -243,6 +244,9 @@ return false; } + *x = info.width; + *y = info.height; + if (map != NULL) { if (!BmpReadBitmap(&buffer, &info, &data)) { ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR); @@ -251,15 +255,12 @@ return false; } >>> limit the size of read images to 16384x16384? - *map = MallocT(info.width * info.height); + *map = MallocT(OSI64(*x) * OSI64(*y)); ReadHeightmapBMPImageData(*map, &info, &data); } BmpDestroyData(&data); - *x = info.width; - *y = info.height; - fclose(f); return true; } Index: src/misc/fixedsizearray.hpp =================================================================== --- src/misc/fixedsizearray.hpp (revision 22836) +++ src/misc/fixedsizearray.hpp (working copy) @@ -13,6 +13,7 @@ #define FIXEDSIZEARRAY_HPP #include "../core/alloc_func.hpp" +#include "../core/overflowsafe_type.hpp" /** * fixed size array @@ -54,7 +55,7 @@ FixedSizeArray() { /* allocate block for header + items (don't construct items) */ >>> simple assert_compile would trigger if it would be too big at compile time. In my opinion much better. - data = (T*)((MallocT(HeaderSize + C * Tsize)) + HeaderSize); + data = (T*)((MallocT(HeaderSize + OSI64(C) * OSI64(Tsize))) + HeaderSize); SizeRef() = 0; // initial number of items RefCnt() = 1; // initial reference counter } Index: src/network/network_chat_gui.cpp =================================================================== --- src/network/network_chat_gui.cpp (revision 22836) +++ src/network/network_chat_gui.cpp (working copy) @@ -24,6 +24,7 @@ #include "../town.h" #include "../window_func.h" #include "../core/geometry_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "network.h" #include "network_client.h" #include "network_base.h" @@ -124,7 +125,7 @@ { _chatmsg_box.y = 3 * FONT_HEIGHT_NORMAL; _chatmsg_box.height = MAX_CHAT_MESSAGES * (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING) + 2; - _chatmessage_backup = ReallocT(_chatmessage_backup, _chatmsg_box.width * _chatmsg_box.height * BlitterFactoryBase::GetCurrentBlitter()->GetBytesPerPixel()); >>> width is max 65535, height is max 255*72. Won't go over the limit ever. + _chatmessage_backup = ReallocT(_chatmessage_backup, OSI64(_chatmsg_box.width) * OSI64(_chatmsg_box.height) * OSI64(BlitterFactoryBase::GetCurrentBlitter()->GetBytesPerPixel())); } /** Initialize all buffers of the chat visualisation. */ Index: src/newgrf_text.cpp =================================================================== --- src/newgrf_text.cpp (revision 22836) +++ src/newgrf_text.cpp (working copy) @@ -27,6 +27,7 @@ #include "debug.h" #include "core/alloc_type.hpp" #include "core/smallmap_type.hpp" +#include "core/overflowsafe_type.hpp" #include "language.h" #include "table/strings.h" @@ -391,7 +392,7 @@ */ char *TranslateTTDPatchCodes(uint32 grfid, uint8 language_id, const char *str, int *olen) { - char *tmp = MallocT(strlen(str) * 10 + 1); // Allocate space to allow for expansion >>> per definition str is never more than 65535 bytes; limit of GRF spec. + char *tmp = MallocT(OSI64(strlen(str)) * 10 + 1); // Allocate space to allow for expansion char *d = tmp; bool unicode = false; WChar c; @@ -535,7 +536,7 @@ grfmsg(1, "duplicate choice list string, ignoring"); d++; } else { - d = mapping->strings[index] = MallocT(strlen(str) * 10 + 1); >>> per definition str is never more than 65535 bytes; limit of GRF spec. + d = mapping->strings[index] = MallocT(OSI64(strlen(str)) * 10 + 1); } } break; Index: src/pathfinder/npf/queue.cpp =================================================================== --- src/pathfinder/npf/queue.cpp (revision 22836) +++ src/pathfinder/npf/queue.cpp (working copy) @@ -11,6 +11,7 @@ #include "../../stdafx.h" #include "../../core/alloc_func.hpp" +#include "../../core/overflowsafe_type.hpp" #include "queue.h" @@ -237,7 +238,7 @@ this->hash = hash; this->size = 0; this->num_buckets = num_buckets; - this->buckets = (HashNode*)MallocT(num_buckets * (sizeof(*this->buckets) + sizeof(*this->buckets_in_use))); >>> this function is always called with the same constant parameters. An assertion would be more than enough. + this->buckets = (HashNode*)MallocT(OSI64(num_buckets) * (sizeof(*this->buckets) + sizeof(*this->buckets_in_use))); this->buckets_in_use = (bool*)(this->buckets + num_buckets); for (i = 0; i < num_buckets; i++) this->buckets_in_use[i] = false; } Index: src/screenshot.cpp =================================================================== --- src/screenshot.cpp (revision 22836) +++ src/screenshot.cpp (working copy) @@ -17,6 +17,7 @@ #include "blitter/factory.hpp" #include "zoom_func.h" #include "core/endian_func.hpp" +#include "core/overflowsafe_type.hpp" #include "saveload/saveload.h" #include "company_func.h" #include "strings_func.h" @@ -179,7 +180,7 @@ /* Try to use 64k of memory, store between 16 and 128 lines */ uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128); // number of lines per iteration - uint8 *buff = MallocT(maxlines * w * pixelformat / 8); // buffer which is rendered to >>> maxlines is per definition not more than 128, the higher w, the lower maxlines. So with max w: 16 >>> w is per definition not more than 64*map width or height, so 64 * 2048 -> 131072 >>> pixel format is never more than 32 >>> max value before division: 16 * 131072 * 4 = 8 MB + uint8 *buff = MallocT(OSI64(maxlines) * OSI64(w) * OSI64(pixelformat) / 8); // buffer which is rendered to uint8 *line = AllocaM(uint8, bytewidth); // one line, stored to file memset(line, 0, bytewidth); @@ -376,7 +377,7 @@ maxlines = Clamp(65536 / w, 16, 128); /* now generate the bitmap bits */ - void *buff = CallocT(w * maxlines * bpp); // by default generate 128 lines at a time. >>> see above, max 8MB allocation + void *buff = CallocT(OSI64(w) * OSI64(maxlines) * OSI64(bpp)); // by default generate 128 lines at a time. y = 0; do { @@ -483,7 +484,7 @@ maxlines = Clamp(65536 / w, 16, 128); /* now generate the bitmap bits */ - uint8 *buff = CallocT(w * maxlines); // by default generate 128 lines at a time. >>> see above, max 8MB allocation + uint8 *buff = CallocT(OSI64(w) * OSI64(maxlines)); // by default generate 128 lines at a time. y = 0; do { Index: src/script/squirrel_helper.hpp =================================================================== --- src/script/squirrel_helper.hpp (revision 22836) +++ src/script/squirrel_helper.hpp (working copy) @@ -15,6 +15,7 @@ #include "squirrel.hpp" #include "../core/math_func.hpp" #include "../core/smallvec_type.hpp" +#include "../core/overflowsafe_type.hpp" #include "../economy_type.h" #include "../string_func.h" #include "squirrel_helper_type.hpp" @@ -138,7 +139,7 @@ } sq_pop(vm, 2); - Array *arr = (Array*)MallocT(sizeof(Array) + sizeof(int32) * data.Length()); >>> just limit the number of data elements in the above loop; bail out if there are too much to prevent >>> pushing openttd into an almost infinite loop. >>> even then, data is an allocated array. For it sizeof(int32) * data.Length() to overflow, you need to >>> have allocated as much memory already. That can't be done as there is other memory allocated, so you >>> can never trigger this. + Array *arr = (Array*)MallocT(sizeof(Array) + sizeof(int32) * OSI64(data.Length())); arr->size = data.Length(); memcpy(arr->array, data.Begin(), sizeof(int32) * data.Length()); Index: src/sound/win32_s.cpp =================================================================== --- src/sound/win32_s.cpp (revision 22836) +++ src/sound/win32_s.cpp (working copy) @@ -15,6 +15,7 @@ #include "../mixer.h" #include "../core/alloc_func.hpp" #include "../core/bitmath_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "win32_s.h" #include #include @@ -32,7 +33,7 @@ { hdr->dwBufferLength = _bufsize * 4; hdr->dwFlags = 0; >>> just restrict bufsize to anything sane (65536 max?) after getting the value. - hdr->lpData = MallocT(_bufsize * 4); + hdr->lpData = MallocT(OSI64(_bufsize) * 4); if (waveOutPrepareHeader(_waveout, hdr, sizeof(WAVEHDR)) != MMSYSERR_NOERROR) throw "waveOutPrepareHeader failed"; } Index: src/spritecache.cpp =================================================================== --- src/spritecache.cpp (revision 22836) +++ src/spritecache.cpp (working copy) @@ -18,6 +18,7 @@ #endif /* WITH_PNG */ #include "blitter/factory.hpp" #include "core/math_func.hpp" +#include "core/overflowsafe_type.hpp" #include "table/sprites.h" #include "table/palette_convert.h" @@ -611,7 +612,7 @@ void GfxInitSpriteMem() { /* initialize sprite cache heap */ - if (_spritecache_ptr == NULL) _spritecache_ptr = (MemBlock*)MallocT(_sprite_cache_size * 1024 * 1024); >>> _sprite_cache_size is limited to 64 by the setting validation + if (_spritecache_ptr == NULL) _spritecache_ptr = (MemBlock*)MallocT(OSI64(_sprite_cache_size) * 1024 * 1024); /* A big free block */ _spritecache_ptr->size = ((_sprite_cache_size * 1024 * 1024) - sizeof(MemBlock)) | S_FREE_MASK; Index: src/spriteloader/grf.cpp =================================================================== --- src/spriteloader/grf.cpp (revision 22836) +++ src/spriteloader/grf.cpp (working copy) @@ -95,6 +95,9 @@ if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + /* Ensure the allocation size does not overflow. */ >>> width is uint16, height is uint8 -> can never be reached. + if (OSI64(sprite->width) * OSI64(sprite->height) > OSI64(SIZE_MAX)) return WarnCorruptSprite(file_slot, file_pos, __LINE__); + sprite->AllocateData(sprite->width * sprite->height); /* When there are transparency pixels, this format has another trick.. decode it */ Index: src/spriteloader/png.cpp =================================================================== --- src/spriteloader/png.cpp (revision 22836) +++ src/spriteloader/png.cpp (working copy) @@ -12,6 +12,7 @@ #ifdef WITH_PNG #include "../stdafx.h" +#include "../core/overflowsafe_type.hpp" #include "../fileio_func.h" #include "../debug.h" #include "png.hpp" @@ -108,6 +109,13 @@ sprite->height = png_get_image_height(png_ptr, info_ptr); sprite->width = png_get_image_width(png_ptr, info_ptr); + >>> limit sprite size to something realistic; 2048 by 2048 should be way more than enough. + /* Ensure the allocation size does not overflow. */ + if (OSI64(sprite->width) * OSI64(sprite->height) > OSI64(SIZE_MAX)) { + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + return false; + } + sprite->AllocateData(sprite->width * sprite->height); } @@ -116,6 +124,7 @@ if (mask && (bit_depth != 8 || colour_type != PNG_COLOR_TYPE_PALETTE)) { DEBUG(misc, 0, "Ignoring mask for SpriteID %d as it isn't a 8 bit palette image", id); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return true; } @@ -145,7 +154,7 @@ pixelsize = sizeof(uint8); } >>> limit sprite size to something realistic; 2048 by 2048 should be way more than enough. - png_bytep row_pointer = AllocaM(png_byte, png_get_image_width(png_ptr, info_ptr) * pixelsize); + png_bytep row_pointer = AllocaM(png_byte, OSI64(png_get_image_width(png_ptr, info_ptr)) * OSI64(pixelsize)); for (i = 0; i < png_get_image_height(png_ptr, info_ptr); i++) { png_read_row(png_ptr, row_pointer, NULL); Index: src/video/dedicated_v.cpp =================================================================== --- src/video/dedicated_v.cpp (revision 22836) +++ src/video/dedicated_v.cpp (working copy) @@ -23,6 +23,7 @@ #include "../blitter/factory.hpp" #include "../company_func.h" #include "../core/random_func.hpp" +#include "../core/overflowsafe_type.hpp" #include "../saveload/saveload.h" #include "dedicated_v.h" @@ -146,7 +147,7 @@ const char *VideoDriver_Dedicated::Start(const char * const *parm) { int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(); - _dedicated_video_mem = (bpp == 0) ? NULL : MallocT(_cur_resolution.width * _cur_resolution.height * (bpp / 8)); >>> better limit the resolution of the screen upon validation + _dedicated_video_mem = (bpp == 0) ? NULL : MallocT(OSI64(_cur_resolution.width) * OSI64(_cur_resolution.height) * OSI64(bpp / 8)); _screen.width = _screen.pitch = _cur_resolution.width; _screen.height = _cur_resolution.height;