Re: Inconsistent behavior with dd(1)

2014-09-11 Thread William Orr
Hey, any thoughts on this? Does it need more work? Can it get merged?

On 08/25/2014 09:49 PM, William Orr wrote:
 On 8/18/14 12:00 PM, William Orr wrote:
 Reply inline.

 On 08/16/2014 10:34 AM, John-Mark Gurney wrote:
 Alan Somers wrote this message on Fri, Aug 15, 2014 at 10:42 -0600:
 On Thu, Aug 14, 2014 at 11:55 PM, William Orr w...@worrbase.com
 wrote:
 Hey,

 I found some inconsistent behavior with dd(1) when it comes to
 specifying arguments in -CURRENT.

   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null
 count=18446744073709551616
 dd: count: Result too large
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null
 count=18446744073709551617
 dd: count: Result too large
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null
 count=18446744073709551615
 dd: count cannot be negative
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null
 count=-18446744073709551615
 1+0 records in
 1+0 records out
 512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
 dd: count cannot be negative

 ???

 Any chance someone has the time and could take a look?
 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263

 Thanks,
 William Orr

 ???

 IMHO, this is a bug in strtouq(3), not in dd(1).  Why should it parse
 negative numbers at all, when there is stroq(3) for that purpose?  The
 standard is clear that it must, though.  Oddly enough, stroq would
 probably not accept -18446744073709551615, even though strtouq does.
 Specific comments on your patch below:


 Here???s the patch:

 Index: bin/dd/args.c
 ===
 --- bin/dd/args.c   (revision 267712)
 +++ bin/dd/args.c   (working copy)
 @@ -186,46 +186,31 @@
   static void
   f_bs(char *arg)
   {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, bs must be between 1 and %jd,
 (intmax_t)SSIZE_MAX);
 -   in.dbsz = out.dbsz = (size_t)res;
 +   in.dbsz = out.dbsz = get_num(arg);
 +   if (in.dbsz  1 || out.dbsz  1)
 Why do you need to check both in and out?  Aren't they the same?
 Also, you eliminated the check for overflowing SSIZE_MAX.  That's not
 ok, because these values get passed to places that expect signed
 numbers, for example in dd.c:303.
 The type of dbsz is size_t, so really:

 +   errx(1, bs must be between 1 and %ju,
 (uintmax_t)-1);
 This should be SIZE_MAX, except there isn't a define for this?  So maybe
 the code really should be:
