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 f8139bb7933ce2a214ea225ec99fbe009ebd5953 Author: Marco Casaroli <[email protected]> AuthorDate: Thu Nov 5 06:51:56 2026 +0100 libs/libc/stream: Read a long double for the L length modifier. printf() took %Lf, %Le, %Lg and %La as double conversions: the L modifier set the same flag as l, and the conversion read the argument with va_arg(ap, double). A long double has a different size and is passed in a different place on arm64, RISC-V and x86_64. The output was wrong, and the arguments after it were also wrong. On arm64 and RISC-V the low 64 bits of a binary128 value, taken as a double, can have more than 300 digits, and a buffer of the correct size for the number then overflows. Now L sets the flags of ll. A floating point conversion with these flags takes a long double, also in a numbered argument. An integer conversion with L takes a long long, as in GNU libc: it was a long, which is smaller on a 32-bit CPU. The conversion to double means that the output has the precision and the range of a double. If CONFIG_HAVE_LONG_DOUBLE is not defined, nothing changes for a floating point conversion. 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 | 51 +++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/libs/libc/stream/lib_libvsprintf.c b/libs/libc/stream/lib_libvsprintf.c index 6a56c94f563..bdd944be9b8 100644 --- a/libs/libc/stream/lib_libvsprintf.c +++ b/libs/libc/stream/lib_libvsprintf.c @@ -97,11 +97,21 @@ #define FL_FLTEXP 0x4000 #define FL_FLTFIX 0x8000 +/* L, as ll, means a long double in a floating point conversion */ + +#ifdef CONFIG_HAVE_LONG_DOUBLE +# define FL_IS_LDOUBLE(f) (((f) & (FL_LONG | FL_REPD_TYPE)) == \ + (FL_LONG | FL_REPD_TYPE)) +#else +# define FL_IS_LDOUBLE(f) 0 +#endif + #define TYPE_INT 1 #define TYPE_LONG 2 #define TYPE_LONG_LONG 3 #define TYPE_DOUBLE 4 #define TYPE_CHAR_POINTER 5 +#define TYPE_LONG_DOUBLE 6 /* Support special access to CODE-space strings for Harvard architectures */ @@ -538,7 +548,14 @@ set_star: continue; } - if (c == 'l' || c == 'L') + if (c == 'L') + { + flags |= FL_LONG | FL_REPD_TYPE; + flags &= ~FL_SHORT; + continue; + } + + if (c == 'l') { if ((flags & FL_LONG) != 0) { @@ -607,7 +624,7 @@ set_star: if (fmt_isfloat(c)) { - type = TYPE_DOUBLE; + type = FL_IS_LDOUBLE(flags) ? TYPE_LONG_DOUBLE : TYPE_DOUBLE; } else if (c == 'i' || c == 'd' || c == 'u' || c == 'p' || c == 'o' || c == 'x' || c == 'X') @@ -708,6 +725,15 @@ flt_oper: value = arglist->value[argnumber - 1].d; } else +#endif +#ifdef CONFIG_HAVE_LONG_DOUBLE + if (FL_IS_LDOUBLE(flags)) + { + /* The output has the precision of a double */ + + value = va_arg(ap, long double); + } + else #endif { value = va_arg(ap, double); @@ -1015,7 +1041,16 @@ flt_oper: if ((flags & FL_ARGNUMBER) == 0) # endif { - va_arg(ap, double); +# ifdef CONFIG_HAVE_LONG_DOUBLE + if (FL_IS_LDOUBLE(flags)) + { + va_arg(ap, long double); + } + else +# endif + { + va_arg(ap, double); + } } pnt = "?"; @@ -1598,6 +1633,16 @@ int lib_vsprintf(FAR struct lib_outstream_s *stream, break; #endif +#ifdef CONFIG_HAVE_LONG_DOUBLE + case TYPE_LONG_DOUBLE: +# ifdef CONFIG_LIBC_FLOATINGPOINT + arglist.value[i].d = va_arg(ap, long double); +# else + va_arg(ap, long double); +# endif + break; +#endif + case TYPE_CHAR_POINTER: arglist.value[i].cp = va_arg(ap, FAR char *); break;
