Origin: https://github.com/drupal/drupal/commit/87942385ae8efd1fece393f69e16e217e33f849f?diff=unified
Forwarded: not-needed
From: Alex Pott, Lee Rowlands, Greg Knaddison, Neil Drumm, Michael
 Hess, David Rothstein, Peter Wolanin
Date: Wed, 20 Mar 2019 12:44:48 -0600
Subject: Fixes for SA-CORE-2019-004
 Backported the diff between 7.64 and 7.65, applying it to the version
 in the Stable Debian release (7.52).
 .
 Under certain circumstances the File module/subsystem allows a
 malicious user to upload a file that can trigger a cross-site
 scripting (XSS) vulnerability.
 .
 The Drupal advisory is available in:
 .
 https://www.drupal.org/SA-CORE-2019-004
 .
 This corresponds to CVE-2019-6341.

Index: drupal7/includes/file.inc
===================================================================
--- drupal7.orig/includes/file.inc
+++ drupal7/includes/file.inc
@@ -983,8 +983,15 @@ function file_build_uri($path) {
  * @return
  *   The destination filepath, or FALSE if the file already exists
  *   and FILE_EXISTS_ERROR is specified.
+ *
+ * @throws RuntimeException
+ *   Thrown if the filename contains invalid UTF-8.
  */
 function file_destination($destination, $replace) {
+  $basename = drupal_basename($destination);
+  if (!drupal_validate_utf8($basename)) {
+    throw new RuntimeException(sprintf("Invalid filename '%s'", $basename));
+  }
   if (file_exists($destination)) {
     switch ($replace) {
       case FILE_EXISTS_REPLACE:
@@ -992,7 +999,6 @@ function file_destination($destination,
         break;
 
       case FILE_EXISTS_RENAME:
-        $basename = drupal_basename($destination);
         $directory = drupal_dirname($destination);
         $destination = file_create_filename($basename, $directory);
         break;
@@ -1208,8 +1214,13 @@ function file_unmunge_filename($filename
  * @return
  *   File path consisting of $directory and a unique filename based off
  *   of $basename.
+ *
+ * @throws RuntimeException
+ *   Thrown if the $basename is not valid UTF-8 or another error occurs
+ *   stripping control characters.
  */
 function file_create_filename($basename, $directory) {
+  $original = $basename;
   // Strip control characters (ASCII value < 32). Though these are allowed in
   // some filesystems, not many applications handle them well.
   $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
@@ -1553,7 +1564,13 @@ function file_save_upload($form_field_na
   if (substr($destination, -1) != '/') {
     $destination .= '/';
   }
-  $file->destination = file_destination($destination . $file->filename, $replace);
+  try {
+    $file->destination = file_destination($destination . $file->filename, $replace);
+  }
+   catch (RuntimeException $e) {
+    drupal_set_message(t('The file %source could not be uploaded because the name is invalid.', array('%source' => $form_field_name)), 'error');
+    return FALSE;
+  }
   // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
   // there's an existing file so we need to bail.
   if ($file->destination === FALSE) {
Index: drupal7/modules/simpletest/tests/file.test
===================================================================
--- drupal7.orig/modules/simpletest/tests/file.test
+++ drupal7/modules/simpletest/tests/file.test
@@ -957,6 +957,15 @@ class FileDirectoryTest extends FileTest
     $path = file_create_filename($basename, $directory);
     $this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File');
 
+    try {
+      $filename = "a\xFFtest\x80€.txt";
+      file_create_filename($filename, $directory);
+      $this->fail('Expected exception not thrown');
+    }
+    catch (RuntimeException $e) {
+      $this->assertEqual("Invalid filename '$filename'", $e->getMessage());
+    }
+
     // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
   }
 
@@ -989,6 +998,14 @@ class FileDirectoryTest extends FileTest
     $this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File');
     $path = file_destination($destination, FILE_EXISTS_ERROR);
     $this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File');
+
+    try {
+      file_destination("core/misc/a\xFFtest\x80€.txt", FILE_EXISTS_REPLACE);
+      $this->fail('Expected exception not thrown');
+    }
+    catch (RuntimeException $e) {
+      $this->assertEqual("Invalid filename 'a\xFFtest\x80€.txt'", $e->getMessage());
+    }
   }
 
   /**
