This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit e45a1ee8eb5647bbc8de822852659c90cec8b361
Author: Marco Casaroli <[email protected]>
AuthorDate: Thu Nov 5 06:51:54 2026 +0100

    libs/libc/stream: Record the type of each numbered argument.
    
    lib_vsprintf() reads the numbered arguments between two passes over the
    format, with the types from the first pass.  arglist.type[] had no
    initial value, and the first pass had no type for %x, %X, %o, %S and %n
    and for an argument number that the format does not use.  So "%2$s %1$x"
    skipped an argument or read one with a size from uninitialized memory,
    and the %s got a wrong pointer.  "%2$s%1$n" printed from an uninitialized
    stack.
    
    Clear arglist.type[] and record the type of these conversions.  At a
    number that the format does not use, the size is not known and the
    place of the next arguments is also not known: read only the arguments
    before it.  A conversion with a number after that ends the output, as a
    number above NL_ARGMAX does.
    
    Part of https://github.com/apache/nuttx/issues/21467
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 libs/libc/stream/lib_libvsprintf.c | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/libs/libc/stream/lib_libvsprintf.c 
b/libs/libc/stream/lib_libvsprintf.c
index 80083855c1c..a53292a79d5 100644
--- a/libs/libc/stream/lib_libvsprintf.c
+++ b/libs/libc/stream/lib_libvsprintf.c
@@ -605,7 +605,8 @@ static int vsprintf_internal(FAR struct lib_outstream_s 
*stream,
                     {
                       arglist->type[argnumber - 1] = TYPE_DOUBLE;
                     }
-                  else if (c == 'i' || c == 'd' || c == 'u' || c == 'p')
+                  else if (c == 'i' || c == 'd' || c == 'u' || c == 'p' ||
+                           c == 'o' || c == 'x' || c == 'X')
                     {
                       if ((flags & FL_LONG) == 0)
                         {
@@ -624,7 +625,7 @@ static int vsprintf_internal(FAR struct lib_outstream_s 
*stream,
                     {
                       arglist->type[argnumber - 1] = TYPE_INT;
                     }
-                  else if (c == 's')
+                  else if (c == 's' || c == 'S' || c == 'n')
                     {
                       arglist->type[argnumber - 1] = TYPE_CHAR_POINTER;
                     }
@@ -1524,7 +1525,11 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream,
 #ifdef CONFIG_LIBC_NUMBERED_ARGS
   /* We do 2 passes of parsing and fill the arglist between the passes. */
 
-  struct arg_s arglist;
+  struct arg_s arglist =
+    {
+      { 0 }
+    };
+
   int numargs = vsprintf_internal(NULL, &arglist, NL_ARGMAX, fmt, ap);
   union arg_u argvalue[MAX(numargs, 1)];
   int i;
@@ -1556,6 +1561,15 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream,
           case TYPE_CHAR_POINTER:
             arglist.value[i].cp = va_arg(ap, FAR char *);
             break;
+
+          default:
+
+            /* A number was not used, so its size is not known and the next
+             * arguments cannot be found.  Use only the arguments before it.
+             */
+
+            numargs = i;
+            break;
         }
     }
 

Reply via email to