shithub: choc

Download patch

ref: a9d9335b20a0b708fae1b978f70348aec998356a
parent: 17c14e1fad6dc277a6e58e4f421d5c65e210d1fe
author: Simon Howard <fraggle@gmail.com>
date: Tue Apr 1 17:49:16 EDT 2014

textscreen: Use safe string functions.

Define TXT_{StringCopy,StringConcat,snprintf,vsnprintf} as analogs of
the m_misc.c versions so that the textscreen library does not need a
dependency on the Doom code, and change all textscreen code to use
these instead of unsafe functions. This fixes #372.

--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -115,7 +115,7 @@
     }
     else
     {
-        TXT_GetKeyDescription(*key_input->variable, buf);
+        TXT_GetKeyDescription(*key_input->variable, buf, sizeof(buf));
     }
 
     TXT_SetWidgetBG(key_input);
--- a/textscreen/examples/calculator.c
+++ b/textscreen/examples/calculator.c
@@ -49,7 +49,7 @@
 {
     char buf[20];
 
-    sprintf(buf, "  %i", input_value);
+    TXT_snprintf(buf, sizeof(buf), "  %i", input_value);
     TXT_SetLabel(input_box, buf);
 }
 
@@ -76,7 +76,7 @@
     val_copy = malloc(sizeof(int));
     *val_copy = value;
 
-    sprintf(buf, "  %i  ", value);
+    TXT_snprintf(buf, sizeof(buf), "  %i  ", value);
 
     TXT_AddWidget(table, TXT_NewButton2(buf, InsertNumber, val_copy));
 }
@@ -98,7 +98,7 @@
     op_copy = malloc(sizeof(operator_t));
     *op_copy = op;
 
-    sprintf(buf, "  %s  ", label);
+    TXT_snprintf(buf, sizeof(buf), "  %s  ", label);
 
     TXT_AddWidget(table, TXT_NewButton2(buf, Operator, op_copy));
 }
--- a/textscreen/examples/guitest.c
+++ b/textscreen/examples/guitest.c
@@ -76,13 +76,13 @@
 {
     char buf[40];
     
-    strcpy(buf, " Current value: ");
+    TXT_StringCopy(buf, " Current value: ", sizeof(buf));
     if (cheesy)
     {
-        strcat(buf, "Cheesy ");
+        TXT_StringConcat(buf, "Cheesy ", sizeof(buf));
     }
-    strcat(buf, radio_values[radiobutton_value]);
-    strcat(buf, "\n");
+    TXT_StringConcat(buf, radio_values[radiobutton_value], sizeof(buf));
+    TXT_StringConcat(buf, "\n", sizeof(buf));
 
     TXT_SetLabel(value_label, buf);
 }
@@ -119,11 +119,11 @@
 
     for (i=0; i<5; ++i)
     {
-        sprintf(buf, "Option %i in a table:", i + 1);
+        TXT_snprintf(buf, sizeof(buf), "Option %i in a table:", i + 1);
         TXT_AddWidget(table, TXT_NewLabel(buf));
-        sprintf(buf, " Button %i-1 ", i + 1);
+        TXT_snprintf(buf, sizeof(buf), " Button %i-1 ", i + 1);
         TXT_AddWidget(table, TXT_NewButton(buf));
-        sprintf(buf, " Button %i-2 ", i + 1);
+        TXT_snprintf(buf, sizeof(buf), " Button %i-2 ", i + 1);
         TXT_AddWidget(table, TXT_NewButton(buf));
     }
 
--- a/textscreen/txt_desktop.c
+++ b/textscreen/txt_desktop.c
@@ -263,7 +263,7 @@
             n = y * 16 + x;
 
             TXT_GotoXY(x * 5, y);
-            sprintf(buf, "%02x   ", n);
+            TXT_snprintf(buf, sizeof(buf), "%02x   ", n);
             TXT_Puts(buf);
 
             // Write the character directly to the screen memory buffer:
--- a/textscreen/txt_fileselect.c
+++ b/textscreen/txt_fileselect.c
@@ -217,6 +217,7 @@
     unsigned int result_len = 1;
     unsigned int i;
     char *result, *out;
+    size_t out_len, offset;
 
     if (extensions == NULL)
     {
@@ -229,15 +230,18 @@
     }
 
     result = malloc(result_len);
-    out = result;
+    out = result; out_len = result_len;
 
     for (i = 0; extensions[i] != NULL; ++i)
     {
         // .wad files (*.wad)\0
-        out += 1 + sprintf(out, "%s files (*.%s)",
-                           extensions[i], extensions[i]);
+        offset = TXT_snprintf(out, out_len, "%s files (*.%s)",
+                              extensions[i], extensions[i]);
+        out += offset + 1; out_len -= offset + 1;
+
         // *.wad\0
-        out += 1 + sprintf(out, "*.%s", extensions[i]);
+        offset = TXT_snprintf(out, out_len, "*.%s", extensions[i]);
+        out_len += offset + 1; out_len -= offset + 1;
     }
 
     *out = '\0';
