[sanitizer] scanf interceptor: fix write size for %mc/%mC/%mS
authorFangrui Song <i@maskray.me>
Mon, 28 Aug 2023 05:19:31 +0000 (22:19 -0700)
committerTobias Hieta <tobias@hieta.se>
Tue, 5 Sep 2023 06:59:58 +0000 (08:59 +0200)
When the optional assignment-allocation character 'm' (Extension to the
ISO C standard) is present, we currently use internal_strlen(buf)+1 for
all of cCsS[ (D85350). Fix cCS to use the correct size.

Fix https://github.com/llvm/llvm-project/issues/61768

Reviewed By: #sanitizers, vitalybuka

Differential Revision: https://reviews.llvm.org/D158485

(cherry picked from commit beeb37a8f3275281be305d2d1afe35ca053e21c0)

compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors_format.inc
compiler-rt/lib/sanitizer_common/tests/sanitizer_format_interceptor_test.cpp

index 220abb8..2448590 100644 (file)
@@ -340,11 +340,19 @@ static void scanf_common(void *ctx, int n_inputs, bool allowGnuMalloc,
       size = 0;
     }
     COMMON_INTERCEPTOR_WRITE_RANGE(ctx, argp, size);
-    // For %ms/%mc, write the allocated output buffer as well.
+    // For %mc/%mC/%ms/%m[/%mS, write the allocated output buffer as well.
     if (dir.allocate) {
-      char *buf = *(char **)argp;
-      if (buf)
-        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, internal_strlen(buf) + 1);
+      if (char *buf = *(char **)argp) {
+        if (dir.convSpecifier == 'c')
+          size = 1;
+        else if (dir.convSpecifier == 'C')
+          size = sizeof(wchar_t);
+        else if (dir.convSpecifier == 'S')
+          size = (internal_wcslen((wchar_t *)buf) + 1) * sizeof(wchar_t);
+        else  // 's' or '['
+          size = internal_strlen(buf) + 1;
+        COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, size);
+      }
     }
   }
 }
index fa52ccc..de96e57 100644 (file)
@@ -9,14 +9,16 @@
 // Tests for *scanf interceptors implementation in sanitizer_common.
 //
 //===----------------------------------------------------------------------===//
+#include <wchar.h>
+
 #include <algorithm>
 #include <vector>
 
+#include "gtest/gtest.h"
 #include "interception/interception.h"
-#include "sanitizer_test_utils.h"
-#include "sanitizer_common/sanitizer_libc.h"
 #include "sanitizer_common/sanitizer_common.h"
-#include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_test_utils.h"
 
 using namespace __sanitizer;
 
@@ -206,21 +208,35 @@ TEST(SanitizerCommonInterceptors, Scanf) {
 
 TEST(SanitizerCommonInterceptors, ScanfAllocate) {
   const char *buf = "123456";
+  const wchar_t *wbuf = L"123";
 
   // Can not use testScanf() because this case needs a valid pointer to a string
   // in the scanf argument.
   {
     std::vector<unsigned> scanf_sizes;
+    testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%mc", &buf);
+    verifyFormatResults("%mc", 2, scanf_sizes, {P, 1u});
+  }
+  {
+    std::vector<unsigned> scanf_sizes;
+    testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%mC", &wbuf);
+    verifyFormatResults("%mC", 2, scanf_sizes, {P, (unsigned)sizeof(wchar_t)});
+  }
+  {
+    std::vector<unsigned> scanf_sizes;
     testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%ms", &buf);
-    verifyFormatResults("%ms", 2, scanf_sizes,
-                        {P, (unsigned)(strlen(buf) + 1)});
+    verifyFormatResults("%ms", 2, scanf_sizes, {P, unsigned(strlen(buf) + 1)});
+    scanf_sizes.clear();
+    testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%m[0-9]",
+               &buf);
+    verifyFormatResults("%m[0-9]", 2, scanf_sizes,
+                        {P, unsigned(strlen(buf) + 1)});
   }
-
   {
     std::vector<unsigned> scanf_sizes;
-    testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%mc", &buf);
-    verifyFormatResults("%mc", 2, scanf_sizes,
-                        {P, (unsigned)(strlen(buf) + 1)});
+    testScanf3((void *)&scanf_sizes, 2, /*allowGnuMalloc=*/false, "%mS", &wbuf);
+    verifyFormatResults("%mS", 2, scanf_sizes,
+                        {P, unsigned((wcslen(wbuf) + 1) * sizeof(wchar_t))});
   }
 }