Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #163926 > unrolled thread

unsafe C

Started byThiago Adams <thiago.adams@gmail.com>
First post2021-12-17 12:13 -0800
Last post2021-12-17 12:35 -0800
Articles 13 — 6 participants

Back to article view | Back to comp.lang.c


Contents

  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

#163926 — unsafe C

FromThiago Adams <thiago.adams@gmail.com>
Date2021-12-17 12:13 -0800
Subjectunsafe 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]


#163927

FromThiago Adams <thiago.adams@gmail.com>
Date2021-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]


#163929

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-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]


#163959

FromMeredith Montgomery <mmontgomery@levado.to>
Date2021-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]


#164078

FromMeredith Montgomery <mmontgomery@levado.to>
Date2021-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]


#164079

FromMeredith Montgomery <mmontgomery@levado.to>
Date2021-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]


#163974

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2021-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]


#163928

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-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]


#163930

FromThiago Adams <thiago.adams@gmail.com>
Date2021-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]


#163986

FromPeter 'Shaggy' Haywood <phaywood@alphalink.com.au>
Date2021-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]


#163999

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-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]


#163931

FromBen Bacarisse <ben.usenet@bsb.me.uk>
Date2021-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]


#163932

FromThiago Adams <thiago.adams@gmail.com>
Date2021-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