Fixed bug 3760 - RWops doesn't check for integer overflow when stdio_fseek only supports 32 bits

Simon Hug

When RWops seeks with fseek or fseeko it uses the types long or off_t which can be 32 bits on some platforms. stdio_seek does not check if the 64-bit integer for the offset fits into a 32-bit integer. Offsets equal or larger than 2 GiB will have implementation-defined behavior and failure states would be very confusing to debug.

The attached patch adds range checking by using the macros from limits.h for long type and some bit shifting for off_t because POSIX couldn't be bothered to specify min and max macros.

It also defines HAVE_FSEEKI64 in SDL_config_windows.h so that the Windows function gets picked up automatically with the default config.

And there's an additional error message for when ftell fails.
This commit is contained in:
Sam Lantinga 2017-09-09 08:36:37 -07:00
parent 125d8f59fc
commit f465f24d73
14 changed files with 139 additions and 86 deletions

View file

@ -32,6 +32,13 @@
#include "../core/windows/SDL_windows.h"
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
/* This file provides a general interface for SDL to read and write
data sources. It can easily be extended to files, memory, etc.
@ -306,6 +313,19 @@ windows_file_close(SDL_RWops * context)
#define fseek fseeko64
#define ftell ftello64
#elif defined(HAVE_FSEEKO)
#if defined(OFF_MIN) && defined(OFF_MAX)
#define FSEEK_OFF_MIN OFF_MIN
#define FSEEK_OFF_MAX OFF_MAX
#elif defined(HAVE_LIMITS_H)
/* POSIX doesn't specify the minimum and maximum macros for off_t so
* we have to improvise and dance around implementation-defined
* behavior. This may fail if the off_t type has padding bits or
* is not a two's-complement representation. The compilers will detect
* and eliminate the dead code if off_t has 64 bits.
*/
#define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
#define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX) - 1)
#endif
#define fseek_off_t off_t
#define fseek fseeko
#define ftell ftello
@ -314,6 +334,10 @@ windows_file_close(SDL_RWops * context)
#define fseek _fseeki64
#define ftell _ftelli64
#else
#ifdef HAVE_LIMITS_H
#define FSEEK_OFF_MIN LONG_MIN
#define FSEEK_OFF_MAX LONG_MAX
#endif
#define fseek_off_t long
#endif
@ -337,8 +361,18 @@ stdio_size(SDL_RWops * context)
static Sint64 SDLCALL
stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
{
#if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
return SDL_SetError("Seek offset out of range");
}
#endif
if (fseek(context->hidden.stdio.fp, (fseek_off_t)offset, whence) == 0) {
return ftell(context->hidden.stdio.fp);
Sint64 pos = ftell(context->hidden.stdio.fp);
if (pos < 0) {
return SDL_SetError("Couldn't get stream offset");
}
return pos;
}
return SDL_Error(SDL_EFSEEK);
}