@@ -378,19 +382,19 @@
     }
 
     result = malloc(result_len);
-    strcpy(result, "{");
+    TXT_StringCopy(result, "{", result_len);
 
     for (i = 0; extensions[i] != NULL; ++i)
     {
         escaped = EscapedString(extensions[i]);
-        strcat(result, escaped);
+        TXT_StringConcat(result, escaped, result_len);
         free(escaped);
 
         if (extensions[i + 1] != NULL)
-            strcat(result, ",");
+            TXT_StringConcat(result, ",", result_len);
     }
 
-    strcat(result, "}");
+    TXT_StringConcat(result, "}", result_len);
 
     return result;
 }
@@ -427,19 +431,19 @@
 
     result = malloc(result_len);
 
-    strcpy(result, chooser);
+    TXT_StringCopy(result, chooser, result_len);
 
     if (window_title != NULL)
     {
-        strcat(result, " with prompt ");
-        strcat(result, window_title);
+        TXT_StringConcat(result, " with prompt ", result_len);
+        TXT_StringConcat(result, window_title, result_len);
         free(window_title);
     }
 
     if (ext_list != NULL)
     {
-        strcat(result, "of type ");
-        strcat(result, ext_list);
+        TXT_StringConcat(result, "of type ", result_len);
+        TXT_StringConcat(result, ext_list, result_len);
         free(ext_list);
     }
 
@@ -449,11 +453,13 @@
 static char *GenerateAppleScript(char *window_title, char **extensions)
 {
     char *selector, *result;
+    size_t result_len;
 
     selector = GenerateSelector(window_title, extensions);
 
-    result = malloc(strlen(APPLESCRIPT_WRAPPER) + strlen(selector));
-    sprintf(result, APPLESCRIPT_WRAPPER, selector);
+    result_len = strlen(APPLESCRIPT_WRAPPER) + strlen(selector);
+    result = malloc(result_len);
+    TXT_snprintf(result, result_len, APPLESCRIPT_WRAPPER, selector);
     free(selector);
 
     return result;
@@ -515,6 +521,7 @@
 char *TXT_SelectFile(char *window_title, char **extensions)
 {
     unsigned int i;
+    size_t len;
     char *result;
     char **argv;
     int argc;
@@ -531,8 +538,9 @@
 
     if (window_title != NULL)
     {
-        argv[argc] = malloc(10 + strlen(window_title));
-        sprintf(argv[argc], "--title=%s", window_title);
+        len = 10 + strlen(window_title);
+        argv[argc] = malloc(len);
+        TXT_snprintf(argv[argc], len, "--title=%s", window_title);
         ++argc;
     }
 
@@ -545,9 +553,10 @@
     {
         for (i = 0; extensions[i] != NULL; ++i)
         {
-            argv[argc] = malloc(30 + strlen(extensions[i]) * 2);
-            sprintf(argv[argc], "--file-filter=.%s | *.%s",
-                    extensions[i], extensions[i]);
+            len = 30 + strlen(extensions[i]) * 2;
+            argv[argc] = malloc(len);
+            TXT_snprintf(argv[argc], len, "--file-filter=.%s | *.%s",
+                         extensions[i], extensions[i]);
             ++argc;
         }
     }
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -44,18 +44,17 @@
 
         if (*value != NULL)
         {
-            strncpy(inputbox->buffer, *value, inputbox->size);
-            inputbox->buffer[inputbox->size] = '\0';
+            TXT_StringCopy(inputbox->buffer, *value, inputbox->size);
         }
         else
         {
-            strcpy(inputbox->buffer, "");
+            TXT_StringCopy(inputbox->buffer, "", inputbox->buffer_len);
         }
     }
     else if (inputbox->widget.widget_class == &txt_int_inputbox_class)
     {
         int *value = (int *) inputbox->value;
-        sprintf(inputbox->buffer, "%i", *value);
+        TXT_snprintf(inputbox->buffer, inputbox->buffer_len, "%i", *value);
     }
 }
 
