Przeglądaj źródła

ADDED: Multiply security checks to avoid crashes on wrongly provided string data #4751

- REVIEWED: Checking `NULL` input on functions getting `const char *text`, to avoid crashes
- REVIEWED: `strcpy()` usage, prioritize `strncpy()` with limited copy to buffer size
- REPLACED: `strlen()` by `TextLength()` on [rtext] module
- REVIEWED: Replaced some early returns (but keeping others, for easier code following)
Ray 3 dni temu
rodzic
commit
2a566544d4
8 zmienionych plików z 275 dodań i 234 usunięć
  1. 9 6
      src/raudio.c
  2. 1 1
      src/raylib.h
  3. 37 31
      src/rcore.c
  4. 4 4
      src/rlgl.h
  5. 7 9
      src/rmodels.c
  6. 204 173
      src/rtext.c
  7. 11 8
      src/rtextures.c
  8. 2 2
      src/utils.c

+ 9 - 6
src/raudio.c

@@ -1140,7 +1140,7 @@ bool ExportWaveAsCode(Wave wave, const char *fileName)
 
     // Get file name from path and convert variable name to uppercase
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
     for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; }
 
     // Add wave information
@@ -2739,11 +2739,13 @@ static const char *GetFileExtension(const char *fileName)
     return dot;
 }
 
-// String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
+// String pointer reverse break: returns right-most occurrence of charset in text
+static const char *strprbrk(const char *text, const char *charset)
 {
     const char *latestMatch = NULL;
-    for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
+ 
+    for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
+    
     return latestMatch;
 }
 
@@ -2766,7 +2768,7 @@ static const char *GetFileNameWithoutExt(const char *filePath)
     static char fileName[MAX_FILENAMEWITHOUTEXT_LENGTH] = { 0 };
     memset(fileName, 0, MAX_FILENAMEWITHOUTEXT_LENGTH);
 
-    if (filePath != NULL) strcpy(fileName, GetFileName(filePath));   // Get filename with extension
+    if (filePath != NULL) strncpy(fileName, GetFileName(filePath), MAX_FILENAMEWITHOUTEXT_LENGTH - 1); // Get filename with extension
 
     int size = (int)strlen(fileName);   // Get size in bytes
 
@@ -2864,7 +2866,8 @@ static bool SaveFileText(const char *fileName, char *text)
 
         if (file != NULL)
         {
-            int count = fprintf(file, "%s", text);
+            int count = 0;
+            if (text != NULL) count = fprintf(file, "%s", text);
 
             if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
             else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);

+ 1 - 1
src/raylib.h

@@ -1145,7 +1145,7 @@ RLAPI const char *GetPrevDirectoryPath(const char *dirPath);        // Get previ
 RLAPI const char *GetWorkingDirectory(void);                        // Get current working directory (uses static string)
 RLAPI const char *GetApplicationDirectory(void);                    // Get the directory of the running application (uses static string)
 RLAPI int MakeDirectory(const char *dirPath);                       // Create directories (including full path requested), returns 0 on success
-RLAPI bool ChangeDirectory(const char *dir);                        // Change working directory, return true on success
+RLAPI bool ChangeDirectory(const char *dirPath);                    // Change working directory, return true on success
 RLAPI bool IsPathFile(const char *path);                            // Check if a given path is a file or a directory
 RLAPI bool IsFileNameValid(const char *fileName);                   // Check if fileName is valid for the platform/OS
 RLAPI FilePathList LoadDirectoryFiles(const char *dirPath);         // Load directory filepaths

+ 37 - 31
src/rcore.c

@@ -113,7 +113,7 @@
 
 #include <stdlib.h>                 // Required for: srand(), rand(), atexit()
 #include <stdio.h>                  // Required for: sprintf() [Used in OpenURL()]
-#include <string.h>                 // Required for: strlen(), strcpy(), strcmp(), strrchr(), memset()
+#include <string.h>                 // Required for: strlen(), strncpy(), strcmp(), strrchr(), memset()
 #include <time.h>                   // Required for: time() [Used in InitTimer()]
 #include <math.h>                   // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()]
 
@@ -1837,8 +1837,8 @@ void TakeScreenshot(const char *fileName)
     unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y));
     Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 };
 
