|
Message-Id: <f68175347554b6db5f38c4de78c5682e01ab3159.1685527375.git.Jens.Gustedt@inria.fr> Date: Wed, 31 May 2023 12:05:17 +0200 From: Jens Gustedt <Jens.Gustedt@...ia.fr> To: musl@...ts.openwall.com Subject: [C23 new stdlib 3/3] C23: implement the new strfrom[dfl] functions These names had been reserved in C17, so it is not necessary to hide these function in conditionals. With the exception of strfroml, the implementation is direct because format strings can be forwarded to snprintf (there is no length modifier for float or double). For strfroml the format has to be assembled from the received format to interlace "L". Because compilers will probably not check their formats for these new functions for some generations, in general this would be passing an unsanitized dynamic format string into snprintf. So we do a relatively simple check before hand, in particular to inhibit appearance of other "%" specifiers in the string that could be used for attacks. --- include/stdlib.h | 4 ++++ src/stdlib/strfromd.c | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/stdlib/strfromd.c diff --git a/include/stdlib.h b/include/stdlib.h index 10bdf7f8..72522cd6 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -29,6 +29,10 @@ float strtof (const char *__restrict, char **__restrict); double strtod (const char *__restrict, char **__restrict); long double strtold (const char *__restrict, char **__restrict); +int strfromd(char *restrict, size_t, const char *restrict, double); +int strfromf(char *restrict, size_t, const char *restrict, float); +int strfroml(char *restrict, size_t, const char *restrict, long double); + long strtol (const char *__restrict, char **__restrict, int); unsigned long strtoul (const char *__restrict, char **__restrict, int); long long strtoll (const char *__restrict, char **__restrict, int); diff --git a/src/stdlib/strfromd.c b/src/stdlib/strfromd.c new file mode 100644 index 00000000..f5b92956 --- /dev/null +++ b/src/stdlib/strfromd.c @@ -0,0 +1,44 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <limits.h> + +static size_t sanitize(const char*format) { + size_t slen = format ? strlen(format) : 0; + if (format[0] != '%' + || (slen > 2 && format[1] != '.') + || strchr(&format[1], '%') + || (strspn(&format[slen-1], "aAeEfFgG") != 1)) return 0; + else return slen; +} + +int strfromd(char *restrict s, size_t n, const char *restrict format, double fp) { + return sanitize(format) ? snprintf(s, n, format, fp) : -1; +} + +int strfromf(char *restrict s, size_t n, const char *restrict format, float fp) { + return sanitize(format) ? snprintf(s, n, format, fp) : -1; +} + +int strfroml(char *restrict s, size_t n, const char *restrict format, long double fp) { + enum { max_len = 1+sizeof "%.18446744073709551615Lg", }; + char ff[max_len]; + size_t slen = sanitize(format); + if (!slen) return -1; + if (slen < max_len-2) { + memcpy(ff, format, slen-1); + ff[slen-1] = 'L'; + ff[slen] = format[slen-1]; + ff[slen+1] = 0; + } else { + // If the precision is unreasonably long, fallback to + // strtoull to parse it, and squeeze it into a + // reasonable length, if possible. + int eback = errno; + unsigned long long prec = strtoull(format+2, NULL, 10); + if (prec == ULLONG_MAX) errno = eback; + snprintf(ff, max_len, "%%.%lldL%c", prec, format[slen-1]); + } + return snprintf(s, n, ff, fp); +} -- 2.34.1
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.