From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.5 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, MAILING_LIST_MULTI,RCVD_IN_DNSWL_LOW,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: (qmail 29408 invoked from network); 31 May 2023 10:06:23 -0000 Received: from second.openwall.net (193.110.157.125) by inbox.vuxu.org with ESMTPUTF8; 31 May 2023 10:06:23 -0000 Received: (qmail 18008 invoked by uid 550); 31 May 2023 10:05:48 -0000 Mailing-List: contact musl-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Reply-To: musl@lists.openwall.com Received: (qmail 17852 invoked from network); 31 May 2023 10:05:46 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=inria.fr; s=dc; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=J2oQVx4h2PK5zCw8dd/WE0XEtUOz0qGPr69uhsqzPr4=; b=t9K6hWzn0+JQx7s1L2l1+2/lN3fMqf58EuwOPFnqIwIcxwWTj7+j9ML+ MAoOmFHJ8He0K2cm5AJiWZ3/wpWfdWtDIyF+WaB4gwn1OErP5Vt64A0rW k1DiE6a876P+9ySk0w89tcRfseVOOSpEtrsfIjy7L4P+sl3mnoAWrtbHU A=; Authentication-Results: mail3-relais-sop.national.inria.fr; dkim=none (message not signed) header.i=none; spf=SoftFail smtp.mailfrom=Jens.Gustedt@inria.fr; dmarc=fail (p=none dis=none) d=inria.fr X-IronPort-AV: E=Sophos;i="6.00,205,1681164000"; d="scan'208";a="57434664" From: Jens Gustedt To: musl@lists.openwall.com Date: Wed, 31 May 2023 12:05:17 +0200 Message-Id: X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [musl] [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 +#include +#include +#include +#include + +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