NetBSD Problem Report #59198

From www@netbsd.org  Wed Mar 19 23:22:14 2025
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 1EE841A9239
	for <gnats-bugs@gnats.NetBSD.org>; Wed, 19 Mar 2025 23:22:14 +0000 (UTC)
Message-Id: <20250319232212.8609C1A923C@mollari.NetBSD.org>
Date: Wed, 19 Mar 2025 23:22:12 +0000 (UTC)
From: alx@kernel.org
Reply-To: alx@kernel.org
To: gnats-bugs@NetBSD.org
Subject: Reject negative input in strtou(3), reporting ERANGE
X-Send-Pr-Version: www-1.0

>Number:         59198
>Category:       lib
>Synopsis:       Reject negative input in strtou(3), reporting ERANGE
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Mar 19 23:25:00 +0000 2025
>Last-Modified:  Fri Mar 21 20:35:00 +0000 2025
>Originator:     Alejandro Colomar
>Release:        10.1
>Organization:
Linux man-pages project, C Committee (WG14), shadow-utils
>Environment:
>Description:
strtou(3), just like the standard strtoumax(3) family of functions, first converts negative values into huge positive values, and then performs the range checks.

This is behavior that most programmers don't even know, less expect.

In every use case of this API, I've always wanted to reject negative input.  In the standard strtoumax(3), and also in the strtou(3) API.  However, I think it is more relevant in strtou(3), because we have explicit range checks.  If I say the minimum acceptable value is 0, then -1 is certainly not an acceptable value, not even converted via modulo arithmetics into UINTMAX_MAX.  It should result in ERANGE.

I have developed my own wrapper, strtou_noneg():

uintmax_t
strtou_noneg(const char *s, char **restrict endp, int base,
    uintmax_t min, uintmax_t max, int *restrict status)
{
	int  st;

	if (status == NULL)
		status = &st;
	if (strtoi(s, endp, base, 0, 1, status) == 0 && *status == ERANGE)
		return min;

	return strtou(s, endp, base, min, max, status);
}


and I always call that, because the behavior of strtou(3) is just evil.  Would you mind reforming strtou(3) to behave like that, rejecting negative input?  I suspect there are no real users of that misfeature, and probably many callers will silently have their bugs fixed.

While discussing standardization of this API, we found some bugs in GNU gettext where the author was unaware that strtoul(3) had this evil behavior.  There are many more programmers that are unaware of this.
>How-To-Repeat:
strtou("-1", NULL, 0, 0, UINTMAX_MAX, &status);

// status will be 0, but should be ERANGE
>Fix:
I have developed my own wrapper, strtou_noneg():

uintmax_t
strtou_noneg(const char *s, char **restrict endp, int base,
    uintmax_t min, uintmax_t max, int *restrict status)
{
	int  st;

	if (status == NULL)
		status = &st;
	if (strtoi(s, endp, base, 0, 1, status) == 0 && *status == ERANGE)
		return min;

	return strtou(s, endp, base, min, max, status);
}

This should be the actual implementation of strtou(3).

>Release-Note:

>Audit-Trail:

Responsible-Changed-From-To: misc-bug-people->lib-bug-people
Responsible-Changed-By: martin@NetBSD.org
Responsible-Changed-When: Thu, 20 Mar 2025 08:42:13 +0000
Responsible-Changed-Why:
Over to the library team


