On Sun, 16 Jan 2022, Pali Rohár wrote:

Currently v*scanf functions are broken and crash when are called with more
than 30 arguments in va_list. This is because va_list v*scanf functions are
redirected to variadic *scanf functions and this redirect implemented in
scanf.S file has fixed limit for 30 arguments.

Number of arguments for msvcrt *scanf function can be determined from
format string by counting number of '%' characters which are not followed
by another '%' or '*'. Every scanf parameter is pointer and therefore has
fixed size which means that required stack size can be exactly calculated.

Fix this scanf.S redirect implementation by dynamically allocating stack
for exact number of pointer parameters.

---

I have tested this patch for i686 and x86_64. Both ARM (arm32 and aarch64)
changes are untested, so please test it if vsscanf() on these platforms
still works.

With this patch following code works fine without any crashing.
Compile for msvcrt with -std=c89 or -D__USE_MINGW_ANSI_STDIO=0

 #include <stdio.h>
 #include <stdarg.h>

 int call_vsscanf(const char *str, const char *format, ...)
 {
   int ret;
   va_list ap;
   va_start(ap, format);
   ret = vsscanf(str, format, ap);
   va_end(ap);
   return ret;
 }

 int main()
 {
   char b[53];
   call_vsscanf(
     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
     "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
     "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
     &b[0],&b[1],&b[2],&b[3],&b[4],&b[5],&b[6],&b[7],&b[8],&b[9],&b[10],&b[11],
     &b[12],&b[13],&b[14],&b[15],&b[16],&b[17],&b[18],&b[19],&b[20],&b[21],
     &b[22],&b[23],&b[24],&b[25],&b[26],&b[27],&b[28],&b[29],&b[30],&b[31],
     &b[32],&b[33],&b[34],&b[35],&b[36],&b[37],&b[38],&b[39],&b[40],&b[41],
     &b[42],&b[43],&b[44],&b[45],&b[46],&b[47],&b[48],&b[49],&b[50],&b[51]
   );
   printf("b=%s\n", b);
   return 0;
 }

For the testcase, it might be good to test with an odd number of arguments.

I didn't read the x86 assembly changes in very close detail, but assuming it mirrors the arm assembly change attempts, it probably looks reasonable.

I see that the code now tries to avoid loading the first couple arguments (that are moved to register arguments) if the number of parameters is lower than that. I think that it might be overkill (the arg pointer probably can't be null anyway, and there should be enough space on the stack to load a couple extra arguments), but I don't mind keeping it if you feel strongly about it.

But it seems like the it doesn't try to align the stack as it's supposed to (it's losing a comment that looks like this):

-    /* The max number of pointers we support.  Must be an even number
-       to keep the 64bit stack 16byte aligned.  Must not be less than 4.  */

To make the code work for arm, I had to make some adjustments - see the attached patch. (Some adjustments, like doing 'subs' before 'str', is to reduce the latency between the 'str' and 'ldr', and likewise between 'subs' and the conditional branch - I guess I could skip that change too, to keep this diff simpler.)

// Martin
From 172df7002c76d7d490b6bf6d6ea6cc985369fecc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Martin=20Storsj=C3=B6?= <mar...@martin.st>
Date: Mon, 24 Jan 2022 13:21:39 +0200
Subject: [PATCH] fixup: Fix stdio/scanf.S for arm/aarch64

---
 mingw-w64-crt/stdio/scanf.S | 64 +++++++++++++++++++++----------------
 1 file changed, 37 insertions(+), 27 deletions(-)

diff --git a/mingw-w64-crt/stdio/scanf.S b/mingw-w64-crt/stdio/scanf.S
index ae8090e4c..d9584b206 100644
--- a/mingw-w64-crt/stdio/scanf.S
+++ b/mingw-w64-crt/stdio/scanf.S
@@ -169,22 +169,27 @@ __argtos:
 
 __argtos:
     push    {r4-r8, lr}
-    ldr     r12, [sp, #0]
+    ldr     r12, [sp, #24]
 
     cmp     r3, #0
-    ldrne   r5, [r2], #4
-    subsne  r3, r3, #1
-    ldrne   r6, [r2], #4
-    subsne  r3, r3, #1
-    moveq   r8, #0
-    beq     2b
+    beq     2f
+    subs    r3, r3, #1
+    ldr     r5, [r2], #4
+    beq     2f
+    subs    r3, r3, #1
+    ldr     r6, [r2], #4
+    beq     2f
 
+    /* Round the number of entries to an even number, to maintain
+     * 8 byte stack alignment. */
     mov     r8, r3
-    sub     sp, sp, r8
+    add     r8, r8, #1
+    bic     r8, r8, #1
+    sub     sp, sp, r8, lsl #2
     mov     r4, sp
 1:  ldr     r7, [r2], #4
-    str     r7, [r4], #4
     subs    r3, r3, #1
+    str     r7, [r4], #4
     bne     1b
 
 2:
@@ -192,7 +197,7 @@ __argtos:
     mov     r3, r6
     blx     r12
 
-    add     sp, sp, r8
+    add     sp, sp, r8, lsl #2
     pop     {r4-r8, pc}
 
 #elif defined (__aarch64__)
@@ -208,38 +213,43 @@ __argtos:
     mov     x11, x3
     mov     x12, x4
 
-    cmp     r11, #0
-    b.eq    2b
+    cmp     x11, #0
+    b.eq    2f
 
-    ldr     x2, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
+    ldr     x2, [x10], #8
+    b.eq    2f
 
-    ldr     x3, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
+    ldr     x3, [x10], #8
+    b.eq    2f
 
-    ldr     x4, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
+    ldr     x4, [x10], #8
+    b.eq    2f
 
-    ldr     x5, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
+    ldr     x5, [x10], #8
+    b.eq    2f
 
-    ldr     x6, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
+    ldr     x6, [x10], #8
+    b.eq    2f
 
-    ldr     x7, [x10], #8
     subs    x11, x11, #1
-    b.eq    2b
-
-    sub     sp, sp, x11
+    ldr     x7, [x10], #8
+    b.eq    2f
+
+    /* Round the number of entries to an even number, to maintain
+     * 16 byte stack alignment. */
+    mov     x13, x11
+    add     x13, x13, #1
+    bic     x13, x13, #1
+    sub     sp, sp, x13, lsl #3
     mov     x9, sp
 1:  ldr     x13, [x10], #8
-    str     x13, [x9], #8
     subs    x11, x11, #1
+    str     x13, [x9], #8
     b.ne    1b
 
 2:
-- 
2.25.1

_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to