-    char path[512] = { 0 };
-    strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName));
+    char path[MAX_FILEPATH_LENGTH] = { 0 };
+    strncpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName), MAX_FILEPATH_LENGTH - 1);
 
     ExportImage(image, path); // WARNING: Module required: rtextures
     RL_FREE(imgData);
@@ -2022,7 +2022,7 @@ bool IsFileExtension(const char *fileName, const char *ext)
         int extLen = (int)strlen(ext);
         char *extList = (char *)RL_CALLOC(extLen + 1, 1);
         char *extListPtrs[MAX_FILE_EXTENSIONS] = { 0 };
-        strcpy(extList, ext);
+        strncpy(extList, ext, extLen);
         extListPtrs[0] = extList;
 
         for (int i = 0; i < extLen; i++)
@@ -2130,11 +2130,11 @@ const char *GetFileExtension(const char *fileName)
 }
 
 // String pointer reverse break: returns right-most occurrence of charset in s
-static const char *strprbrk(const char *s, const char *charset)
+static const char *strprbrk(const char *text, const char *charset)
 {
     const char *latestMatch = NULL;
 
-    for (; s = strpbrk(s, charset), s != NULL; latestMatch = s++) { }
+    for (; (text != NULL) && (text = strpbrk(text, charset)); latestMatch = text++) { }
 
     return latestMatch;
 }
@@ -2161,7 +2161,7 @@ const char *GetFileNameWithoutExt(const char *filePath)
 
     if (filePath != NULL)
     {
-        strcpy(fileName, GetFileName(filePath)); // Get filename.ext without path
+        strncpy(fileName, GetFileName(filePath), MAX_FILENAME_LENGTH - 1); // Get filename.ext without path
         int size = (int)strlen(fileName); // Get size in bytes
 
         for (int i = size; i > 0; i--) // Reverse search '.'
@@ -2233,7 +2233,7 @@ const char *GetPrevDirectoryPath(const char *dirPath)
     memset(prevDirPath, 0, MAX_FILEPATH_LENGTH);
     int pathLen = (int)strlen(dirPath);
 
-    if (pathLen <= 3) strcpy(prevDirPath, dirPath);
+    if (pathLen <= 3) strncpy(prevDirPath, dirPath, MAX_FILEPATH_LENGTH  - 1);
 
     for (int i = (pathLen - 1); (i >= 0) && (pathLen > 3); i--)
     {
@@ -2472,12 +2472,12 @@ int MakeDirectory(const char *dirPath)
 }
 
 // Change working directory, returns true on success
-bool ChangeDirectory(const char *dir)
+bool ChangeDirectory(const char *dirPath)
 {
-    bool result = CHDIR(dir);
+    bool result = CHDIR(dirPath);
 
-    if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dir);
-    else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dir);
+    if (result != 0) TRACELOG(LOG_WARNING, "SYSTEM: Failed to change to directory: %s", dirPath);
+    else TRACELOG(LOG_INFO, "SYSTEM: Working Directory: %s", dirPath);
 
     return (result == 0);
 }
@@ -2708,6 +2708,9 @@ unsigned char *DecodeDataBase64(const char *text, int *outputSize)
         ['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
         ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
     };
+ 
+    *outputSize = 0;
+    if (text == NULL) return NULL;
 
     // Compute expected size and padding
     int dataSize = (int)strlen(text); // WARNING: Expecting NULL terminated strings!
@@ -3952,7 +3955,7 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
                     {
                         if (IsFileExtension(path, filter))
                         {
-                            strcpy(files->paths[files->count], path);
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                             files->count++;
                         }
                     }
@@ -3960,14 +3963,14 @@ static void ScanDirectoryFiles(const char *basePath, FilePathList *files, const
                     {
                         if (strstr(filter, DIRECTORY_FILTER_TAG) != NULL)
                         {
-                            strcpy(files->paths[files->count], path);
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                             files->count++;
                         }
                     }
                 }
                 else
                 {
-                    strcpy(files->paths[files->count], path);
+                    strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                     files->count++;
                 }
             }
@@ -4011,13 +4014,13 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
                     {
                         if (IsFileExtension(path, filter))
                         {
-                            strcpy(files->paths[files->count], path);
+                            strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                             files->count++;
                         }
                     }
                     else
                     {
-                        strcpy(files->paths[files->count], path);
+                        strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                         files->count++;
                     }
 
