bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

write() not retried after EINTR in printf and echo


From: Serge van den Boom
Subject: write() not retried after EINTR in printf and echo
Date: Fri, 12 Jan 2018 18:05:19 +0100 (CET)
User-agent: Alpine 2.00 (BSF 1167 2008-08-23)

Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64' 
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-unknown-linux-gnu' 
-DCONF_VENDOR='unknown' -DLOCALEDIR='/opt/bash-4.4.12/share/locale' 
-DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H   -I.  -I. -I./include -I./lib   -g 
-O2 -Wno-parentheses -Wno-format-security
uname output: Linux test 4.4.0-98-generic #121-Ubuntu SMP Tue Oct 10 14:24:03 
UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Machine Type: x86_64-unknown-linux-gnu

Bash Version: 4.4
Patch Level: 12
Release Status: release

Description:
        If during an 'echo' or 'printf', the write() system call returns an
        EINTR error, the write() call is not retried.
        This can can happen when a system call is interrupted when a
        signal is received, which can happen when the user resized the
        terminal while the write() call is blocked.

        As a consequence, the intended output of 'echo' or 'printf' will be
        missing.

Repeat-By:
        1. Run the following command in a terminal (at least xterm works):
                while :; do builtin echo foo; done | sleep 100
        2. Wait a few seconds
        3. Resize the terminal (try again if this does not work immediately)
           This will send a 'SIGWINCH' signal to bash. (You can also send
           it manually.)
        4. "bash: echo: write error: Interrupted system call" will appear.

        The same thing happens with 'printf' instead of 'echo'. There may be
        other bash functionality which suffers from this.

        The pipe, the 'sleep 100', and the waiting in step 2, are to fill up
        the buffer of the pipe, so that the call to write() will block.

        To see that data is actually omitted from the output (and that this
        is not only a warning), you could run something like this instead
        of step 1:
            I=0
            while :; do
                builtin echo "$I"
                I=$(($I + 1))
            done | { sleep 5; cat > /tmp/test; }
        Trigger the sending of the signal in the first 5 seconds, and then let
        it run for a few seconds after. Inspecting /tmp/test will show that
        certain numbers in the sequence are missing.

Fix:
        I have not examined the actual Bash code, but typically, code
        which causes such a problem will look like this:
            int writeResult = write(fd, buf, toWrite);
            if (writeResult == -1) {
                perror("write");
                // Handle error here.
            }

        It should be replaced by something like:
            const char *bufPtr = buf;
            int leftToWrite = toWrite;
            while (leftToWrite > 0) {
                int writeResult = write(fd, bufPtr, leftToWrite);
                if (writeResult == -1) {
                    if (errno == EINTR)
                        continue;

                    perror("write");
                    // Handle error here.
                }
                
                leftToWrite -= writeResult;
                bufPtr += writeResult;
            }




reply via email to

[Prev in Thread] Current Thread [Next in Thread]