Re: echo(1): check for stdio errors

2022-08-11 Thread Stuart Henderson
On 2022/08/10 19:37, Scott Cheloha wrote:
> On Thu, Aug 11, 2022 at 02:22:08AM +0200, Jeremie Courreges-Anglas wrote:
> > On Wed, Aug 10 2022, Scott Cheloha  wrote:
> > > [...]
> > >
> > > 1. Our ksh(1) already checks for stdout errors in the echo builtin.
> > 
> > So do any of the scripts in our source tree use /bin/echo for whatever
> > reason?  If so, could one of these scripts be broken if /bin/echo
> > started to report an error?  Shouldn't those scripts be reviewed?
> 
> I didn't look.
> 
> There are... hundreds files that look like shell scripts in src.
> 
> $ cd /usr/src
> $ find . -exec egrep -l '\#.*\!.*sh' > ~/src-shell-script-paths
> $ wc -l ~/src-shell-script-paths
> 1118 /home/ssc/src-shell-script-paths
> 
> A lot of them are in regress/.
> 
> I guess I better start looking.

The list is much smaller if you just search the tree for /bin/echo



Re: echo(1): check for stdio errors

2022-08-10 Thread Scott Cheloha
On Thu, Aug 11, 2022 at 02:22:08AM +0200, Jeremie Courreges-Anglas wrote:
> On Wed, Aug 10 2022, Scott Cheloha  wrote:
> > [...]
> >
> > 1. Our ksh(1) already checks for stdout errors in the echo builtin.
> 
> So do any of the scripts in our source tree use /bin/echo for whatever
> reason?  If so, could one of these scripts be broken if /bin/echo
> started to report an error?  Shouldn't those scripts be reviewed?

I didn't look.

There are... hundreds files that look like shell scripts in src.

$ cd /usr/src
$ find . -exec egrep -l '\#.*\!.*sh' > ~/src-shell-script-paths
$ wc -l ~/src-shell-script-paths
1118 /home/ssc/src-shell-script-paths

A lot of them are in regress/.

I guess I better start looking.



Re: echo(1): check for stdio errors

2022-08-10 Thread Jeremie Courreges-Anglas
On Wed, Aug 10 2022, Scott Cheloha  wrote:
> On Wed, Aug 10, 2022 at 02:23:08PM -0600, Theo de Raadt wrote:
>> Scott Cheloha  wrote:
>> 
>> > On Wed, Aug 10, 2022 at 12:26:17PM -0600, Theo de Raadt wrote:
>> > > Scott Cheloha  wrote:
>> > > 
>> > > > We're sorta-kinda circling around adding the missing (?) stdio error
>> > > > checking to other utilities in bin/ and usr.bin/, no?  I want to be
>> > > > sure I understand how to do the next patch, because if we do that it
>> > > > will probably be a bunch of programs all at once.
>> > > 
>> > > This specific program has not checked for this condition since at least
>> > > 2 AT UNIX.
>> > > 
>> > > Your change does not just add a new warning.  It adds a new exit code
>> > > condition.
>> > > 
>> > > Some scripts using echo, which accepted the condition because echo would
>> > > exit 0 and not check for this condition, will now see this exit 1.  Some
>> > > scripts will abort, because they use "set -o errexit" or similar.
>> > > 
>> > > You are changing the exit code for a command which is used a lot.
>> > > 
>> > > POSIX does not require or specify exit 1 for this condition.  If you
>> > > disagree, please show where it says so.
>> > 
>> > It's the usual thing.  >0 if "an error occurred".
>> 
>> The 40 year old code base says otherwise.
>> 
>> > Here is my thinking:
>> > 
>> >echo(1) has ONE job: print the arguments given.
>> > 
>> > If it fails to print those arguments, shouldn't we signal that to the
>> > program that forked echo(1)?
>> 
>> Only if you validate all callers can handle this change in behaviour.
>> 
>> > How is echo(1) supposed to differentiate between a write(2) that is
>> > allowed to fail, e.g. a diagnostic printout from fw_update to the
>> > user's stderr, and one that is not allowed to fail?
>> 
>> Perhaps it is not supposed to validate this problem  in 2022, because it
>> didn't validate it for 40 years.
>> 
>> > Consider this scenario:
>> > 
>> > 1.  A shell script uses echo(1) to write something to a file.
>> > 
>> >/bin/echo foo.dat >> /var/workerd/data-processing-list
>> > 
>> > 2.  The bytes don't arrive on disk because the file system is full.
>> > 
>> > 3.  The shell script succeeds because echo(1) can't fail, even if
>> > it fails to do what it was told to do.
>> > 
>> > Isn't that bad?
>> > 
>> > And it isn't necessarily true that some other thing will fail later
>> > and the whole interlocking system will fail.  ENOSPC is a transient
>> > condition.  One write(2) can fail and the next write(2) can succeed.
>> 
>> Yet, for 40 years noone complained.
>> 
>> Consider the situation you break and change the behaviour of 1000's of
>> shell scripts, and haven'd lifted your finger once to review all the
>> shell scripts that call echo.
>> 
>> Have you even compared this behaviour to the echo built-ins in all
>> the shells?
>
> I assume what you mean to say is, roughly:
>
>   Gee, this seems risky.
>
>   What do other echo implementations do?
>
> 1. Our ksh(1) already checks for stdout errors in the echo builtin.