@@ -4031,7 +4034,7 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
                 {
                     if ((filter != NULL) && (strstr(filter, DIRECTORY_FILTER_TAG) != NULL))
                     {
-                        strcpy(files->paths[files->count], path);
+                        strncpy(files->paths[files->count], path, MAX_FILEPATH_LENGTH - 1);
                         files->count++;
                     }
 
@@ -4334,23 +4337,26 @@ const char *TextFormat(const char *text, ...)
 
     char *currentBuffer = buffers[index];
     memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
+    
+    if (text != NULL)
+    {
+        va_list args;
+        va_start(args, text);
+        int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+        va_end(args);
 
-    va_list args;
-    va_start(args, text);
-    int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
-    va_end(args);
+        // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+        if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+        {
+            // Inserting "..." at the end of the string to mark as truncated
+            char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+            snprintf(truncBuffer, 4, "...");
+        }
 
-    // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
-    if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
-    {
-        // Inserting "..." at the end of the string to mark as truncated
-        char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
-        snprintf(truncBuffer, 4, "...");
+        index += 1; // Move to next buffer for next function call
+        if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
     }
 
-    index += 1;     // Move to next buffer for next function call
-    if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
-
     return currentBuffer;
 }
 

+ 4 - 4
src/rlgl.h

@@ -2492,12 +2492,12 @@ void rlLoadExtensions(void *loader)
     const char *extensions = (const char *)glGetString(GL_EXTENSIONS);  // One big const string
 
     // NOTE: We have to duplicate string because glGetString() returns a const string
-    int size = strlen(extensions) + 1;      // Get extensions string size in bytes
-    char *extensionsDup = (char *)RL_CALLOC(size, sizeof(char));
-    strcpy(extensionsDup, extensions);
+    int extSize = (int)strlen(extensions); // Get extensions string size in bytes
+    char *extensionsDup = (char *)RL_CALLOC(extSize + 1, sizeof(char)); // Allocate space for copy with additional EOL byte
+    strncpy(extensionsDup, extensions, extSize);
     extList[numExt] = extensionsDup;
 
-    for (int i = 0; i < size; i++)
+    for (int i = 0; i < extSize; i++)
     {
         if (extensionsDup[i] == ' ')
         {

+ 7 - 9
src/rmodels.c

@@ -2060,7 +2060,7 @@ bool ExportMeshAsCode(Mesh mesh, const char *fileName)
 
     // Get file name from path and convert variable name to uppercase
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
     for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
 
     // Add image information
@@ -4306,8 +4306,8 @@ static Model LoadOBJ(const char *fileName)
         return model;
     }
 
-    char currentDir[1024] = { 0 };
-    strcpy(currentDir, GetWorkingDirectory()); // Save current working directory
+    char currentDir[MAX_FILEPATH_LENGTH] = { 0 };
+    strncpy(currentDir, GetWorkingDirectory(), MAX_FILEPATH_LENGTH - 1); // Save current working directory
     const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness
     if (CHDIR(workingDir) != 0) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir);
 
@@ -5025,10 +5025,8 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
         for (unsigned int j = 0; j < iqmHeader->num_poses; j++)
         {
             // If animations and skeleton are in the same file, copy bone names to anim
-            if (iqmHeader->num_joints > 0)
-                memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
-            else
-                strcpy(animations[a].bones[j].name, "ANIMJOINTNAME"); // Default bone name otherwise
+            if (iqmHeader->num_joints > 0) memcpy(animations[a].bones[j].name, fileDataPtr + iqmHeader->ofs_text + joints[j].name, BONE_NAME_LENGTH*sizeof(char));
+            else memcpy(animations[a].bones[j].name, "ANIMJOINTNAME", 13); // Default bone name otherwise
             animations[a].bones[j].parent = poses[j].parent;
         }
 
@@ -6970,7 +6968,7 @@ static Model LoadM3D(const char *fileName)
 
             // Add a special "no bone" bone
             model.bones[i].parent = -1;
-            strcpy(model.bones[i].name, "NO BONE");
+            memcpy(model.bones[i].name, "NO BONE", 7);
             model.bindPose[i].translation.x = 0.0f;
             model.bindPose[i].translation.y = 0.0f;
             model.bindPose[i].translation.z = 0.0f;
@@ -7062,7 +7060,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou
 
             // A special, never transformed "no bone" bone, used for boneless vertices
             animations[a].bones[i].parent = -1;