@@ -65,7 +64,7 @@
 
     if (inputbox->widget.widget_class == &txt_int_inputbox_class)
     {
-        strcpy(inputbox->buffer, "");
+        TXT_StringCopy(inputbox->buffer, "", inputbox->buffer_len);
     }
     else
     {
@@ -322,7 +321,8 @@
     // 'size' is the maximum number of characters that can be entered,
     // but for a UTF-8 string, each character can take up to four
     // characters.
-    inputbox->buffer = malloc(size * 4 + 1);
+    inputbox->buffer_len = size * 4 + 1;
+    inputbox->buffer = malloc(inputbox->buffer_len);
     inputbox->editing = 0;
 
     return inputbox;
--- a/textscreen/txt_inputbox.h
+++ b/textscreen/txt_inputbox.h
@@ -45,6 +45,7 @@
 {
     txt_widget_t widget;
     char *buffer;
+    size_t buffer_len;
     unsigned int size;
     int editing;
     void *value;
--- a/textscreen/txt_main.h
+++ b/textscreen/txt_main.h
@@ -109,55 +109,55 @@
 
 // Initialize the screen
 // Returns 1 if successful, 0 if failed.
-
 int TXT_Init(void);
 
 // Shut down text mode emulation
-
 void TXT_Shutdown(void);
 
 // Get a pointer to the buffer containing the raw screen data.
-
 unsigned char *TXT_GetScreenData(void);
 
 // Update an area of the screen
-
 void TXT_UpdateScreenArea(int x, int y, int w, int h);
 
 // Update the whole screen
-
 void TXT_UpdateScreen(void);
 
 // Read a character from the keyboard
-
 int TXT_GetChar(void);
 
 // Read the current state of modifier keys that are held down.
-
 int TXT_GetModifierState(txt_modifier_t mod);
 
 // Provides a short description of a key code, placing into the 
 // provided buffer.
+void TXT_GetKeyDescription(int key, char *buf, size_t buf_len);
 
-void TXT_GetKeyDescription(int key, char *buf);
-
 // Retrieve the current position of the mouse
-
 void TXT_GetMousePosition(int *x, int *y);
 
 // Sleep until an event is received or the screen needs updating
 // Optional timeout in ms (timeout == 0 : sleep forever)
-
 void TXT_Sleep(int timeout);
 
 // Controls whether keys are returned from TXT_GetChar based on keyboard
 // mapping, or raw key code.
-
 void TXT_EnableKeyMapping(int enable);
 
 // Set the window title of the window containing the text mode screen
-
 void TXT_SetWindowTitle(char *title);
+
+// Safe string copy.
+void TXT_StringCopy(char *dest, const char *src, size_t dest_len);
+
+// Safe string concatenate.
+void TXT_StringConcat(char *dest, const char *src, size_t dest_len);
+
+// Safe version of vsnprintf().
+int TXT_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args);
+
+// Safe version of snprintf().
+int TXT_snprintf(char *buf, size_t buf_len, const char *s, ...);
 
 #endif /* #ifndef TXT_MAIN_H */
 
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -751,7 +751,7 @@
     }
 }
 
-void TXT_GetKeyDescription(int key, char *buf)
+void TXT_GetKeyDescription(int key, char *buf, size_t buf_len)
 {
     const char *keyname;
 
@@ -759,15 +759,15 @@
 
     if (keyname != NULL)
     {
-        strcpy(buf, keyname);
+        TXT_StringCopy(buf, keyname, buf_len);
     }
     else if (isprint(key))
     {
-        sprintf(buf, "%c", toupper(key));
+        TXT_snprintf(buf, buf_len, "%c", toupper(key));
     }
     else
     {
-        sprintf(buf, "??%i", key);
+        TXT_snprintf(buf, buf_len, "??%i", key);
     }
 }
 
@@ -868,5 +868,64 @@
 {
     event_callback = callback;
     event_callback_data = user_data;
+}
+
+// Safe string functions.
+
+void TXT_StringCopy(char *dest, const char *src, size_t dest_len)
+{
+    if (dest_len < 1)
+    {
+        return;
+    }
+
+    dest[dest_len - 1] = '\0';
+    strncpy(dest, src, dest_len - 1);
+}
+
+void TXT_StringConcat(char *dest, const char *src, size_t dest_len)
+{
+    size_t offset;
+
+    offset = strlen(dest);
+    if (offset > dest_len)
+    {
+        offset = dest_len;
+    }
+
+    TXT_StringCopy(dest + offset, src, dest_len - offset);
+}
+
+// On Windows, vsnprintf() is _vsnprintf().
+#ifdef _WIN32
+#if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
+#define vsnprintf _vsnprintf
+#endif
+#endif
+
+// Safe, portable vsnprintf().
+int TXT_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
+{
+    if (buf_len < 1)
+    {
+        return 0;
+    }
+
+    // Windows (and other OSes?) has a vsnprintf() that doesn't always
+    // append a trailing \0. So we must do it, and write into a buffer
+    // that is one byte shorter; otherwise this function is unsafe.
+    buf[buf_len - 1] = '\0';
+    return vsnprintf(buf, buf_len - 1, s, args);
+}
+
+// Safe, portable snprintf().
+int TXT_snprintf(char *buf, size_t buf_len, const char *s, ...)
+{
+    va_list args;
+    int result;
+    va_start(args, s);
+    result = TXT_vsnprintf(buf, buf_len, s, args);
+    va_end(args);
+    return result;
 }
 