So do any of the scripts in our source tree use /bin/echo for whatever
reason?  If so, could one of these scripts be broken if /bin/echo
started to report an error?  Shouldn't those scripts be reviewed?

/bin/echo may look unimportant but I think it deserves the same
treatment as more complex tools that you may modify in a similar way.
While I agree that adding more error reporting would be good, it
sounds fair that it should be done while caring for the actual errors. ;)

[...]

-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE



Re: echo(1): check for stdio errors

2022-08-10 Thread Scott Cheloha
On Wed, Aug 10, 2022 at 02:23:08PM -0600, Theo de Raadt wrote:
> Scott Cheloha  wrote:
> 
> > On Wed, Aug 10, 2022 at 12:26:17PM -0600, Theo de Raadt wrote:
> > > Scott Cheloha  wrote:
> > > 
> > > > We're sorta-kinda circling around adding the missing (?) stdio error
> > > > checking to other utilities in bin/ and usr.bin/, no?  I want to be
> > > > sure I understand how to do the next patch, because if we do that it
> > > > will probably be a bunch of programs all at once.
> > > 
> > > This specific program has not checked for this condition since at least
> > > 2 AT UNIX.
> > > 
> > > Your change does not just add a new warning.  It adds a new exit code
> > > condition.
> > > 
> > > Some scripts using echo, which accepted the condition because echo would
> > > exit 0 and not check for this condition, will now see this exit 1.  Some
> > > scripts will abort, because they use "set -o errexit" or similar.
> > > 
> > > You are changing the exit code for a command which is used a lot.
> > > 
> > > POSIX does not require or specify exit 1 for this condition.  If you
> > > disagree, please show where it says so.
> > 
> > It's the usual thing.  >0 if "an error occurred".
> 
> The 40 year old code base says otherwise.
> 
> > Here is my thinking:
> > 
> > echo(1) has ONE job: print the arguments given.
> > 
> > If it fails to print those arguments, shouldn't we signal that to the
> > program that forked echo(1)?
> 
> Only if you validate all callers can handle this change in behaviour.
> 
> > How is echo(1) supposed to differentiate between a write(2) that is
> > allowed to fail, e.g. a diagnostic printout from fw_update to the
> > user's stderr, and one that is not allowed to fail?
> 
> Perhaps it is not supposed to validate this problem  in 2022, because it
> didn't validate it for 40 years.
> 
> > Consider this scenario:
> > 
> > 1.  A shell script uses echo(1) to write something to a file.
> > 
> > /bin/echo foo.dat >> /var/workerd/data-processing-list
> > 
> > 2.  The bytes don't arrive on disk because the file system is full.
> > 
> > 3.  The shell script succeeds because echo(1) can't fail, even if
> > it fails to do what it was told to do.
> > 
> > Isn't that bad?
> > 
> > And it isn't necessarily true that some other thing will fail later
> > and the whole interlocking system will fail.  ENOSPC is a transient
> > condition.  One write(2) can fail and the next write(2) can succeed.
> 
> Yet, for 40 years noone complained.
> 
> Consider the situation you break and change the behaviour of 1000's of
> shell scripts, and haven'd lifted your finger once to review all the
> shell scripts that call echo.
> 
> Have you even compared this behaviour to the echo built-ins in all
> the shells?

I assume what you mean to say is, roughly:

Gee, this seems risky.

What do other echo implementations do?

1. Our ksh(1) already checks for stdout errors in the echo builtin.