-            strcpy(animations[a].bones[i].name, "NO BONE");
+            memcpy(animations[a].bones[i].name, "NO BONE", 7);
 
             // M3D stores frames at arbitrary intervals with sparse skeletons. We need full skeletons at
             // regular intervals, so let the M3D SDK do the heavy lifting and calculate interpolated bones

+ 204 - 173
src/rtext.c

@@ -67,7 +67,7 @@
 
 #include <stdlib.h>         // Required for: malloc(), free()
 #include <stdio.h>          // Required for: vsprintf()
-#include <string.h>         // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
+#include <string.h>         // Required for: strcmp(), strstr(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
 #include <stdarg.h>         // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
 #include <ctype.h>          // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
 
@@ -164,9 +164,8 @@ extern void LoadFontDefault(void)
 {
     #define BIT_CHECK(a,b) ((a) & (1u << (b)))
 
-    // check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return
-    if (defaultFont.glyphs != NULL && !isGpuReady)
-        return;
+    // Check to see if we have allready allocated the font for an image, and if we don't need to upload, then just return
+    if ((defaultFont.glyphs != NULL) && !isGpuReady) return;
 
     // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
     // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
@@ -1453,29 +1452,31 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint)
 char **LoadTextLines(const char *text, int *count)
 {
     char **lines = NULL;
+    int lineCount = 0;
 
-    if (text == NULL) { *count = 0; return lines; }
-
-    int lineCount = 1;
-    int textSize = (int)strlen(text);
-
-    // First text scan pass to get required line count
-    for (int i = 0; i < textSize; i++)
+    if (text != NULL)
     {
-        if (text[i] == '\n') lineCount++;
-    }
+        int textSize = TextLength(text);
+        lineCount = 1;
 
-    lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
-    for (int i = 0, l = 0, lineLen = 0; i <= textSize; i++)
-    {
-        if ((text[i] == '\n') || (text[i] == '\0'))
+        // First text scan pass to get required line count
+        for (int i = 0; i < textSize; i++)
+        {
+            if (text[i] == '\n') lineCount++;
+        }
+
+        lines = (char **)RL_CALLOC(lineCount, sizeof(char *));
+        for (int i = 0, l = 0, lineLen = 0; i <= textSize; i++)
         {
-            lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
-            strncpy(lines[l], &text[i - lineLen], lineLen);
-            lineLen = 0;
-            l++;
+            if ((text[i] == '\n') || (text[i] == '\0'))
+            {
+                lines[l] = (char *)RL_CALLOC(lineLen + 1, 1);
+                strncpy(lines[l], &text[i - lineLen], lineLen);
+                lineLen = 0;
+                l++;
+            }
+            else lineLen++;
         }
-        else lineLen++;
     }
 
     *count = lineCount;
@@ -1517,24 +1518,27 @@ const char *TextFormat(const char *text, ...)
     static int index = 0;
 
     char *currentBuffer = buffers[index];
-    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
+    memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using
+    
+    if (text != NULL)
+    {
+        va_list args;
+        va_start(args, text);
+        int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
+        va_end(args);
 
-    va_list args;
-    va_start(args, text);
-    int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
-    va_end(args);
+        // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
+        if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
+        {
+            // Inserting "..." at the end of the string to mark as truncated
+            char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
+            snprintf(truncBuffer, 4, "...");
+        }
 
-    // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occurred
-    if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
-    {
-        // Inserting "..." at the end of the string to mark as truncated
-        char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
-        snprintf(truncBuffer, 4, "...");
+        index += 1;     // Move to next buffer for next function call
+        if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
     }
 
-    index += 1;     // Move to next buffer for next function call
-    if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
-
     return currentBuffer;
 }
 
@@ -1545,13 +1549,16 @@ int TextToInteger(const char *text)
     int value = 0;
     int sign = 1;
 
-    if ((text[0] == '+') || (text[0] == '-'))
+    if (text != NULL)
     {
-        if (text[0] == '-') sign = -1;
-        text++;
-    }
+        if ((text[0] == '+') || (text[0] == '-'))
+        {
+            if (text[0] == '-') sign = -1;
+            text++;
+        }
 
-    for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+        for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
+    }
 
     return value*sign;
 }
@@ -1564,22 +1571,25 @@ float TextToFloat(const char *text)
     float value = 0.0f;
     float sign = 1.0f;
 
