Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #163926 > unrolled thread
| Started by | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| First post | 2021-12-17 12:13 -0800 |
| Last post | 2021-12-17 12:35 -0800 |
| Articles | 13 — 6 participants |
Back to article view | Back to comp.lang.c
unsafe C Thiago Adams <thiago.adams@gmail.com> - 2021-12-17 12:13 -0800
Re: unsafe C Thiago Adams <thiago.adams@gmail.com> - 2021-12-17 12:16 -0800
Re: unsafe C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-12-17 12:25 -0800
Re: unsafe C Meredith Montgomery <mmontgomery@levado.to> - 2021-12-18 12:40 -0300
Re: unsafe C Meredith Montgomery <mmontgomery@levado.to> - 2021-12-25 21:41 -0300
Re: unsafe C Meredith Montgomery <mmontgomery@levado.to> - 2021-12-25 21:42 -0300
Re: unsafe C Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-12-18 12:29 -0800
Re: unsafe C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-12-17 12:20 -0800
Re: unsafe C Thiago Adams <thiago.adams@gmail.com> - 2021-12-17 12:29 -0800
Re: unsafe C Peter 'Shaggy' Haywood <phaywood@alphalink.com.au> - 2021-12-19 17:16 +1100
Re: unsafe C Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-12-19 13:32 -0800
Re: unsafe C Ben Bacarisse <ben.usenet@bsb.me.uk> - 2021-12-17 20:30 +0000
Re: unsafe C Thiago Adams <thiago.adams@gmail.com> - 2021-12-17 12:35 -0800
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2021-12-17 12:13 -0800 |
| Subject | unsafe C |
| Message-ID | <d8227134-6e0b-486e-af91-aeece50a466bn@googlegroups.com> |
I realized strtoull is unsafe.
#include <stdlib.h>
#include <stdio.h>
int main() {
char* begin = "123456789011121314151617181920";
unsigned long long value = strtoull(begin, NULL, 10);
printf("%s\n", begin);
printf("%llu\n", value);
}
it prints
123456789011121314151617181920
18446744073709551615
[toc] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2021-12-17 12:16 -0800 |
| Message-ID | <54ebbb14-cb1e-4dd5-a4ec-9dd086e0eaa3n@googlegroups.com> |
| In reply to | #163926 |
On Friday, December 17, 2021 at 5:13:32 PM UTC-3, Thiago Adams wrote:
> I realized strtoull is unsafe.
>
> #include <stdlib.h>
> #include <stdio.h>
>
> int main() {
> char* begin = "123456789011121314151617181920";
> unsigned long long value = strtoull(begin, NULL, 10);
> printf("%s\n", begin);
> printf("%llu\n", value);
> }
> it prints
> 123456789011121314151617181920
> 18446744073709551615
The gcc compiler uses (internally) a better function:
unsigned long long value = 123456789011121314151617181920;
warning: integer constant is too large for its type
unsigned long long value = 123456789011121314151617181920;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
it is funny that this is a warning and not an error.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2021-12-17 12:25 -0800 |
| Message-ID | <87pmpv6kss.fsf@nosuchdomain.example.com> |
| In reply to | #163927 |
Thiago Adams <thiago.adams@gmail.com> writes:
> On Friday, December 17, 2021 at 5:13:32 PM UTC-3, Thiago Adams wrote:
>> I realized strtoull is unsafe.
>>
>> #include <stdlib.h>
>> #include <stdio.h>
>>
>> int main() {
>> char* begin = "123456789011121314151617181920";
>> unsigned long long value = strtoull(begin, NULL, 10);
>> printf("%s\n", begin);
>> printf("%llu\n", value);
>> }
>> it prints
>> 123456789011121314151617181920
>> 18446744073709551615
>
> The gcc compiler uses (internally) a better function:
I don't know what gcc uses internally, but it could well be strtoull().
> unsigned long long value = 123456789011121314151617181920;
>
> warning: integer constant is too large for its type
> unsigned long long value = 123456789011121314151617181920;
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> it is funny that this is a warning and not an error.
That's a constraint violation (N1570 6.4.4p2), but I'm not surprised
that gcc issues a mere warning. A non-fatal warning is a diagnostic, so
it satisfies the standard's requirement. gcc does that by default in a
lot of cases. Use "-pedantic-errors" to get a fatal error.
(My personal preference would be for constraint violations to be treated
as fatal errors by defaut, but the standard doesn't require that.)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Meredith Montgomery <mmontgomery@levado.to> |
|---|---|
| Date | 2021-12-18 12:40 -0300 |
| Message-ID | <86wnk153br.fsf@levado.to> |
| In reply to | #163927 |
Thiago Adams <thiago.adams@gmail.com> writes:
> On Friday, December 17, 2021 at 5:13:32 PM UTC-3, Thiago Adams wrote:
>> I realized strtoull is unsafe.
>>
>> #include <stdlib.h>
>> #include <stdio.h>
>>
>> int main() {
>> char* begin = "123456789011121314151617181920";
>> unsigned long long value = strtoull(begin, NULL, 10);
>> printf("%s\n", begin);
>> printf("%llu\n", value);
>> }
>> it prints
>> 123456789011121314151617181920
>> 18446744073709551615
>
> The gcc compiler uses (internally) a better function:
>
> unsigned long long value = 123456789011121314151617181920;
>
> warning: integer constant is too large for its type
> unsigned long long value = 123456789011121314151617181920;
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> it is funny that this is a warning and not an error.
Lol. So true.
When I read an integer from a numeric-string, I usually use a procedure
that I once saw in some library written by Daniel J. Bernstein. The
code below is my typing of his code, so I might have changed variable
names, spacing, added or removed comments or something like that.
--8<---------------cut here---------------start------------->8---
#include <limits.h>
#include <inttypes.h>
int scan_ulong(register char *s, register unsigned long *u)
{
register unsigned int pos;
register unsigned long r;
register unsigned long c;
pos = 0; r = 0;
for ( ;; ) {
c = (unsigned long) (unsigned char) (s[pos] - '0');
if (c < 10) {
if( ((ULONG_MAX - c) / 10) >= r)
r = r * 10 + c;
else return -1; /* lack of space */
++pos; continue;
}
break;
}
*u = r;
return pos;
}
--8<---------------cut here---------------end--------------->8---
[toc] | [prev] | [next] | [standalone]
| From | Meredith Montgomery <mmontgomery@levado.to> |
|---|---|
| Date | 2021-12-25 21:41 -0300 |
| Message-ID | <86sfug19l8.fsf@levado.to> |
| In reply to | #163959 |
ram@zedat.fu-berlin.de (Stefan Ram) writes:
> Meredith Montgomery <mmontgomery@levado.to> writes:
>>> it is funny that this is a warning and not an error.
>
>>int scan_ulong(register char *s, register unsigned long *u)
>>{
>> register unsigned int pos;
> ...
>> return pos;
>>}
>
> It returns the /unsigned/ int "pos", but its return type is
> declared /signed/ int?
>
> /u/ is accessed at most once in the function, but declared
> "register"?
Let's see. I don't think I would've changed that much, but I might.
Lol. I did. I can't even copy properly! Thanks for spotting that.
Hm, wait! The check for overflow is not his. It's mine. Alas, memory
failing me. Daniel J. Bernstein, sorry about that.
--8<---------------cut here---------------start------------->8---
/* Public domain. */
#include "scan.h"
unsigned int scan_ulong(register const char *s,register unsigned long *u)
{
register unsigned int pos = 0;
register unsigned long result = 0;
register unsigned long c;
while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
result = result * 10 + c;
++pos;
}
*u = result;
return pos;
}
--8<---------------cut here---------------end--------------->8---
But, yes, he did qualify u with ``register''.
[toc] | [prev] | [next] | [standalone]
| From | Meredith Montgomery <mmontgomery@levado.to> |
|---|---|
| Date | 2021-12-25 21:42 -0300 |
| Message-ID | <86lf0819k7.fsf@levado.to> |
| In reply to | #164078 |
Meredith Montgomery <mmontgomery@levado.to> writes:
> ram@zedat.fu-berlin.de (Stefan Ram) writes:
>
>> Meredith Montgomery <mmontgomery@levado.to> writes:
>>>> it is funny that this is a warning and not an error.
>>
>>>int scan_ulong(register char *s, register unsigned long *u)
>>>{
>>> register unsigned int pos;
>> ...
>>> return pos;
>>>}
>>
>> It returns the /unsigned/ int "pos", but its return type is
>> declared /signed/ int?
>>
>> /u/ is accessed at most once in the function, but declared
>> "register"?
>
> Let's see. I don't think I would've changed that much, but I might.
> Lol. I did. I can't even copy properly! Thanks for spotting that.
>
> Hm, wait! The check for overflow is not his. It's mine. Alas, memory
> failing me. Daniel J. Bernstein, sorry about that.
>
> /* Public domain. */
>
> #include "scan.h"
>
> unsigned int scan_ulong(register const char *s,register unsigned long *u)
> {
> register unsigned int pos = 0;
> register unsigned long result = 0;
> register unsigned long c;
> while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10) {
> result = result * 10 + c;
> ++pos;
> }
> *u = result;
> return pos;
> }
>
> But, yes, he did qualify u with ``register''.
By the way, that's src/scan_ulong.c in daemontools 0.76.
[toc] | [prev] | [next] | [standalone]
| From | Andrey Tarasevich <andreytarasevich@hotmail.com> |
|---|---|
| Date | 2021-12-18 12:29 -0800 |
| Message-ID | <splgb5$v7n$1@dont-email.me> |
| In reply to | #163927 |
On 12/17/2021 12:16 PM, Thiago Adams wrote:
> On Friday, December 17, 2021 at 5:13:32 PM UTC-3, Thiago Adams wrote:
>> I realized strtoull is unsafe.
>>
>> #include <stdlib.h>
>> #include <stdio.h>
>>
>> int main() {
>> char* begin = "123456789011121314151617181920";
>> unsigned long long value = strtoull(begin, NULL, 10);
>> printf("%s\n", begin);
>> printf("%llu\n", value);
>> }
>> it prints
>> 123456789011121314151617181920
>> 18446744073709551615
>
> The gcc compiler uses (internally) a better function:
>
> unsigned long long value = 123456789011121314151617181920;
>
> warning: integer constant is too large for its type
> unsigned long long value = 123456789011121314151617181920;
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> it is funny that this is a warning and not an error.
>
By default GCC works in a sort of "backward compatibility" mode. It
attempts to marry two seemingly incompatible requirements:
1. Properly supporting modern C standards
2. Compiling legacy code, which often contains what's considered
constraint violations (i.e. hard errors) by modern C standards
In other to achieve that GCC uses a "loophole" purposely designed into
the standard: a compiler is required to issue a diagnostic message for a
constraint violation, but is not required to stop translation or refuse
to produce some sort of "result".
So, this is what GCC often does: it barks a "warning" for a hard error,
and just continues translation following some sort of amalgamation of
legacy C standards. This is what many GCC "warnings" are: hard errors,
which just happen to be reported as non-fatal warnings.
In your specific example you are looking at a constant that is too long
to fit into the largest integer type. In C89/90 this situation was not
considered a constraint violation. It was handed over to undefined
behavior. Starting from C99 this became a constraint violation. This
disparity between C89/90 and later C standards is what persuades GCC to
report this error as a "warning".
--
Best regards,
Andrey Tarasevich
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2021-12-17 12:20 -0800 |
| Message-ID | <87tuf76l17.fsf@nosuchdomain.example.com> |
| In reply to | #163926 |
Thiago Adams <thiago.adams@gmail.com> writes:
> I realized strtoull is unsafe.
>
> #include <stdlib.h>
> #include <stdio.h>
>
> int main() {
> char* begin = "123456789011121314151617181920";
> unsigned long long value = strtoull(begin, NULL, 10);
> printf("%s\n", begin);
> printf("%llu\n", value);
> }
> it prints
> 123456789011121314151617181920
> 18446744073709551615
It's a little tricky, but it's not unsafe.
The strtol, strtoll, strtoul, and strtoull functions return the
converted value, if any. If no conversion could be performed, zero
is returned. If the correct value is outside the range of
representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX,
ULONG_MAX, or ULLONG_MAX is returned (according to the return type
and sign of the value, if any), and the value of the macro ERANGE is
stored in errno.
If you set errno to 0 before calling strtoull() and check its value
after the call (before calling anything else that might set errno),
you'll find it's been set to ERANGE to indicate the error.
(18446744073709551615 is LLONG_MAX, assuming 64 bits.)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2021-12-17 12:29 -0800 |
| Message-ID | <928dd852-efe3-43af-91cf-acdbac8b4005n@googlegroups.com> |
| In reply to | #163928 |
On Friday, December 17, 2021 at 5:20:34 PM UTC-3, Keith Thompson wrote:
> Thiago Adams <thiago...@gmail.com> writes:
> > I realized strtoull is unsafe.
> >
> > #include <stdlib.h>
> > #include <stdio.h>
> >
> > int main() {
> > char* begin = "123456789011121314151617181920";
> > unsigned long long value = strtoull(begin, NULL, 10);
> > printf("%s\n", begin);
> > printf("%llu\n", value);
> > }
> > it prints
> > 123456789011121314151617181920
> > 18446744073709551615
> It's a little tricky, but it's not unsafe.
>
> The strtol, strtoll, strtoul, and strtoull functions return the
> converted value, if any. If no conversion could be performed, zero
> is returned. If the correct value is outside the range of
> representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX,
> ULONG_MAX, or ULLONG_MAX is returned (according to the return type
> and sign of the value, if any), and the value of the macro ERANGE is
> stored in errno.
>
> If you set errno to 0 before calling strtoull() and check its value
> after the call (before calling anything else that might set errno),
> you'll find it's been set to ERANGE to indicate the error.
>
> (18446744073709551615 is LLONG_MAX, assuming 64 bits.)
>
> --
> Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
> Working, but not speaking, for Philips
> void Void(void) { Void(); } /* The recursive call of the void */
wow.. I wasn't unexpecting that I didn't check the documentation
thanks.
In any case I think I will implement a safer version then I can have
a warning in my c front end.
C23 will add some checked function.
#include <stdckdint.h>
bool ckd_add(type1 *result, type2 a, type3 b);
bool ckd_sub(type1 *result, type2 a, type3 b);
bool ckd_mul(type1 *result, type2 a, type3 b);
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2792.pdf
I can try to implement a safer strtoull using these functions.
and of course I will have to find a implementation of ckd_mul and ckd_add
[toc] | [prev] | [next] | [standalone]
| From | Peter 'Shaggy' Haywood <phaywood@alphalink.com.au> |
|---|---|
| Date | 2021-12-19 17:16 +1100 |
| Message-ID | <8av29i-dr1.ln1@aretha.foo> |
| In reply to | #163928 |
Groovy hepcat Keith Thompson was jivin' in comp.lang.c on Sat, 18 Dec
2021 07:20 am. It's a cool scene! Dig it.
> (18446744073709551615 is LLONG_MAX, assuming 64 bits.)
ULLONG_MAX, no?
--
----- Dig the NEW and IMPROVED news sig!! -----
-------------- Shaggy was here! ---------------
Ain't I'm a dawg!!
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <Keith.S.Thompson+u@gmail.com> |
|---|---|
| Date | 2021-12-19 13:32 -0800 |
| Message-ID | <875yrk702n.fsf@nosuchdomain.example.com> |
| In reply to | #163986 |
Peter 'Shaggy' Haywood <phaywood@alphalink.com.au> writes:
> Groovy hepcat Keith Thompson was jivin' in comp.lang.c on Sat, 18 Dec
> 2021 07:20 am. It's a cool scene! Dig it.
>
>> (18446744073709551615 is LLONG_MAX, assuming 64 bits.)
>
> ULLONG_MAX, no?
ULLONG_MAX, yes.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2021-12-17 20:30 +0000 |
| Message-ID | <87fsqrrn2s.fsf@bsb.me.uk> |
| In reply to | #163926 |
Thiago Adams <thiago.adams@gmail.com> writes:
> I realized strtoull is unsafe.
>
> #include <stdlib.h>
> #include <stdio.h>
>
> int main() {
> char* begin = "123456789011121314151617181920";
> unsigned long long value = strtoull(begin, NULL, 10);
> printf("%s\n", begin);
> printf("%llu\n", value);
> }
> it prints
> 123456789011121314151617181920
> 18446744073709551615
You didn't listen to what strtoull had to say! When the result would be
out of range for the return type, the strto* functions return the
maximum or minimum representable value and set errno to ERANGE.
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | Thiago Adams <thiago.adams@gmail.com> |
|---|---|
| Date | 2021-12-17 12:35 -0800 |
| Message-ID | <06df9dc6-8d5a-4567-9ac6-0327d56e63a0n@googlegroups.com> |
| In reply to | #163931 |
On Friday, December 17, 2021 at 5:30:46 PM UTC-3, Ben Bacarisse wrote:
> Thiago Adams <thiago...@gmail.com> writes:
>
> > I realized strtoull is unsafe.
> >
> > #include <stdlib.h>
> > #include <stdio.h>
> >
> > int main() {
> > char* begin = "123456789011121314151617181920";
> > unsigned long long value = strtoull(begin, NULL, 10);
> > printf("%s\n", begin);
> > printf("%llu\n", value);
> > }
> > it prints
> > 123456789011121314151617181920
> > 18446744073709551615
> You didn't listen to what strtoull had to say! When the result would be
> out of range for the return type, the strto* functions return the
> maximum or minimum representable value and set errno to ERANGE.
Yes, I can use strtoull to emit this warnings. You are right.
I don't need to implement a new one just check the result. The same for gcc,
they can use strtoull internally.
And I can use the unsigned long long version to check overflow for small integers
like int or char.
char c = 12345;
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.c
csiph-web