2. FreeBSD's /bin/echo has checked for writev(2) errors in /bin/echo
   since 2003:

https://cgit.freebsd.org/src/commit/bin/echo/echo.c?id=91b7d6dc5871f532b1a86ee76389a9bc348bdf58

3. NetBSD's /bin/echo has checked for stdout errors with ferror(3)
   since 2008:

http://cvsweb.netbsd.org/bsdweb.cgi/src/bin/echo/echo.c?rev=1.18=text/x-cvsweb-markup_with_tag=MAIN

4. NetBSD's /bin/sh echo builtin has checked for write errors since
   2008:

http://cvsweb.netbsd.org/bsdweb.cgi/src/bin/sh/bltin/echo.c?rev=1.14=text/x-cvsweb-markup_with_tag=MAIN

5. OpenSolaris has checked for fflush(3) errors in /usr/bin/echo since
   2005 (OpenSolaris launch):

https://github.com/illumos/illumos-gate/blob/7c478bd95313f5f23a4c958a745db2134aa03244/usr/src/cmd/echo/echo.c#L144

6. Looking forward, illumos inherited and retains the behavior in
   their /usr/bin/echo.

7. Extrapolating backward, we can assume Solaris did that checking in
   /usr/bin/echo prior to 2005.

8. GNU Coreutils echo has checked for fflush(3) and fclose(3) errors on
   stdout since 2000:

https://git.savannah.gnu.org/cgit/coreutils.git/commit/src/echo.c?id=d3683509b3953beb014e540f6d6194658ede1dea

   They use close_stdout() in an atexit(3) hook.  close_stdout() is a
   convenience function provided by gnulib since 1998 that does what I
   described:

https://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=commitdiff;h=23928550db5d400f27fa67de29c738ca324a31ea;hp=f76477e515b36a1e10f7734aac3c5478ccf75989

   Maybe of note is that they do this atexit(3) stdout flush/close
   error checking for many of their utilities.

9. The GNU Bash echo builtin has checked for write errors since v2.04,
   in 2000:

https://git.savannah.gnu.org/cgit/bash.git/commit/builtins/echo.def?id=bb70624e964126b7ac4ff085ba163a9c35ffa18f

   They even noted it in the CHANGES file for that release:

https://git.savannah.gnu.org/cgit/bash.git/commit/CHANGES?id=bb70624e964126b7ac4ff085ba163a9c35ffa18f

--

I don't think that we are first movers in this case.



Re: echo(1): check for stdio errors

2022-08-10 Thread Jeremie Courreges-Anglas
On Wed, Aug 10 2022, "Theo de Raadt"  wrote:
> Scott Cheloha  wrote:
>
>> We're sorta-kinda circling around adding the missing (?) stdio error
>> checking to other utilities in bin/ and usr.bin/, no?  I want to be
>> sure I understand how to do the next patch, because if we do that it
>> will probably be a bunch of programs all at once.
>
>
> This specific program has not checked for this condition since at least
> 2 AT UNIX.
>
> Your change does not just add a new warning.  It adds a new exit code
> condition.
>
> Some scripts using echo, which accepted the condition because echo would
> exit 0 and not check for this condition, will now see this exit 1.  Some
> scripts will abort, because they use "set -o errexit" or similar.

About this, and only related to echo: the builtin version in ksh(1) does
report an error.  I have just tried the mentioned ENOSPC case with

  exec 3>/tmp/o/file; echo foobar >&3; echo $?

and /tmp/o being a full MFS filesystem.  So shell scripts run with
ksh(1) should already handle echo - the builtin - failures.
I think it makes sense for the echo(1) executable to behave like the
shell builtin.

For the other programs, well, I don't have a strong feeling.  I'll just
note that adding stdio error reporting has been done in some other
projects.

-- 
jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE



Re: echo(1): check for stdio errors

2022-08-10 Thread Theo de Raadt
Scott Cheloha  wrote:

> On Wed, Aug 10, 2022 at 12:26:17PM -0600, Theo de Raadt wrote:
> > Scott Cheloha  wrote:
> > 
> > > We're sorta-kinda circling around adding the missing (?) stdio error
> > > checking to other utilities in bin/ and usr.bin/, no?  I want to be
> > > sure I understand how to do the next patch, because if we do that it
> > > will probably be a bunch of programs all at once.
> > 
> > This specific program has not checked for this condition since at least
> > 2 AT UNIX.
> > 
> > Your change does not just add a new warning.  It adds a new exit code
> > condition.
> > 
> > Some scripts using echo, which accepted the condition because echo would
> > exit 0 and not check for this condition, will now see this exit 1.  Some
> > scripts will abort, because they use "set -o errexit" or similar.
> > 
> > You are changing the exit code for a command which is used a lot.
> > 
> > POSIX does not require or specify exit 1 for this condition.  If you
> > disagree, please show where it says so.
> 
> It's the usual thing.  >0 if "an error occurred".