-    if ((text[0] == '+') || (text[0] == '-'))
+    if (text != NULL)
     {
-        if (text[0] == '-') sign = -1.0f;
-        text++;
-    }
+        if ((text[0] == '+') || (text[0] == '-'))
+        {
+            if (text[0] == '-') sign = -1.0f;
+            text++;
+        }
 
-    int i = 0;
-    for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
+        int i = 0;
+        for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0');
 
-    if (text[i++] == '.')
-    {
-        float divisor = 10.0f;
-        for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+        if (text[i++] == '.')
         {
-            value += ((float)(text[i] - '0'))/divisor;
-            divisor = divisor*10.0f;
+            float divisor = 10.0f;
+            for (; ((text[i] >= '0') && (text[i] <= '9')); i++)
+            {
+                value += ((float)(text[i] - '0'))/divisor;
+                divisor = divisor*10.0f;
+            }
         }
     }
 
@@ -1631,25 +1641,22 @@ const char *TextSubtext(const char *text, int position, int length)
     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
 
-    int textLength = TextLength(text);
-
-    if (position >= textLength)
+    if (text != NULL)
     {
-        return buffer; //First char is already '\0' by memset
-    }
+        int textLength = TextLength(text);
 
-    int maxLength = textLength - position;
-    if (length > maxLength) length = maxLength;
-    if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
+        if (position >= textLength) return buffer; // First char is already '\0' by memset
 
-    // NOTE: Alternative: memcpy(buffer, text + position, length)
+        int maxLength = textLength - position;
+        if (length > maxLength) length = maxLength;
+        if (length >= MAX_TEXT_BUFFER_LENGTH) length = MAX_TEXT_BUFFER_LENGTH - 1;
 
-    for (int c = 0 ; c < length ; c++)
-    {
-        buffer[c] = text[position + c];
-    }
+        // NOTE: Alternative: memcpy(buffer, text + position, length)
+
+        for (int c = 0; c < length; c++) buffer[c] = text[position + c];
 
-    buffer[length] = '\0';
+        buffer[length] = '\0';
+    }
 
     return buffer;
 }
@@ -1684,7 +1691,7 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
 
     if (beginIndex > -1)
     {
-        int beginLen = (int)strlen(begin);
+        int beginLen = TextLength(begin);
         int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
 
         if (endIndex > -1)
@@ -1700,84 +1707,86 @@ char *GetTextBetween(const char *text, const char *begin, const char *end)
 }
 
 // Replace text string