--- a/textscreen/txt_spinctrl.c
+++ b/textscreen/txt_spinctrl.c
@@ -35,7 +35,7 @@
 
 // Generate the format string to be used for displaying floats
 
-static void FloatFormatString(float step, char *buf)
+static void FloatFormatString(float step, char *buf, size_t buf_len)
 {
     int precision;
 
@@ -43,11 +43,11 @@
 
     if (precision > 0)
     {
-        sprintf(buf, "%%.%if", precision);
+        TXT_snprintf(buf, buf_len, "%%.%if", precision);
     }
     else
     {
-        strcpy(buf, "%.1f");
+        TXT_StringCopy(buf, "%.1f", buf_len);
     }
 }
 
@@ -57,7 +57,7 @@
 {
     char buf[25];
 
-    sprintf(buf, "%i", val);
+    TXT_snprintf(buf, sizeof(buf), "%i", val);
 
     return strlen(buf);
 }
@@ -132,12 +132,14 @@
     switch (spincontrol->type)
     {
         case TXT_SPINCONTROL_INT:
-            sprintf(spincontrol->buffer, "%i", spincontrol->value->i);
+            TXT_snprintf(spincontrol->buffer, spincontrol->buffer_len,
+                         "%i", spincontrol->value->i);
             break;
 
         case TXT_SPINCONTROL_FLOAT:
-            FloatFormatString(spincontrol->step.f, format);
-            sprintf(spincontrol->buffer, format, spincontrol->value->f);
+            FloatFormatString(spincontrol->step.f, format, sizeof(format));
+            TXT_snprintf(spincontrol->buffer, spincontrol->buffer_len,
+                         format, spincontrol->value->f);
             break;
     }
 }
@@ -300,7 +302,7 @@
         if (key == KEY_ENTER)
         {
             spincontrol->editing = 1;
-            strcpy(spincontrol->buffer, "");
+            TXT_StringCopy(spincontrol->buffer, "", spincontrol->buffer_len);
             return 1;
         }
         if (key == KEY_LEFTARROW)
@@ -387,8 +389,9 @@
     spincontrol = malloc(sizeof(txt_spincontrol_t));
 
     TXT_InitWidget(spincontrol, &txt_spincontrol_class);
-    spincontrol->buffer = malloc(25);
-    strcpy(spincontrol->buffer, "");
+    spincontrol->buffer_len = 25;
+    spincontrol->buffer = malloc(spincontrol->buffer_len);
+    TXT_StringCopy(spincontrol->buffer, "", spincontrol->buffer_len);
     spincontrol->editing = 0;
 
     return spincontrol;
--- a/textscreen/txt_spinctrl.h
+++ b/textscreen/txt_spinctrl.h
@@ -53,6 +53,7 @@
     union { float f; int i; } min, max, *value, step; 
     int editing;
     char *buffer;
+    size_t buffer_len;
 };
 
 /**
--- a/textscreen/txt_utf8.c
+++ b/textscreen/txt_utf8.c
@@ -19,6 +19,7 @@
 // 02111-1307, USA.
 //
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
--- a/textscreen/txt_utf8.h
+++ b/textscreen/txt_utf8.h
@@ -22,6 +22,8 @@
 #ifndef TXT_UTF8_H
 #define TXT_UTF8_H
 
+#include <stdarg.h>
+
 char *TXT_EncodeUTF8(char *p, unsigned int c);
 unsigned int TXT_DecodeUTF8(const char **ptr);
 unsigned int TXT_UTF8_Strlen(const char *s);
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -511,7 +511,7 @@
     va_list args;
 
     va_start(args, message);
-    vsnprintf(buf, sizeof(buf), message, args);
+    TXT_vsnprintf(buf, sizeof(buf), message, args);
     va_end(args);
 
     window = TXT_NewWindow(title);
--- a/textscreen/txt_window_action.c
+++ b/textscreen/txt_window_action.c
@@ -35,7 +35,7 @@
     TXT_CAST_ARG(txt_window_action_t, action);
     char buf[10];
 
-    TXT_GetKeyDescription(action->key, buf);
+    TXT_GetKeyDescription(action->key, buf, sizeof(buf));
 
     // Width is label length, plus key description length, plus '='
     // and two surrounding spaces.
@@ -49,7 +49,7 @@
     TXT_CAST_ARG(txt_window_action_t, action);
     char buf[10];
 
-    TXT_GetKeyDescription(action->key, buf);
+    TXT_GetKeyDescription(action->key, buf, sizeof(buf));
 
     if (TXT_HoveringOverWidget(action))
     {