(uintmax_t)(size_t)-1

 to get the correct value for SIZE_MAX...

 Otherwise on systems that uintmax_t is 32bits and size_t is 32bits,
 the error message will be wrong...
 Yes, this should probably be SIZE_MAX rather than that cast. Same with
 the others

   }

   static void
   f_cbs(char *arg)
   {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, cbs must be between 1 and %jd,
 (intmax_t)SSIZE_MAX);
 -   cbsz = (size_t)res;
 +   cbsz = get_num(arg);
 +   if (cbsz  1)
 +   errx(1, cbs must be between 1 and %ju,
 (uintmax_t)-1);
   }
 Again, you eliminated the check for SSIZE_MAX, but cbsz must be signed.
 What do you mean by this?  cbsz is size_t which is unsigned...
 I believe he's referring to this use of cbsz/in.dbsz/out.dbsz:

 https://svnweb.freebsd.org/base/head/bin/dd/dd.c?revision=265698view=markup#l171


 Really, this is more wrong since there is math inside of a malloc(3)
 call without any overflow handling. By virtue of making this max out at
 a ssize_t, it becomes more unlikely that you'll have overflow.

 This math should probably be done ahead of time with proper overflow
 handling. I'll include that in my next patch, if there's no objection.

 I don't see any other reason why in.dbsz, out.dbsz or cbsz should be
 signed, but it's very possible that I didn't look hard enough.

 Again, the cast above is wrong...  Maybe we should add a SIZE_MAX
 define so we don't have to see the double cast...

   static void
   f_count(char *arg)
   {
 -   intmax_t res;
 -
 -   res = (intmax_t)get_num(arg);
 -   if (res  0)
 -   errx(1, count cannot be negative);
 -   if (res == 0)
 -   cpy_cnt = (uintmax_t)-1;
 This is a special case.  See dd_in().  I think that eliminating this
 special case will have the unintended effect of breaking count=0.

 -   else
 -   cpy_cnt = (uintmax_t)res;
 +   cpy_cnt = get_num(arg);
   }

   static void
   f_files(char *arg)
   {
 -
 Don't eliminate these blank lines.. they are intentional per style(9):
   /* Insert an empty line if the function has no local
 variables. */

  files_cnt = get_num(arg);
  if (files_cnt  1)
 -   errx(1, files must be between 1 and %jd,
 (uintmax_t)-1);
 +   errx(1, files must be between 1 and %ju,
 (uintmax_t)-1

Re: Inconsistent behavior with dd(1)

2014-08-25 Thread William Orr

On 8/18/14 12:00 PM, William Orr wrote:

Reply inline.

On 08/16/2014 10:34 AM, John-Mark Gurney wrote:

Alan Somers wrote this message on Fri, Aug 15, 2014 at 10:42 -0600:

On Thu, Aug 14, 2014 at 11:55 PM, William Orr w...@worrbase.com wrote:

Hey,

I found some inconsistent behavior with dd(1) when it comes to specifying 
arguments in -CURRENT.

  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551616
dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551617
dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551615
dd: count cannot be negative
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=-18446744073709551615
1+0 records in
1+0 records out
512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
dd: count cannot be negative

???

Any chance someone has the time and could take a look? 
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263

Thanks,
William Orr

???


IMHO, this is a bug in strtouq(3), not in dd(1).  Why should it parse
negative numbers at all, when there is stroq(3) for that purpose?  The
standard is clear that it must, though.  Oddly enough, stroq would
probably not accept -18446744073709551615, even though strtouq does.
Specific comments on your patch below:



Here???s the patch:

Index: bin/dd/args.c
===
--- bin/dd/args.c   (revision 267712)
+++ bin/dd/args.c   (working copy)
@@ -186,46 +186,31 @@
  static void
  f_bs(char *arg)
  {
-   uintmax_t res;
-
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, bs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
-   in.dbsz = out.dbsz = (size_t)res;
+   in.dbsz = out.dbsz = get_num(arg);
+   if (in.dbsz  1 || out.dbsz  1)

Why do you need to check both in and out?  Aren't they the same?
Also, you eliminated the check for overflowing SSIZE_MAX.  That's not
ok, because these values get passed to places that expect signed
numbers, for example in dd.c:303.

The type of dbsz is size_t, so really:


+   errx(1, bs must be between 1 and %ju, (uintmax_t)-1);

This should be SIZE_MAX, except there isn't a define for this?  So maybe
the code really should be:
   (uintmax_t)(size_t)-1

to get the correct value for SIZE_MAX...

Otherwise on systems that uintmax_t is 32bits and size_t is 32bits,
the error message will be wrong...

Yes, this should probably be SIZE_MAX rather than that cast. Same with
the others


  }

  static void
  f_cbs(char *arg)
  {
-   uintmax_t res;
-
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, cbs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
-   cbsz = (size_t)res;
+   cbsz = get_num(arg);
+   if (cbsz  1)
+   errx(1, cbs must be between 1 and %ju, (uintmax_t)-1);
  }

Again, you eliminated the check for SSIZE_MAX, but cbsz must be signed.

What do you mean by this?  cbsz is size_t which is unsigned...

I believe he's referring to this use of cbsz/in.dbsz/out.dbsz:

https://svnweb.freebsd.org/base/head/bin/dd/dd.c?revision=265698view=markup#l171

Really, this is more wrong since there is math inside of a malloc(3)
call without any overflow handling. By virtue of making this max out at
a ssize_t, it becomes more unlikely that you'll have overflow.

This math should probably be done ahead of time with proper overflow
handling. I'll include that in my next patch, if there's no objection.

I don't see any other reason why in.dbsz, out.dbsz or cbsz should be
signed, but it's very possible that I didn't look hard enough.


Again, the cast above is wrong...  Maybe we should add a SIZE_MAX
define so we don't have to see the double cast...


  static void
  f_count(char *arg)
  {
-   intmax_t res;
-
-   res = (intmax_t)get_num(arg);
-   if (res  0)
-   errx(1, count cannot be negative);
-   if (res == 0)
-   cpy_cnt = (uintmax_t)-1;

This is a special case.  See dd_in().  I think that eliminating this
special case will have the unintended effect of breaking count=0.


-   else
-   cpy_cnt = (uintmax_t)res;
+   cpy_cnt = get_num(arg);
  }

  static void
  f_files(char *arg)
  {
-

Don't eliminate these blank lines.. they are intentional per style(9):
  /* Insert an empty line if the function has no local variables. */


 files_cnt = get_num(arg);
 if (files_cnt  1)
-   errx(1, files must be between 1 and %jd, (uintmax_t)-1);
+   errx(1, files must be between 1 and %ju, (uintmax_t)-1);

Good catch.


  }

  static void
@@ -241,14 +226,10 @@
  static void
  f_ibs(char *arg)
  {
-   uintmax_t res;
-
 if (!(ddflags  C_BS)) {
-   res = get_num(arg);
-   if (res

Re: Inconsistent behavior with dd(1)

2014-08-18 Thread William Orr
Reply inline.

On 08/16/2014 10:34 AM, John-Mark Gurney wrote:
 Alan Somers wrote this message on Fri, Aug 15, 2014 at 10:42 -0600:
 On Thu, Aug 14, 2014 at 11:55 PM, William Orr w...@worrbase.com wrote:
 Hey,

 I found some inconsistent behavior with dd(1) when it comes to specifying 
 arguments in -CURRENT.

  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551616
 dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551617
 dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551615
 dd: count cannot be negative
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=-18446744073709551615
 1+0 records in
 1+0 records out
 512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
 dd: count cannot be negative

 ???

 Any chance someone has the time and could take a look? 
 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263

 Thanks,
 William Orr

 ???


 IMHO, this is a bug in strtouq(3), not in dd(1).  Why should it parse
 negative numbers at all, when there is stroq(3) for that purpose?  The
 standard is clear that it must, though.  Oddly enough, stroq would
 probably not accept -18446744073709551615, even though strtouq does.
 Specific comments on your patch below:



 Here???s the patch:

 Index: bin/dd/args.c
 ===
 --- bin/dd/args.c   (revision 267712)
 +++ bin/dd/args.c   (working copy)
 @@ -186,46 +186,31 @@
  static void
  f_bs(char *arg)
  {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, bs must be between 1 and %jd, 
 (intmax_t)SSIZE_MAX);
 -   in.dbsz = out.dbsz = (size_t)res;
 +   in.dbsz = out.dbsz = get_num(arg);
 +   if (in.dbsz  1 || out.dbsz  1)

 Why do you need to check both in and out?  Aren't they the same?
 Also, you eliminated the check for overflowing SSIZE_MAX.  That's not
 ok, because these values get passed to places that expect signed
 numbers, for example in dd.c:303.
 
 The type of dbsz is size_t, so really:
 
 +   errx(1, bs must be between 1 and %ju, (uintmax_t)-1);
 
 This should be SIZE_MAX, except there isn't a define for this?  So maybe
 the code really should be:
   (uintmax_t)(size_t)-1
 
 to get the correct value for SIZE_MAX...
 
 Otherwise on systems that uintmax_t is 32bits and size_t is 32bits,
 the error message will be wrong...

Yes, this should probably be SIZE_MAX rather than that cast. Same with
the others

 
  }

  static void
  f_cbs(char *arg)
  {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, cbs must be between 1 and %jd, 
 (intmax_t)SSIZE_MAX);
 -   cbsz = (size_t)res;
 +   cbsz = get_num(arg);
 +   if (cbsz  1)
 +   errx(1, cbs must be between 1 and %ju, (uintmax_t)-1);
  }

 Again, you eliminated the check for SSIZE_MAX, but cbsz must be signed.
 
 What do you mean by this?  cbsz is size_t which is unsigned...

I believe he's referring to this use of cbsz/in.dbsz/out.dbsz:

https://svnweb.freebsd.org/base/head/bin/dd/dd.c?revision=265698view=markup#l171

Really, this is more wrong since there is math inside of a malloc(3)
call without any overflow handling. By virtue of making this max out at
a ssize_t, it becomes more unlikely that you'll have overflow.

This math should probably be done ahead of time with proper overflow
handling. I'll include that in my next patch, if there's no objection.

I don't see any other reason why in.dbsz, out.dbsz or cbsz should be
signed, but it's very possible that I didn't look hard enough.

 Again, the cast above is wrong...  Maybe we should add a SIZE_MAX
 define so we don't have to see the double cast...
 
  static void
  f_count(char *arg)
  {
 -   intmax_t res;
 -
 -   res = (intmax_t)get_num(arg);
 -   if (res  0)
 -   errx(1, count cannot be negative);
 -   if (res == 0)
 -   cpy_cnt = (uintmax_t)-1;

 This is a special case.  See dd_in().  I think that eliminating this
 special case will have the unintended effect of breaking count=0.

 -   else
 -   cpy_cnt = (uintmax_t)res;
 +   cpy_cnt = get_num(arg);
  }

  static void
  f_files(char *arg)
  {
 -
 
 Don't eliminate these blank lines.. they are intentional per style(9):
  /* Insert an empty line if the function has no local variables. 
 */
 
 files_cnt = get_num(arg);
 if (files_cnt  1)
 -   errx(1, files must be between 1 and %jd, (uintmax_t)-1);
 +   errx(1, files must be between 1 and %ju, (uintmax_t)-1);

 Good catch.

  }

  static void
 @@ -241,14 +226,10 @@
  static void
  f_ibs(char *arg)
  {
 -   uintmax_t res;
 -
 if (!(ddflags  C_BS

Re: Inconsistent behavior with dd(1)

2014-08-16 Thread John-Mark Gurney
Alan Somers wrote this message on Fri, Aug 15, 2014 at 10:42 -0600:
 On Thu, Aug 14, 2014 at 11:55 PM, William Orr w...@worrbase.com wrote:
  Hey,
 
  I found some inconsistent behavior with dd(1) when it comes to specifying 
  arguments in -CURRENT.
 
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
  count=18446744073709551616
  dd: count: Result too large
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
  count=18446744073709551617
  dd: count: Result too large
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
  count=18446744073709551615
  dd: count cannot be negative
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
  count=-18446744073709551615
  1+0 records in
  1+0 records out
  512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
   [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
  dd: count cannot be negative
 
  ???
 
  Any chance someone has the time and could take a look? 
  https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263
 
  Thanks,
  William Orr
 
  ???
 
 
 IMHO, this is a bug in strtouq(3), not in dd(1).  Why should it parse
 negative numbers at all, when there is stroq(3) for that purpose?  The
 standard is clear that it must, though.  Oddly enough, stroq would
 probably not accept -18446744073709551615, even though strtouq does.
 Specific comments on your patch below:
 
 
 
  Here???s the patch:
 
  Index: bin/dd/args.c
  ===
  --- bin/dd/args.c   (revision 267712)
  +++ bin/dd/args.c   (working copy)
  @@ -186,46 +186,31 @@
   static void
   f_bs(char *arg)
   {
  -   uintmax_t res;
  -
  -   res = get_num(arg);
  -   if (res  1 || res  SSIZE_MAX)
  -   errx(1, bs must be between 1 and %jd, 
  (intmax_t)SSIZE_MAX);
  -   in.dbsz = out.dbsz = (size_t)res;
  +   in.dbsz = out.dbsz = get_num(arg);
  +   if (in.dbsz  1 || out.dbsz  1)
 
 Why do you need to check both in and out?  Aren't they the same?
 Also, you eliminated the check for overflowing SSIZE_MAX.  That's not
 ok, because these values get passed to places that expect signed
 numbers, for example in dd.c:303.

The type of dbsz is size_t, so really:

  +   errx(1, bs must be between 1 and %ju, (uintmax_t)-1);

This should be SIZE_MAX, except there isn't a define for this?  So maybe
the code really should be:
  (uintmax_t)(size_t)-1

to get the correct value for SIZE_MAX...

Otherwise on systems that uintmax_t is 32bits and size_t is 32bits,
the error message will be wrong...

   }
 
   static void
   f_cbs(char *arg)
   {
  -   uintmax_t res;
  -
  -   res = get_num(arg);
  -   if (res  1 || res  SSIZE_MAX)
  -   errx(1, cbs must be between 1 and %jd, 
  (intmax_t)SSIZE_MAX);
  -   cbsz = (size_t)res;
  +   cbsz = get_num(arg);
  +   if (cbsz  1)
  +   errx(1, cbs must be between 1 and %ju, (uintmax_t)-1);
   }
 
 Again, you eliminated the check for SSIZE_MAX, but cbsz must be signed.

What do you mean by this?  cbsz is size_t which is unsigned...

Again, the cast above is wrong...  Maybe we should add a SIZE_MAX
define so we don't have to see the double cast...

   static void
   f_count(char *arg)
   {
  -   intmax_t res;
  -
  -   res = (intmax_t)get_num(arg);
  -   if (res  0)
  -   errx(1, count cannot be negative);
  -   if (res == 0)
  -   cpy_cnt = (uintmax_t)-1;
 
 This is a special case.  See dd_in().  I think that eliminating this
 special case will have the unintended effect of breaking count=0.
 
  -   else
  -   cpy_cnt = (uintmax_t)res;
  +   cpy_cnt = get_num(arg);
   }
 
   static void
   f_files(char *arg)
   {
  -

Don't eliminate these blank lines.. they are intentional per style(9):
 /* Insert an empty line if the function has no local variables. */

  files_cnt = get_num(arg);
  if (files_cnt  1)
  -   errx(1, files must be between 1 and %jd, (uintmax_t)-1);
  +   errx(1, files must be between 1 and %ju, (uintmax_t)-1);
 
 Good catch.
 
   }
 
   static void
  @@ -241,14 +226,10 @@
   static void
   f_ibs(char *arg)
   {
  -   uintmax_t res;
  -
  if (!(ddflags  C_BS)) {
  -   res = get_num(arg);
  -   if (res  1 || res  SSIZE_MAX)
  -   errx(1, ibs must be between 1 and %jd,
  -   (intmax_t)SSIZE_MAX);
  -   in.dbsz = (size_t)res;
  +   in.dbsz = get_num(arg);
  +   if (in.dbsz  1)
  +   errx(1, ibs must be between 1 and %ju, 
  (uintmax_t)-1);
 
 Again, you eliminated the check for SSIZE_MAX, but dbsz must be signed.

If dbsz must be signed, we should change it's definition to ssize_t
instead of size_t...  Can you point to the line that says this?

In investigating this, it looks like we may have a bug in ftruncate

Inconsistent behavior with dd(1)

2014-08-15 Thread William Orr
Hey,

I found some inconsistent behavior with dd(1) when it comes to specifying 
arguments in -CURRENT.

 [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551616
dd: count: Result too large
 [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551617
dd: count: Result too large
 [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=18446744073709551615
dd: count cannot be negative
 [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
count=-18446744073709551615
1+0 records in
1+0 records out
512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
 [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
dd: count cannot be negative

—

Any chance someone has the time and could take a look? 
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263

Thanks,
William Orr

—

Here’s the patch:

Index: bin/dd/args.c
===
--- bin/dd/args.c   (revision 267712)
+++ bin/dd/args.c   (working copy)
@@ -186,46 +186,31 @@
 static void
 f_bs(char *arg)
 {
-   uintmax_t res;
-
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, bs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
-   in.dbsz = out.dbsz = (size_t)res;
+   in.dbsz = out.dbsz = get_num(arg);
+   if (in.dbsz  1 || out.dbsz  1)
+   errx(1, bs must be between 1 and %ju, (uintmax_t)-1);
 }
 
 static void
 f_cbs(char *arg)
 {
-   uintmax_t res;
-
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, cbs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
-   cbsz = (size_t)res;
+   cbsz = get_num(arg);
+   if (cbsz  1)
+   errx(1, cbs must be between 1 and %ju, (uintmax_t)-1);
 }
 
 static void
 f_count(char *arg)
 {
-   intmax_t res;
-
-   res = (intmax_t)get_num(arg);
-   if (res  0)
-   errx(1, count cannot be negative);
-   if (res == 0)
-   cpy_cnt = (uintmax_t)-1;
-   else
-   cpy_cnt = (uintmax_t)res;
+   cpy_cnt = get_num(arg);
 }
 
 static void
 f_files(char *arg)
 {
-
files_cnt = get_num(arg);
if (files_cnt  1)
-   errx(1, files must be between 1 and %jd, (uintmax_t)-1);
+   errx(1, files must be between 1 and %ju, (uintmax_t)-1);
 }
 
 static void
@@ -241,14 +226,10 @@
 static void
 f_ibs(char *arg)
 {
-   uintmax_t res;
-
if (!(ddflags  C_BS)) {
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, ibs must be between 1 and %jd,
-   (intmax_t)SSIZE_MAX);
-   in.dbsz = (size_t)res;
+   in.dbsz = get_num(arg);
+   if (in.dbsz  1)
+   errx(1, ibs must be between 1 and %ju, (uintmax_t)-1);
}
 }
 
@@ -262,14 +243,10 @@
 static void
 f_obs(char *arg)
 {
-   uintmax_t res;
-
if (!(ddflags  C_BS)) {
-   res = get_num(arg);
-   if (res  1 || res  SSIZE_MAX)
-   errx(1, obs must be between 1 and %jd,
-   (intmax_t)SSIZE_MAX);
-   out.dbsz = (size_t)res;
+   out.dbsz = get_num(arg);
+   if (out.dbsz  1)
+   errx(1, obs must be between 1 and %ju, (uintmax_t)-1);
}
 }
 
@@ -378,11 +355,14 @@
uintmax_t num, mult, prevnum;
char *expr;
 
+   if (val[0] == '-')
+   errx(1, %s: cannot be negative, oper);
+
errno = 0;
num = strtouq(val, expr, 0);
if (errno != 0) /* Overflow or underflow. */
err(1, %s, oper);
-   
+
if (expr == val)/* No valid digits. */
errx(1, %s: illegal numeric value, oper);
 
Index: bin/dd/dd.c
===
--- bin/dd/dd.c (revision 267712)
+++ bin/dd/dd.c (working copy)
@@ -284,8 +284,6 @@
 
for (;;) {
switch (cpy_cnt) {
-   case -1:/* count=0 was specified */
-   return;
case 0:
break;
default:




signature.asc
Description: Message signed with OpenPGP using GPGMail


Re: Inconsistent behavior with dd(1)

2014-08-15 Thread Alan Somers
On Thu, Aug 14, 2014 at 11:55 PM, William Orr w...@worrbase.com wrote:
 Hey,

 I found some inconsistent behavior with dd(1) when it comes to specifying 
 arguments in -CURRENT.

  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551616
 dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551617
 dd: count: Result too large
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=18446744073709551615
 dd: count cannot be negative
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null 
 count=-18446744073709551615
 1+0 records in
 1+0 records out
 512 bytes transferred in 0.000373 secs (1373071 bytes/sec)
  [ worr on terra ] ( ~ ) % dd if=/dev/zero of=/dev/null count=-1
 dd: count cannot be negative

 —

 Any chance someone has the time and could take a look? 
 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=191263

 Thanks,
 William Orr

 —


IMHO, this is a bug in strtouq(3), not in dd(1).  Why should it parse
negative numbers at all, when there is stroq(3) for that purpose?  The
standard is clear that it must, though.  Oddly enough, stroq would
probably not accept -18446744073709551615, even though strtouq does.
Specific comments on your patch below:



 Here’s the patch:

 Index: bin/dd/args.c
 ===
 --- bin/dd/args.c   (revision 267712)
 +++ bin/dd/args.c   (working copy)
 @@ -186,46 +186,31 @@
  static void
  f_bs(char *arg)
  {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, bs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
 -   in.dbsz = out.dbsz = (size_t)res;
 +   in.dbsz = out.dbsz = get_num(arg);
 +   if (in.dbsz  1 || out.dbsz  1)

Why do you need to check both in and out?  Aren't they the same?
Also, you eliminated the check for overflowing SSIZE_MAX.  That's not
ok, because these values get passed to places that expect signed
numbers, for example in dd.c:303.

 +   errx(1, bs must be between 1 and %ju, (uintmax_t)-1);
  }

  static void
  f_cbs(char *arg)
  {
 -   uintmax_t res;
 -
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, cbs must be between 1 and %jd, (intmax_t)SSIZE_MAX);
 -   cbsz = (size_t)res;
 +   cbsz = get_num(arg);
 +   if (cbsz  1)
 +   errx(1, cbs must be between 1 and %ju, (uintmax_t)-1);
  }

Again, you eliminated the check for SSIZE_MAX, but cbsz must be signed.


  static void
  f_count(char *arg)
  {
 -   intmax_t res;
 -
 -   res = (intmax_t)get_num(arg);
 -   if (res  0)
 -   errx(1, count cannot be negative);
 -   if (res == 0)
 -   cpy_cnt = (uintmax_t)-1;

This is a special case.  See dd_in().  I think that eliminating this
special case will have the unintended effect of breaking count=0.

 -   else
 -   cpy_cnt = (uintmax_t)res;
 +   cpy_cnt = get_num(arg);
  }

  static void
  f_files(char *arg)
  {
 -
 files_cnt = get_num(arg);
 if (files_cnt  1)
 -   errx(1, files must be between 1 and %jd, (uintmax_t)-1);
 +   errx(1, files must be between 1 and %ju, (uintmax_t)-1);

Good catch.

  }

  static void
 @@ -241,14 +226,10 @@
  static void
  f_ibs(char *arg)
  {
 -   uintmax_t res;
 -
 if (!(ddflags  C_BS)) {
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, ibs must be between 1 and %jd,
 -   (intmax_t)SSIZE_MAX);
 -   in.dbsz = (size_t)res;
 +   in.dbsz = get_num(arg);
 +   if (in.dbsz  1)
 +   errx(1, ibs must be between 1 and %ju, 
 (uintmax_t)-1);

Again, you eliminated the check for SSIZE_MAX, but dbsz must be signed.

 }
  }

 @@ -262,14 +243,10 @@
  static void
  f_obs(char *arg)
  {
 -   uintmax_t res;
 -
 if (!(ddflags  C_BS)) {
 -   res = get_num(arg);
 -   if (res  1 || res  SSIZE_MAX)
 -   errx(1, obs must be between 1 and %jd,
 -   (intmax_t)SSIZE_MAX);
 -   out.dbsz = (size_t)res;
 +   out.dbsz = get_num(arg);
 +   if (out.dbsz  1)
 +   errx(1, obs must be between 1 and %ju, 
 (uintmax_t)-1);
 }
  }

Again, you eliminated the check for SSIZE_MAX, but dbsz must be signed.


 @@ -378,11 +355,14 @@
 uintmax_t num, mult, prevnum;
 char *expr;

 +   if (val[0] == '-')
 +   errx(1, %s: cannot be negative, oper);
 +

In general, I like this part of the diff.  Every user of get_num
checks for negative values, so why not move the check into get_num
itself?  But you changed it from a numeric check to a text check, and
writing text parsers makes me nervous.  I can't see any problems,
though