-// REQUIRES: strstr(), strncpy(), strcpy()
+// REQUIRES: strstr(), strncpy()
 // TODO: If (replacement == "") remove "search" text
 // WARNING: Allocated memory must be manually freed
 char *TextReplace(const char *text, const char *search, const char *replacement)
 {
     char *result = NULL;
 
-    if (!text || !search) return NULL; // Sanity check
+    if ((text != NULL) && (search != NULL))
+    {
+        char *insertPoint = NULL;   // Next insert point
+        char *temp = NULL;          // Temp pointer
+        int searchLen = 0;          // Search string length of (the string to remove)
+        int replaceLen = 0;         // Replacement length (the string to replace by)
+        int lastReplacePos = 0;     // Distance between next search and end of last replace
+        int count = 0;              // Number of replacements
 
-    char *insertPoint = NULL;   // Next insert point
-    char *temp = NULL;          // Temp pointer
-    int searchLen = 0;          // Search string length of (the string to remove)
-    int replaceLen = 0;         // Replacement length (the string to replace by)
-    int lastReplacePos = 0;     // Distance between next search and end of last replace
-    int count = 0;              // Number of replacements
+        searchLen = TextLength(search);
+        if (searchLen == 0) return NULL;  // Empty search causes infinite loop during count
 
-    searchLen = TextLength(search);
-    if (searchLen == 0) return NULL;  // Empty search causes infinite loop during count
+        replaceLen = TextLength(replacement);
 
-    replaceLen = TextLength(replacement);
+        // Count the number of replacements needed
+        insertPoint = (char *)text;
+        for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
 
-    // Count the number of replacements needed
-    insertPoint = (char *)text;
-    for (count = 0; (temp = strstr(insertPoint, search)); count++) insertPoint = temp + searchLen;
+        // Allocate returning string and point temp to it
+        temp = result = (char *)RL_MALLOC(TextLength(text) + (replaceLen - searchLen)*count + 1);
 
-    // Allocate returning string and point temp to it
-    temp = result = (char *)RL_MALLOC(TextLength(text) + (replaceLen - searchLen)*count + 1);
+        if (!result) return NULL;   // Memory could not be allocated
 
-    if (!result) return NULL;   // Memory could not be allocated
+        // First time through the loop, all the variable are set correctly from here on,
+        //  - 'temp' points to the end of the result string
+        //  - 'insertPoint' points to the next occurrence of replace in text
+        //  - 'text' points to the remainder of text after "end of replace"
+        while (count--)
+        {
+            insertPoint = (char *)strstr(text, search);
+            lastReplacePos = (int)(insertPoint - text);
+            temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
+            temp = strcpy(temp, replacement) + replaceLen;
+            text += lastReplacePos + searchLen; // Move to next "end of replace"
+        }
 
-    // First time through the loop, all the variable are set correctly from here on,
-    //  - 'temp' points to the end of the result string
-    //  - 'insertPoint' points to the next occurrence of replace in text
-    //  - 'text' points to the remainder of text after "end of replace"
-    while (count--)
-    {
-        insertPoint = (char *)strstr(text, search);
-        lastReplacePos = (int)(insertPoint - text);
-        temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
-        temp = strcpy(temp, replacement) + replaceLen;
-        text += lastReplacePos + searchLen; // Move to next "end of replace"
+        // Copy remaind text part after replacement to result (pointed by moving temp)
+        strcpy(temp, text);
     }
 
-    // Copy remaind text part after replacement to result (pointed by moving temp)
-    strcpy(temp, text);
-
     return result;
 }
 
 // Replace text between two specific strings
-// REQUIRES: strlen(), strncpy()
+// REQUIRES: strncpy()
 // NOTE: If (replacement == NULL) remove "begin"[ ]"end" text
 // WARNING: Returned string must be freed by user
 char *TextReplaceBetween(const char *text, const char *begin, const char *end, const char *replacement)
 {
     char *result = NULL;
 
-    if (!text || !begin || !end) return NULL; // Sanity check
-
-    int beginIndex = TextFindIndex(text, begin);
-
-    if (beginIndex > -1)
+    if ((text != NULL) && (begin != NULL) && (end != NULL))
     {
-        int beginLen = (int)strlen(begin);
-        int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
+        int beginIndex = TextFindIndex(text, begin);
 
-        if (endIndex > -1)
+        if (beginIndex > -1)
         {
-            endIndex += (beginIndex + beginLen);
+            int beginLen = TextLength(begin);
+            int endIndex = TextFindIndex(text + beginIndex + beginLen, end);
+
+            if (endIndex > -1)
+            {
+                endIndex += (beginIndex + beginLen);
 
-            int textLen = (int)strlen(text);
-            int replaceLen = (replacement == NULL)? 0 : (int)strlen(replacement);
-            int toreplaceLen = endIndex - beginIndex - beginLen;
-            result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
+                int textLen = TextLength(text);
+                int replaceLen = (replacement == NULL)? 0 : TextLength(replacement);
+                int toreplaceLen = endIndex - beginIndex - beginLen;
+                result = (char *)RL_CALLOC(textLen + replaceLen - toreplaceLen + 1, sizeof(char));
 
-            strncpy(result, text, beginIndex + beginLen); // Copy first text part
-            if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
-            strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
+                strncpy(result, text, beginIndex + beginLen); // Copy first text part
+                if (replacement != NULL) strncpy(result + beginIndex + beginLen, replacement, replaceLen); // Copy replacement (if provided)
+                strncpy(result + beginIndex + beginLen + replaceLen, text + endIndex, textLen - endIndex); // Copy end text part
+            }
         }
     }
 
@@ -1788,16 +1797,21 @@ char *TextReplaceBetween(const char *text, const char *begin, const char *end, c
 // WARNING: Allocated memory must be manually freed
 char *TextInsert(const char *text, const char *insert, int position)
 {
-    int textLen = TextLength(text);
-    int insertLen = TextLength(insert);
+    char *result = NULL;
 
-    char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
+    if ((text != NULL) && (insert != NULL))
+    {
+        int textLen = TextLength(text);
+        int insertLen = TextLength(insert);
+
+        result = (char *)RL_MALLOC(textLen + insertLen + 1);
 
-    for (int i = 0; i < position; i++) result[i] = text[i];
-    for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
-    for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
+        for (int i = 0; i < position; i++) result[i] = text[i];
+        for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
+        for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
 
-    result[textLen + insertLen] = '\0';     // Make sure text string is valid!
+        result[textLen + insertLen] = '\0'; // Add EOL
+    }
 
     return result;
 }
@@ -1879,11 +1893,13 @@ char **TextSplit(const char *text, char delimiter, int *count)
 
 // Append text at specific position and move cursor
 // WARNING: It's up to the user to make sure appended text does not overflow the buffer!
-// REQUIRES: strcpy()
 void TextAppend(char *text, const char *append, int *position)
 {
-    strcpy(text + *position, append);
-    *position += TextLength(append);
+    if ((text != NULL) && (append != NULL))
+    {
+        TextCopy(text + *position, append);
+        *position += TextLength(append);
+    }
 }
 
 // Find first text occurrence within a string
@@ -1891,11 +1907,13 @@ void TextAppend(char *text, const char *append, int *position)
 int TextFindIndex(const char *text, const char *search)
 {
     int position = -1;
-    if (text == NULL) return position;
 
-    char *ptr = (char *)strstr(text, search);
+    if (text != NULL)
+    {
+        char *ptr = (char *)strstr(text, search);
 
-    if (ptr != NULL) position = (int)(ptr - text);
+        if (ptr != NULL) position = (int)(ptr - text);
+    }
 
     return position;
 }
@@ -2029,24 +2047,29 @@ char *TextToCamel(const char *text)
 // WARNING: Allocated memory must be manually freed
 char *LoadUTF8(const int *codepoints, int length)
 {
-    // We allocate enough memory to fit all possible codepoints
-    // NOTE: 5 bytes for every codepoint should be enough
-    char *text = (char *)RL_CALLOC(length*5, 1);
-    const char *utf8 = NULL;
-    int size = 0;
-
-    for (int i = 0, bytes = 0; i < length; i++)
+    char *text = NULL;
+    
+    if ((codepoints != NULL) && (length > 0))
     {
-        utf8 = CodepointToUTF8(codepoints[i], &bytes);
-        memcpy(text + size, utf8, bytes);
-        size += bytes;
-    }
+        // We allocate enough memory to fit all possible codepoints
+        // NOTE: 5 bytes for every codepoint should be enough
+        text = (char *)RL_CALLOC(length*5, 1);
+        const char *utf8 = NULL;
+        int size = 0;
 
-    // Create second buffer and copy data manually to it
-    char *temp = (char *)RL_CALLOC(size + 1, 1);
-    memcpy(temp, text, size);
-    RL_FREE(text);
-    text = temp;
+        for (int i = 0, bytes = 0; i < length; i++)
+        {
+            utf8 = CodepointToUTF8(codepoints[i], &bytes);
+            memcpy(text + size, utf8, bytes);
+            size += bytes;
+        }
+
+        // Create second buffer and copy data manually to it
+        char *temp = (char *)RL_CALLOC(size + 1, 1);
+        memcpy(temp, text, size);
+        RL_FREE(text);
+        text = temp;
+    }
 
     return text;
 }
@@ -2060,28 +2083,31 @@ void UnloadUTF8(char *text)
 // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
 int *LoadCodepoints(const char *text, int *count)
 {
-    int textLength = TextLength(text);
-
-    int codepointSize = 0;
+    int *codepoints = NULL;
     int codepointCount = 0;
+    
+    if (text != NULL)
+    {
+        int textLength = TextLength(text);
 
-    // Allocate a big enough buffer to store as many codepoints as text bytes
-    int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
+        // Allocate a big enough buffer to store as many codepoints as text bytes
+        int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int));
 
-    for (int i = 0; i < textLength; codepointCount++)
-    {
-        codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
-        i += codepointSize;
-    }
+        int codepointSize = 0;
+        for (int i = 0; i < textLength; codepointCount++)
+        {
+            codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize);
+            i += codepointSize;
+        }
 
-    // Create second buffer and copy data manually to it
-    int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
-    for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
-    RL_FREE(codepoints);
-    codepoints = temp;
+        // Create second buffer and copy data manually to it
+        int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int));
+        for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i];
+        RL_FREE(codepoints);
+        codepoints = temp;
+    }
 
     *count = codepointCount;
