SDL: Mitigate BatBadBut vulnerability

From e9bfa5bf6a350d348db17571e029b4680140aba1 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Mon, 16 Sep 2024 16:51:38 +0200
Subject: [PATCH] Mitigate BatBadBut vulnerability

---
 src/process/windows/SDL_windowsprocess.c | 46 +++++++++++++++++++++++-
 1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/src/process/windows/SDL_windowsprocess.c b/src/process/windows/SDL_windowsprocess.c
index 6c816d0fdc9aa..989711a3152c1 100644
--- a/src/process/windows/SDL_windowsprocess.c
+++ b/src/process/windows/SDL_windowsprocess.c
@@ -76,12 +76,24 @@ static bool SetupRedirect(SDL_PropertiesID props, const char *property, HANDLE *
     return true;
 }
 
+static bool is_batch_file_path(const char *path) {
+    size_t len_path = SDL_strlen(path);
+    if (len_path < 4) {
+        return false;
+    }
+    if (SDL_strcasecmp(path + len_path - 4, ".bat") == 0 || SDL_strcasecmp(path + len_path - 4, ".cmd") == 0) {
+        return true;
+    }
+    return false;
+}
+
 static bool join_arguments(const char * const *args, LPWSTR *args_out)
 {
     size_t len;
     int i;
     int i_out;
     char *result;
+    bool batch_file = is_batch_file_path(args[0]);
 
     len = 0;
     for (i = 0; args[i]; i++) {
@@ -99,6 +111,18 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
                 /* only escape backslashes that precede a double quote */
                 len += (a[1] == '"' || a[1] == '\0') ? 2 : 1;
                 break;
+            case ' ':
+            case '^':
+            case '&':
+            case '|':
+            case '<':
+            case '>':
+                if (batch_file) {
+                    len += 2;
+                } else {
+                    len += 1;
+                }
+                break;
             default:
                 len += 1;
                 break;
@@ -122,7 +146,11 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
         for (; *a; a++) {
             switch (*a) {
             case '"':
-                result[i_out++] = '\\';
+                if (batch_file) {
+                    result[i_out++] = '"';
+                } else {
+                    result[i_out++] = '\\';
+                }
                 result[i_out++] = *a;
                 break;
             case '\\':
@@ -131,6 +159,22 @@ static bool join_arguments(const char * const *args, LPWSTR *args_out)
                     result[i_out++] = *a;
                 }
                 break;
+            case ' ':
+                if (batch_file) {
+                    result[i_out++] = '^';
+                }
+                result[i_out++] = *a;
+                break;
+            case '^':
+            case '&':
+            case '|':
+            case '<':
+            case '>':
+                if (batch_file) {
+                    result[i_out++] = '^';
+                }
+                result[i_out++] = *a;
+                break;
             default:
                 result[i_out++] = *a;
                 break;