From: Alejandro Colomar <alx@kernel.org>
To: gnats-bugs@netbsd.org
Cc: Bruno Haible <bruno@clisp.org>
Subject: Re: lib/59198: Reject negative input in strtou(3), reporting ERANGE
Date: Fri, 21 Mar 2025 21:25:36 +0100

 --jrofmsi74wrqmdsk
 Content-Type: text/plain; protected-headers=v1; charset=utf-8
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable
 From: Alejandro Colomar <alx@kernel.org>
 To: gnats-bugs@netbsd.org
 Cc: Bruno Haible <bruno@clisp.org>
 Subject: Re: lib/59198: Reject negative input in strtou(3), reporting ERANGE
 References: <pr-misc-59198@gnats.netbsd.org>
  <20250319232212.8609C1A923C@mollari.NetBSD.org>
  <20250319232500.4D8401A923C@mollari.NetBSD.org>
 MIME-Version: 1.0
 In-Reply-To: <20250319232500.4D8401A923C@mollari.NetBSD.org>

 Hi,

 Actually, considering the name strtou(3), which is about **unsigned**
 integers, maybe negative numbers should not trigger an ERANGE error, but
 actually an ECANCELED error.  The sign, even a positive sign, should not
 be part of an unsigned number.

 Here are some example implementations for this:

 	unsigned long
 	strtoul_nn(const char *restrict s, char **restrict endp, int base)
 	{
 		while (isspace((unsigned char) *s))
 			s++;
 		if (!isdigit((unsigned char) *s))
 			return 0;

 		return strtoul(s, endp, base);
 	}

 	uintmax_t
 	strtou_nn(const char *restrict s, char **restrict endp, int base,
 	    uintmax_t min, uintmax_t max, int *status)
 	{
 		while (isspace((unsigned char) *s))
 			s++;
 		if (!isdigit((unsigned char) *s)) {
 			if (status)
 				*status =3D ECANCELED;
 			return min;
 		}

 		return strtou(s, endp, base, min, max, status);
 	}

 I'm going to expand the BUGS section in the strtoul(3) Linux manual
 page, and mention the strtoul_nn() example as a workaround for this bug
 in the standard strtoul(3).

 The strtou_nn() should be the ideal behavior of strtou(3).

 (I hope this arrives at the bug ticket.)


 Cheers,
 Alex

 --=20
 <https://www.alejandro-colomar.es/>

 --jrofmsi74wrqmdsk
 Content-Type: application/pgp-signature; name="signature.asc"

 -----BEGIN PGP SIGNATURE-----

 iQIzBAABCgAdFiEES7Jt9u9GbmlWADAi64mZXMKQwqkFAmfdyzkACgkQ64mZXMKQ
 wqmUkBAAi/WCW17t22OdSSJOMnHOcE4VudxM0Y7PZls5jE9oKxPIUoEcTlTR9566
 QKHMCJPswkazqUqrJMb4/4Gsr2RyHcJPJTz9HYqIK7BX4MZWOl/4BXe7Qam6POvn
 YIdBhKmUoePKfK+diicGqqfcWWUZXJIqZM1PD1JASveemrs6KxP5Iqjo5RzTJogV
 VSNwmLVgnqy3DO50usehl2bEeiDQLxGNPoKsIg9YRY0T3x3dVkcYkcV0guGw3riL
 7EhcJWFA6ENbuGrfRgAZhfXw2ltO5vb9KMxjplbjSLIPEDytDAFvtrfKuvV4pcbK
 7zEM3ql7lNH8czoeI6t/mkauUjnRkWLJ/dgnZvOm3eo0jjzcNsI3ZUlsjnklayHY
 8lzleyTBGLMZ3k76QPSXaQkaj109juWU02U7XKhlF+2CH+X+J3gDuvWZuJt3F6Ik
 PMk6Sb5vb12RI0NxS1LSX0NyXMZ7Y7ns+DD6M9aEY89hjhsaVgVtsu94LRyFRv1m
 G3fwhVAPPx1zmcZLtigWnv1PLKeYsVHydn85rcAouN3aCqvNLYezg9nXXbirrLOI
 yIeSAO1acFXVIprC/RBhBs9NIo8/Qj501pGtQ0TVfxZGhhvWKH51ERghybfQzQ3G
 TV5gfMRiSv9YkELM7uSCioXok5Z6sw77oMopRPS5Jj1fxrJYkTo=
 =RFuY
 -----END PGP SIGNATURE-----

 --jrofmsi74wrqmdsk--