The 40 year old code base says otherwise.

> Here is my thinking:
> 
>   echo(1) has ONE job: print the arguments given.
> 
> If it fails to print those arguments, shouldn't we signal that to the
> program that forked echo(1)?

Only if you validate all callers can handle this change in behaviour.

> How is echo(1) supposed to differentiate between a write(2) that is
> allowed to fail, e.g. a diagnostic printout from fw_update to the
> user's stderr, and one that is not allowed to fail?

Perhaps it is not supposed to validate this problem  in 2022, because it
didn't validate it for 40 years.

> Consider this scenario:
> 
> 1.  A shell script uses echo(1) to write something to a file.
> 
>   /bin/echo foo.dat >> /var/workerd/data-processing-list
> 
> 2.  The bytes don't arrive on disk because the file system is full.
> 
> 3.  The shell script succeeds because echo(1) can't fail, even if
> it fails to do what it was told to do.
> 
> Isn't that bad?
> 
> And it isn't necessarily true that some other thing will fail later
> and the whole interlocking system will fail.  ENOSPC is a transient
> condition.  One write(2) can fail and the next write(2) can succeed.

Yet, for 40 years noone complained.


Consider the situation you break and change the behaviour of 1000's of
shell scripts, and haven'd lifted your finger once to review all the
shell scripts that call echo.

Have you even compared this behaviour to the echo built-ins in all
the shells?

Isn't that bad?


Your proposal is dead in the water until you assess *the code that
calls echo*.  If you find one script that breaks, the proposal is
even more dead.



Re: echo(1): check for stdio errors

2022-08-10 Thread Scott Cheloha
On Wed, Aug 10, 2022 at 12:26:17PM -0600, Theo de Raadt wrote:
> Scott Cheloha  wrote:
> 
> > We're sorta-kinda circling around adding the missing (?) stdio error
> > checking to other utilities in bin/ and usr.bin/, no?  I want to be
> > sure I understand how to do the next patch, because if we do that it
> > will probably be a bunch of programs all at once.
> 
> This specific program has not checked for this condition since at least
> 2 AT UNIX.
> 
> Your change does not just add a new warning.  It adds a new exit code
> condition.
> 
> Some scripts using echo, which accepted the condition because echo would
> exit 0 and not check for this condition, will now see this exit 1.  Some
> scripts will abort, because they use "set -o errexit" or similar.
> 
> You are changing the exit code for a command which is used a lot.
> 
> POSIX does not require or specify exit 1 for this condition.  If you
> disagree, please show where it says so.

It's the usual thing.  >0 if "an error occurred".

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html

EXIT STATUS

The following exit values shall be returned:

 0
Successful completion.
>0
An error occurred.

CONSEQUENCES OF ERRORS

Default.

> So my question is:  What will be broken by this change?
> 
> Nothing isn't an answer.  I can write a 5 line shell script that will
> observe the change in behaviour.  Many large shell scripts could break
> from this.  I am thinking of fw_update and the installer, but it could
> also be a problem in Makefiles.

Here is my thinking:

echo(1) has ONE job: print the arguments given.

If it fails to print those arguments, shouldn't we signal that to the
program that forked echo(1)?

How is echo(1) supposed to differentiate between a write(2) that is
allowed to fail, e.g. a diagnostic printout from fw_update to the
user's stderr, and one that is not allowed to fail?

> > I want to be sure I understand how to do the next patch, because if we
> > do that it will probably be a bunch of programs all at once.
> 
> If you cannot speak to the exit code command changing for this one
> simple program, I think there is no case for adding to to hundreds of
> other programs.  Unless POSIX specifies the requirement, I'd like to see
> some justification.
> 
> There will always be situations that UNIX didn't anticipate or handle,
> and then POSIX failed to specify.  Such things are now unhandled, probably
> forever, and have become defacto standards.
> 
> On the balance, is your diff improving on some dangerous problem, or is
> it introducing a vast number of dangerous new risks which cannot be
> identified (and which would require an audit of every known script
> calling echo).  Has such an audit been started?