-
     return codepoints;
 }
 
@@ -2098,14 +2124,15 @@ int GetCodepointCount(const char *text)
     unsigned int length = 0;
     const char *ptr = text;
 
-    while (*ptr != '\0')
+    if (ptr != NULL)
     {
-        int next = 0;
-        GetCodepointNext(ptr, &next);
-
-        ptr += next;
-
-        length++;
+        while (*ptr != '\0')
+        {
+            int next = 0;
+            GetCodepointNext(ptr, &next);
+            ptr += next;
+            length++;
+        }
     }
 
     return length;
@@ -2170,11 +2197,14 @@ int GetCodepoint(const char *text, int *codepointSize)
     0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
     0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
 */
-    // NOTE: on decode errors we return as soon as possible
 
+    
     int codepoint = 0x3f;   // Codepoint (defaults to '?')
-    int octet = (unsigned char)(text[0]); // The first UTF8 octet
     *codepointSize = 1;
+    if (text == NULL) return codepoint;
+
+    // NOTE: on decode errors we return as soon as possible
+    int octet = (unsigned char)(text[0]); // The first UTF8 octet
 
     if (octet <= 0x7f)
     {
@@ -2266,6 +2296,7 @@ int GetCodepointNext(const char *text, int *codepointSize)
     const char *ptr = text;
     int codepoint = 0x3f;       // Codepoint (defaults to '?')
     *codepointSize = 1;
+    if (text == NULL) return codepoint;
 
     // Get current codepoint and bytes processed
     if (0xf0 == (0xf8 & ptr[0]))
@@ -2304,15 +2335,15 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
 {
     const char *ptr = text;
     int codepoint = 0x3f;       // Codepoint (defaults to '?')
-    int cpSize = 0;
-    *codepointSize = 0;
+    *codepointSize = 1;
+    if (text == NULL) return codepoint;
 
     // Move to previous codepoint
     do ptr--;
     while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) ==  0x80));
 
