The primary purpose of this is to encourage adoption of libjpeg-turbo in downstream Windows projects that forbid the use of "deprecated" functions. libjpeg-turbo's usage of those functions was not actually unsafe, because: - libjpeg-turbo always checks the return value of fopen() and ensures that a NULL filename can never be passed to it. - libjpeg-turbo always checks the return value of getenv() and never passes a NULL argument to it. - The sprintf() calls in format_message() (jerror.c) could never overflow the destination string buffer or leave it unterminated as long as the buffer was at least JMSG_LENGTH_MAX bytes in length, as instructed. (Regardless, this commit replaces those calls with snprintf() calls.) - libjpeg-turbo never uses sscanf() to read strings or multi-byte character arrays. - Because ofb7d6e84d6a, wrjpgcom explicitly checks the bounds of the source and destination strings before calling strcat() and strcpy(). - libjpeg-turbo always ensures that the destination string is terminated when using strncpy(). (548490fe5emade this explicit.) Regarding thread safety: Technically speaking, getenv() is not thread-safe, because the returned pointer may be invalidated if another thread sets the same environment variable between the time that the first thread calls getenv() and the time that that thread uses the return value. In practice, however, this could only occur with libjpeg-turbo if: (1) A multithreaded calling application used the deprecated and undocumented TJFLAG_FORCEMMX/TJFLAG_FORCESSE/TJFLAG_FORCESSE2 flags in the TurboJPEG API or set one of the corresponding environment variables (which are only intended for testing purposes.) Since the TurboJPEG API library only ever passed string constants to putenv(), the only inherent risk (i.e. the only risk introduced by the library and not the calling application) was that the SIMD extensions may have read an incorrect value from one of the aforementioned environment variables. or (2) A multithreaded calling application modified the value of the JPEGMEM environment variable in one thread while another thread was reading the value of that environment variable (in the body of jpeg_create_compress() or jpeg_create_decompress().) Given that the libjpeg API provides a thread-safe way for applications to modify the default memory limit without using the JPEGMEM environment variable, direct modification of that environment variable by calling applications is not supported. Microsoft's implementation of getenv_s() does not claim to be thread-safe either, so this commit uses getenv_s() solely to mollify Visual Studio. New inline functions and macros (GETENV_S() and PUTENV_S) wrap getenv_s()/_putenv_s() when building for Visual Studio and getenv()/setenv() otherwise, but GETENV_S()/PUTENV_S() provide no advantages over getenv()/setenv() other than parameter validation. They are implemented solely for convenience. Technically speaking, strerror() is not thread-safe, because the returned pointer may be invalidated if another thread changes the locale and/or calls strerror() between the time that the first thread calls strerror() and the time that that thread uses the return value. In practice, however, this could only occur with libjpeg-turbo if a multithreaded calling application encountered a file I/O error in tjLoadImage() or tjSaveImage(). Since both of those functions immediately copy the string returned from strerror() into a thread-local buffer, the risk is minimal, and the worst case would involve an incorrect error string being reported to the calling application. Regardless, this commit uses strerror_s() in the TurboJPEG API library when building for Visual Studio. Note that strerror_r() could have been used on Un*x systems, but it would have been necessary to handle both the POSIX and GNU implementations of that function and perform widespread compatibility testing. Such is left as an exercise for another day. Fixes #568
252 lines
7.7 KiB
C
252 lines
7.7 KiB
C
/*
|
|
* jerror.c
|
|
*
|
|
* This file was part of the Independent JPEG Group's software:
|
|
* Copyright (C) 1991-1998, Thomas G. Lane.
|
|
* libjpeg-turbo Modifications:
|
|
* Copyright (C) 2022, D. R. Commander.
|
|
* For conditions of distribution and use, see the accompanying README.ijg
|
|
* file.
|
|
*
|
|
* This file contains simple error-reporting and trace-message routines.
|
|
* These are suitable for Unix-like systems and others where writing to
|
|
* stderr is the right thing to do. Many applications will want to replace
|
|
* some or all of these routines.
|
|
*
|
|
* If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile,
|
|
* you get a Windows-specific hack to display error messages in a dialog box.
|
|
* It ain't much, but it beats dropping error messages into the bit bucket,
|
|
* which is what happens to output to stderr under most Windows C compilers.
|
|
*
|
|
* These routines are used by both the compression and decompression code.
|
|
*/
|
|
|
|
/* this is not a core library module, so it doesn't define JPEG_INTERNALS */
|
|
#include "jinclude.h"
|
|
#include "jpeglib.h"
|
|
#include "jversion.h"
|
|
#include "jerror.h"
|
|
|
|
#ifdef USE_WINDOWS_MESSAGEBOX
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#ifndef EXIT_FAILURE /* define exit() codes if not provided */
|
|
#define EXIT_FAILURE 1
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Create the message string table.
|
|
* We do this from the master message list in jerror.h by re-reading
|
|
* jerror.h with a suitable definition for macro JMESSAGE.
|
|
* The message table is made an external symbol just in case any applications
|
|
* want to refer to it directly.
|
|
*/
|
|
|
|
#define JMESSAGE(code, string) string,
|
|
|
|
const char * const jpeg_std_message_table[] = {
|
|
#include "jerror.h"
|
|
NULL
|
|
};
|
|
|
|
|
|
/*
|
|
* Error exit handler: must not return to caller.
|
|
*
|
|
* Applications may override this if they want to get control back after
|
|
* an error. Typically one would longjmp somewhere instead of exiting.
|
|
* The setjmp buffer can be made a private field within an expanded error
|
|
* handler object. Note that the info needed to generate an error message
|
|
* is stored in the error object, so you can generate the message now or
|
|
* later, at your convenience.
|
|
* You should make sure that the JPEG object is cleaned up (with jpeg_abort
|
|
* or jpeg_destroy) at some point.
|
|
*/
|
|
|
|
METHODDEF(void)
|
|
error_exit(j_common_ptr cinfo)
|
|
{
|
|
/* Always display the message */
|
|
(*cinfo->err->output_message) (cinfo);
|
|
|
|
/* Let the memory manager delete any temp files before we die */
|
|
jpeg_destroy(cinfo);
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Actual output of an error or trace message.
|
|
* Applications may override this method to send JPEG messages somewhere
|
|
* other than stderr.
|
|
*
|
|
* On Windows, printing to stderr is generally completely useless,
|
|
* so we provide optional code to produce an error-dialog popup.
|
|
* Most Windows applications will still prefer to override this routine,
|
|
* but if they don't, it'll do something at least marginally useful.
|
|
*
|
|
* NOTE: to use the library in an environment that doesn't support the
|
|
* C stdio library, you may have to delete the call to fprintf() entirely,
|
|
* not just not use this routine.
|
|
*/
|
|
|
|
METHODDEF(void)
|
|
output_message(j_common_ptr cinfo)
|
|
{
|
|
char buffer[JMSG_LENGTH_MAX];
|
|
|
|
/* Create the message */
|
|
(*cinfo->err->format_message) (cinfo, buffer);
|
|
|
|
#ifdef USE_WINDOWS_MESSAGEBOX
|
|
/* Display it in a message dialog box */
|
|
MessageBox(GetActiveWindow(), buffer, "JPEG Library Error",
|
|
MB_OK | MB_ICONERROR);
|
|
#else
|
|
/* Send it to stderr, adding a newline */
|
|
fprintf(stderr, "%s\n", buffer);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Decide whether to emit a trace or warning message.
|
|
* msg_level is one of:
|
|
* -1: recoverable corrupt-data warning, may want to abort.
|
|
* 0: important advisory messages (always display to user).
|
|
* 1: first level of tracing detail.
|
|
* 2,3,...: successively more detailed tracing messages.
|
|
* An application might override this method if it wanted to abort on warnings
|
|
* or change the policy about which messages to display.
|
|
*/
|
|
|
|
METHODDEF(void)
|
|
emit_message(j_common_ptr cinfo, int msg_level)
|
|
{
|
|
struct jpeg_error_mgr *err = cinfo->err;
|
|
|
|
if (msg_level < 0) {
|
|
/* It's a warning message. Since corrupt files may generate many warnings,
|
|
* the policy implemented here is to show only the first warning,
|
|
* unless trace_level >= 3.
|
|
*/
|
|
if (err->num_warnings == 0 || err->trace_level >= 3)
|
|
(*err->output_message) (cinfo);
|
|
/* Always count warnings in num_warnings. */
|
|
err->num_warnings++;
|
|
} else {
|
|
/* It's a trace message. Show it if trace_level >= msg_level. */
|
|
if (err->trace_level >= msg_level)
|
|
(*err->output_message) (cinfo);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Format a message string for the most recent JPEG error or message.
|
|
* The message is stored into buffer, which should be at least JMSG_LENGTH_MAX
|
|
* characters. Note that no '\n' character is added to the string.
|
|
* Few applications should need to override this method.
|
|
*/
|
|
|
|
METHODDEF(void)
|
|
format_message(j_common_ptr cinfo, char *buffer)
|
|
{
|
|
struct jpeg_error_mgr *err = cinfo->err;
|
|
int msg_code = err->msg_code;
|
|
const char *msgtext = NULL;
|
|
const char *msgptr;
|
|
char ch;
|
|
boolean isstring;
|
|
|
|
/* Look up message string in proper table */
|
|
if (msg_code > 0 && msg_code <= err->last_jpeg_message) {
|
|
msgtext = err->jpeg_message_table[msg_code];
|
|
} else if (err->addon_message_table != NULL &&
|
|
msg_code >= err->first_addon_message &&
|
|
msg_code <= err->last_addon_message) {
|
|
msgtext = err->addon_message_table[msg_code - err->first_addon_message];
|
|
}
|
|
|
|
/* Defend against bogus message number */
|
|
if (msgtext == NULL) {
|
|
err->msg_parm.i[0] = msg_code;
|
|
msgtext = err->jpeg_message_table[0];
|
|
}
|
|
|
|
/* Check for string parameter, as indicated by %s in the message text */
|
|
isstring = FALSE;
|
|
msgptr = msgtext;
|
|
while ((ch = *msgptr++) != '\0') {
|
|
if (ch == '%') {
|
|
if (*msgptr == 's') isstring = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Format the message into the passed buffer */
|
|
if (isstring)
|
|
snprintf(buffer, JMSG_LENGTH_MAX, msgtext, err->msg_parm.s);
|
|
else
|
|
snprintf(buffer, JMSG_LENGTH_MAX, msgtext,
|
|
err->msg_parm.i[0], err->msg_parm.i[1],
|
|
err->msg_parm.i[2], err->msg_parm.i[3],
|
|
err->msg_parm.i[4], err->msg_parm.i[5],
|
|
err->msg_parm.i[6], err->msg_parm.i[7]);
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset error state variables at start of a new image.
|
|
* This is called during compression startup to reset trace/error
|
|
* processing to default state, without losing any application-specific
|
|
* method pointers. An application might possibly want to override
|
|
* this method if it has additional error processing state.
|
|
*/
|
|
|
|
METHODDEF(void)
|
|
reset_error_mgr(j_common_ptr cinfo)
|
|
{
|
|
cinfo->err->num_warnings = 0;
|
|
/* trace_level is not reset since it is an application-supplied parameter */
|
|
cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill in the standard error-handling methods in a jpeg_error_mgr object.
|
|
* Typical call is:
|
|
* struct jpeg_compress_struct cinfo;
|
|
* struct jpeg_error_mgr err;
|
|
*
|
|
* cinfo.err = jpeg_std_error(&err);
|
|
* after which the application may override some of the methods.
|
|
*/
|
|
|
|
GLOBAL(struct jpeg_error_mgr *)
|
|
jpeg_std_error(struct jpeg_error_mgr *err)
|
|
{
|
|
err->error_exit = error_exit;
|
|
err->emit_message = emit_message;
|
|
err->output_message = output_message;
|
|
err->format_message = format_message;
|
|
err->reset_error_mgr = reset_error_mgr;
|
|
|
|
err->trace_level = 0; /* default = no tracing */
|
|
err->num_warnings = 0; /* no warnings emitted yet */
|
|
err->msg_code = 0; /* may be useful as a flag for "no error" */
|
|
|
|
/* Initialize message table pointers */
|
|
err->jpeg_message_table = jpeg_std_message_table;
|
|
err->last_jpeg_message = (int)JMSG_LASTMSGCODE - 1;
|
|
|
|
err->addon_message_table = NULL;
|
|
err->first_addon_message = 0; /* for safety */
|
|
err->last_addon_message = 0;
|
|
|
|
return err;
|
|
}
|