Consider this scenario:

1.  A shell script uses echo(1) to write something to a file.

/bin/echo foo.dat >> /var/workerd/data-processing-list

2.  The bytes don't arrive on disk because the file system is full.

3.  The shell script succeeds because echo(1) can't fail, even if
it fails to do what it was told to do.

Isn't that bad?

And it isn't necessarily true that some other thing will fail later
and the whole interlocking system will fail.  ENOSPC is a transient
condition.  One write(2) can fail and the next write(2) can succeed.



Re: echo(1): check for stdio errors

2022-08-10 Thread Theo de Raadt
Scott Cheloha  wrote:

> We're sorta-kinda circling around adding the missing (?) stdio error
> checking to other utilities in bin/ and usr.bin/, no?  I want to be
> sure I understand how to do the next patch, because if we do that it
> will probably be a bunch of programs all at once.


This specific program has not checked for this condition since at least
2 AT UNIX.

Your change does not just add a new warning.  It adds a new exit code
condition.

Some scripts using echo, which accepted the condition because echo would
exit 0 and not check for this condition, will now see this exit 1.  Some
scripts will abort, because they use "set -o errexit" or similar.

You are changing the exit code for a command which is used a lot.

POSIX does not require or specify exit 1 for this condition.  If you
disagree, please show where it says so.

So my question is:  What will be broken by this change?

Nothing isn't an answer.  I can write a 5 line shell script that will
observe the change in behaviour.  Many large shell scripts could break
from this.  I am thinking of fw_update and the installer, but it could
also be a problem in Makefiles.

> I want to be sure I understand how to do the next patch, because if we
> do that it will probably be a bunch of programs all at once.

If you cannot speak to the exit code command changing for this one
simple program, I think there is no case for adding to to hundreds of
other programs.  Unless POSIX specifies the requirement, I'd like to see
some justification.

There will always be situations that UNIX didn't anticipate or handle,
and then POSIX failed to specify.  Such things are now unhandled, probably
forever, and have become defacto standards.

On the balance, is your diff improving on some dangerous problem, or is
it introducing a vast number of dangerous new risks which cannot be
identified (and which would require an audit of every known script
calling echo).  Has such an audit been started?







Re: echo(1): check for stdio errors

2022-08-10 Thread Scott Cheloha
On Sat, Jul 30, 2022 at 05:23:37PM -0600, Todd C. Miller wrote:
> On Sat, 30 Jul 2022 18:19:02 -0500, Scott Cheloha wrote:
> 
> > Bump.  The standard's error cases for fflush(3) are identical to those
> > for fclose(3):
> >
> > https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html
> > https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html
> >
> > Is the fact that our fclose(3) can succeed even if the error flag is
> > set a bug?
> 
> As far as I can tell, neither fflush() nor fclose() check the status
> of the error flag, though they may set it of course.  That is why
> I was suggesting an explicit ferror() call at the end.

I'm sorry, I'm having a dumb moment, I don't quite understand what
you're looking for.

Please tweak my patch so it's the way you want it, with the ferror(3)
call in the right spot.

We're sorta-kinda circling around adding the missing (?) stdio error
checking to other utilities in bin/ and usr.bin/, no?  I want to be
sure I understand how to do the next patch, because if we do that it
will probably be a bunch of programs all at once.

Index: echo.c
===
RCS file: /cvs/src/bin/echo/echo.c,v
retrieving revision 1.10
diff -u -p -r1.10 echo.c
--- echo.c  9 Oct 2015 01:37:06 -   1.10
+++ echo.c  10 Aug 2022 18:00:12 -
@@ -53,12 +53,15 @@ main(int argc, char *argv[])
nflag = 0;
 
while (*argv) {
-   (void)fputs(*argv, stdout);
-   if (*++argv)
-   putchar(' ');
+   if (fputs(*argv, stdout) == EOF)
+   err(1, "stdout");
+   if (*++argv && putchar(' ') == EOF)
+   err(1, "stdout");
}
-   if (!nflag)
-   putchar('\n');
+   if (!nflag && putchar('\n') == EOF)
+   err(1, "stdout");
+   if (fflush(stdout) == EOF || ferror(stdout) || fclose(stdout) == EOF)
+   err(1, "stdout");
 
return 0;
 }



Re: echo(1): check for stdio errors