+    int cpSize = 0;
     codepoint = GetCodepointNext(ptr, &cpSize);
-
     if (codepoint != 0) *codepointSize = cpSize;
 
     return codepoint;

+ 11 - 8
src/rtextures.c

@@ -771,7 +771,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
 
     // Get file name from path and convert variable name to uppercase
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1); // NOTE: Using function provided by [rcore] module
     for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
 
     // Add image information
@@ -1125,17 +1125,19 @@ Image GenImageCellular(int width, int height, int tileSize)
 Image GenImageText(int width, int height, const char *text)
 {
     Image image = { 0 };
-
-    int textLength = (int)strlen(text);
-    int imageViewSize = width*height;
-
+    
+    int imageSize = width*height;
     image.width = width;
     image.height = height;
     image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
-    image.data = RL_CALLOC(imageViewSize, 1);
+    image.data = RL_CALLOC(imageSize, 1);
     image.mipmaps = 1;
 
-    memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
+    if (text != NULL)
+    {
+        int textLength = (int)strlen(text);
+        memcpy(image.data, text, (textLength > imageSize)? imageSize : textLength);
+    }
 
     return image;
 }
@@ -1484,8 +1486,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
 {
     Image imText = { 0 };
 #if defined(SUPPORT_MODULE_RTEXT)
+    if (text == NULL) return imText;
+    
     int size = (int)strlen(text);   // Get size in bytes of text
-
     int textOffsetX = 0;            // Image drawing position X
     int textOffsetY = 0;            // Offset between lines (on linebreak '\n')
 

+ 2 - 2
src/utils.c

@@ -105,7 +105,7 @@ void TraceLog(int logType, const char *text, ...)
 {
 #if defined(SUPPORT_TRACELOG)
     // Message has level below current threshold, don't emit
-    if (logType < logTypeLevel) return;
+    if ((logType < logTypeLevel) || (text == NULL)) return;
 
     va_list args;
     va_start(args, text);
@@ -313,7 +313,7 @@ bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileN
 
     // Get file name from path
     char varFileName[256] = { 0 };
-    strcpy(varFileName, GetFileNameWithoutExt(fileName));
+    strncpy(varFileName, GetFileNameWithoutExt(fileName), 256 - 1);
     for (int i = 0; varFileName[i] != '\0'; i++)
     {
         // Convert variable name to uppercase