From: Alejandro Colomar <alx@kernel.org>
To: gnats-bugs@netbsd.org
Cc: Bruno Haible <bruno@clisp.org>
Subject: Re: lib/59198: Reject negative input in strtou(3), reporting ERANGE
Date: Fri, 21 Mar 2025 21:34:42 +0100

 --77v3ngsjjqengmmj
 Content-Type: text/plain; protected-headers=v1; charset=utf-8
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable
 From: Alejandro Colomar <alx@kernel.org>
 To: gnats-bugs@netbsd.org
 Cc: Bruno Haible <bruno@clisp.org>
 Subject: Re: lib/59198: Reject negative input in strtou(3), reporting ERANGE
 References: <pr-misc-59198@gnats.netbsd.org>
  <20250319232212.8609C1A923C@mollari.NetBSD.org>
  <20250319232500.4D8401A923C@mollari.NetBSD.org>
  <knzempljqvci4o5v4qnunnog7l5yarhc5ttxktgyynkrfzyniv@2yj6ku46u76m>
 MIME-Version: 1.0
 In-Reply-To: <knzempljqvci4o5v4qnunnog7l5yarhc5ttxktgyynkrfzyniv@2yj6ku46u76m>

 On Fri, Mar 21, 2025 at 09:25:38PM +0100, Alejandro Colomar wrote:
 > Hi,
 >=20
 > Actually, considering the name strtou(3), which is about **unsigned**
 > integers, maybe negative numbers should not trigger an ERANGE error, but
 > actually an ECANCELED error.  The sign, even a positive sign, should not
 > be part of an unsigned number.
 >=20
 > Here are some example implementations for this:
 >=20
 > 	unsigned long
 > 	strtoul_nn(const char *restrict s, char **restrict endp, int base)
 > 	{
 > 		while (isspace((unsigned char) *s))
 > 			s++;
 > 		if (!isdigit((unsigned char) *s))

 This should call isxdigit(3).

 > 			return 0;
 >=20
 > 		return strtoul(s, endp, base);
 > 	}
 >=20
 > 	uintmax_t
 > 	strtou_nn(const char *restrict s, char **restrict endp, int base,
 > 	    uintmax_t min, uintmax_t max, int *status)
 > 	{
 > 		while (isspace((unsigned char) *s))
 > 			s++;
 > 		if (!isdigit((unsigned char) *s)) {

 This should call isxdigit(3).

 > 			if (status)
 > 				*status =3D ECANCELED;
 > 			return min;
 > 		}
 >=20
 > 		return strtou(s, endp, base, min, max, status);
 > 	}
 >=20
 > I'm going to expand the BUGS section in the strtoul(3) Linux manual
 > page, and mention the strtoul_nn() example as a workaround for this bug
 > in the standard strtoul(3).
 >=20
 > The strtou_nn() should be the ideal behavior of strtou(3).
 >=20
 > (I hope this arrives at the bug ticket.)
 >=20
 >=20
 > Cheers,
 > Alex
 >=20
 > --=20
 > <https://www.alejandro-colomar.es/>



 --=20
 <https://www.alejandro-colomar.es/>

 --77v3ngsjjqengmmj
 Content-Type: application/pgp-signature; name="signature.asc"

 -----BEGIN PGP SIGNATURE-----

 iQIzBAABCgAdFiEES7Jt9u9GbmlWADAi64mZXMKQwqkFAmfdzWEACgkQ64mZXMKQ
 wqkpLg/+LKjyFbj58SpjPDdXb/TKevnJ4mGg5b7x8d1VWuf+AO4zTu8Vqzvc7stH
 6nuGZxaurPGkEj+zxfOUUHwWUZzZ+xFIM4y69B9x+ldM8EHyATjzehR08+UPo3cY
 gt7pUDVYFVTTQEvcMZmdUXJUX/jV8zS1/ZO/pw9yXk6jgRQl4i1PDrg0Dzobz/zz
 WTYqhlBwsr6RYjFwvZFCGUpL5QuFV/8ssWkrF9hIa1O/fbPA1KQm26otIgziIk+k
 uHGN1E5IZavAS2Lp0sjVh9akMys945Shp4vk7gT3xMAXfLAiEIJg2BHw4cM3KFHb
 oMOCrY6x9GmkipJPGrRS3AbRcFBLOIG6DNfEJRXpKaOpbs7+54CJpAgrFTNKIdtL
 uTWxH2qrqUK/j/C3opnjER23poDJJNe6iLh/pERnp1cRAuPv5etzB6eZHQZzJaTx
 I420QqfKU2smX/3bKD1s7Le+G0svdRzmzuXz/WhWjkkBpHhrjX+xKBBiEHRSuvlh
 NByBBOTtmJ92RisUzJBaFFgBPDz8ywXw76R7u7hbOtS5MazeBljZWL4A65kmsIld
 Z5fkff57Q/uSklWUarx1lI+oEcudabY+zOxfo/ChUGOSUVfa99rlSrRwVc4wg+Pd
 t6lhf5+h2XnO5/yEMXD6xg8XJWxl/DOYiOnVUNIhJynO604D8ao=
 =SOm5
 -----END PGP SIGNATURE-----

 --77v3ngsjjqengmmj--

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2025 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.