2022-07-30 Thread Todd C . Miller
On Sat, 30 Jul 2022 18:19:02 -0500, Scott Cheloha wrote:

> Bump.  The standard's error cases for fflush(3) are identical to those
> for fclose(3):
>
> https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html
> https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html
>
> Is the fact that our fclose(3) can succeed even if the error flag is
> set a bug?

As far as I can tell, neither fflush() nor fclose() check the status
of the error flag, though they may set it of course.  That is why
I was suggesting an explicit ferror() call at the end.

 - todd



Re: echo(1): check for stdio errors

2022-07-30 Thread Scott Cheloha
On Mon, Jul 11, 2022 at 01:27:23PM -0500, Scott Cheloha wrote:
> On Mon, Jul 11, 2022 at 08:31:04AM -0600, Todd C. Miller wrote:
> > On Sun, 10 Jul 2022 20:58:35 -0900, Philip Guenther wrote:
> > 
> > > Three thoughts:
> > > 1) Since stdio errors are sticky, is there any real advantage to checking
> > > each call instead of just checking the final fclose()?
> 
> My thinking was that we have no idea how many arguments we're going to
> print, so we may as well fail as soon as possible.
> 
> Maybe in more complex programs there would be a code-length or
> complexity-reducing upside to deferring the ferror(3) check until,
> say, the end of a subroutine or something.
> 
> > > [...]
> > 
> > Will that really catch all errors?  From what I can tell, fclose(3)
> > can succeed even if the error flag was set.  The pattern I prefer
> > is to use a final fflush(3) followed by a call to ferror(3) before
> > the fclose(3).
> 
> [...]

Bump.  The standard's error cases for fflush(3) are identical to those
for fclose(3):

https://pubs.opengroup.org/onlinepubs/9699919799/functions/fflush.html
https://pubs.opengroup.org/onlinepubs/9699919799/functions/fclose.html

Is the fact that our fclose(3) can succeed even if the error flag is
set a bug?

Also, can I go ahead with this?  With this patch, echo(1) fails if we
(for example) try to write to a full file system.  So we are certainly
catching more stdio failures:

$ /bin/echo test > /tmp/myfile

/tmp: write failed, file system is full
$ echo $?
0

$ obj/echo test > /tmp/myfile

/tmp: write failed, file system is full
echo: stdout: No space left on device
$ echo $?
1

Progress!  Note that the shell builtin already fails in this case:

$ type echo
echo is a shell builtin
$ echo test > /tmp/myfile

/tmp: write failed, file system is full
jetsam$ echo $?
1

Index: echo.c
===
RCS file: /cvs/src/bin/echo/echo.c,v
retrieving revision 1.10
diff -u -p -r1.10 echo.c
--- echo.c  9 Oct 2015 01:37:06 -   1.10
+++ echo.c  30 Jul 2022 23:10:24 -
@@ -53,12 +53,15 @@ main(int argc, char *argv[])
nflag = 0;
 
while (*argv) {
-   (void)fputs(*argv, stdout);
-   if (*++argv)
-   putchar(' ');
+   if (fputs(*argv, stdout) == EOF)
+   err(1, "stdout");
+   if (*++argv && putchar(' ') == EOF)
+   err(1, "stdout");
}
-   if (!nflag)
-   putchar('\n');
+   if (!nflag && putchar('\n') == EOF)
+   err(1, "stdout");
+   if (fflush(stdout) == EOF || fclose(stdout) == EOF)
+   err(1, "stdout");
 
return 0;
 }



Re: echo(1): check for stdio errors

2022-07-11 Thread Scott Cheloha
On Mon, Jul 11, 2022 at 08:31:04AM -0600, Todd C. Miller wrote:
> On Sun, 10 Jul 2022 20:58:35 -0900, Philip Guenther wrote:
> 
> > Three thoughts:
> > 1) Since stdio errors are sticky, is there any real advantage to checking
> > each call instead of just checking the final fclose()?

My thinking was that we have no idea how many arguments we're going to
print, so we may as well fail as soon as possible.

Maybe in more complex programs there would be a code-length or
complexity-reducing upside to deferring the ferror(3) check until,
say, the end of a subroutine or something.

> > [...]
> 
> Will that really catch all errors?  From what I can tell, fclose(3)
> can succeed even if the error flag was set.  The pattern I prefer
> is to use a final fflush(3) followed by a call to ferror(3) before
> the fclose(3).

That's weird, I was under the impression POSIX mandated an error case
for the implicit fflush(3) done by fclose(3).  But I'm looking at the
standard and seeing nothing specific.

So, yes?  It is probably more portable to check fflush(3) explicitly?

This feels redundant though.  Like, obviously I want to flush the
descriptor when we close the stream, and obviously I would want to
know if the flush failed.  That's why I'm using stdio.

Index: echo.c
===
RCS file: /cvs/src/bin/echo/echo.c,v
retrieving revision 1.10
diff -u -p -r1.10 echo.c
--- echo.c  9 Oct 2015 01:37:06 -   1.10
+++ echo.c  11 Jul 2022 18:19:39 -
@@ -53,12 +53,15 @@ main(int argc, char *argv[])
nflag = 0;
 
while (*argv) {
-   (void)fputs(*argv, stdout);
-   if (*++argv)
-   putchar(' ');
+   if (fputs(*argv, stdout) == EOF)
+   err(1, "stdout");
+   if (*++argv && putchar(' ') == EOF)
+   err(1, "stdout");
}
-   if (!nflag)
-   putchar('\n');
+   if (!nflag && putchar('\n') == EOF)
+   err(1, "stdout");
+   if (fflush(stdout) == EOF || fclose(stdout) == EOF)
+   err(1, "stdout");
 
return 0;
 }



Re: echo(1): check for stdio errors

2022-07-11 Thread Todd C . Miller
On Sun, 10 Jul 2022 20:58:35 -0900, Philip Guenther wrote:

> Three thoughts:
> 1) Since stdio errors are sticky, is there any real advantage to checking
> each call instead of just checking the final fclose()?

Will that really catch all errors?  From what I can tell, fclose(3)
can succeed even if the error flag was set.  The pattern I prefer
is to use a final fflush(3) followed by a call to ferror(3) before
the fclose(3).

 - todd



Re: echo(1): check for stdio errors

2022-07-10 Thread Philip Guenther
On Sun, Jul 10, 2022 at 1:08 PM Scott Cheloha 
wrote:

> ok?
>
> Index: echo.c
> ===
> RCS file: /cvs/src/bin/echo/echo.c,v
> retrieving revision 1.10
> diff -u -p -r1.10 echo.c
> --- echo.c  9 Oct 2015 01:37:06 -   1.10
> +++ echo.c  10 Jul 2022 22:00:18 -
> @@ -53,12 +53,15 @@ main(int argc, char *argv[])
> nflag = 0;
>
> while (*argv) {
> -   (void)fputs(*argv, stdout);
> -   if (*++argv)
> -   putchar(' ');
> +   if (fputs(*argv, stdout) == EOF)
> +   err(1, "stdout");
> +   if (*++argv && putchar(' ') == EOF)
> +   err(1, "stdout");
> }
> if (!nflag)
> putchar('\n');
> +   if (fclose(stdout) == EOF)
> +   err(1, "stdout");
>
> return 0;
>  }
>
>
Three thoughts:
1) Since stdio errors are sticky, is there any real advantage to checking
each call instead of just checking the final fclose()?
2) It is claimed that POSIX requires *all* standard utilities to return
failure if any writes to stdout failed.  If it's important to fix this in
'echo', can we sketch out how we might fix make fixing this in the other
utilities easier?  I believe at least some chunk of the GNU utilities now
use an atexit() handler to do that; is that a reasonable idea and if not
can we come up with something better?
3) It would be no end of surprise and frustration for the external echo
utility to behave differently than the sh builtin.

Philip


echo(1): check for stdio errors

2022-07-10 Thread Scott Cheloha
ok?

Index: echo.c
===
RCS file: /cvs/src/bin/echo/echo.c,v
retrieving revision 1.10
diff -u -p -r1.10 echo.c
--- echo.c  9 Oct 2015 01:37:06 -   1.10
+++ echo.c  10 Jul 2022 22:00:18 -
@@ -53,12 +53,15 @@ main(int argc, char *argv[])
nflag = 0;
 
while (*argv) {
-   (void)fputs(*argv, stdout);
-   if (*++argv)
-   putchar(' ');
+   if (fputs(*argv, stdout) == EOF)
+   err(1, "stdout");
+   if (*++argv && putchar(' ') == EOF)
+   err(1, "stdout");
}
if (!nflag)
putchar('\n');
+   if (fclose(stdout) == EOF)
+   err(1, "stdout");
 
return 0;
 }