6 2

dmesg hands when piped through head - only on mrsh

Tudor Roman
Details
Message ID
<a34d74bc-802d-1f74-8d63-b5a883e7548a@gmail.com>
DKIM signature
pass
Download raw message
Self explanatory:

dmesg | head


The shell hangs, the dmesg process remains. Not a zombie, probably 
trying to read something.
Details
Message ID
<Qof8ejgyACmijfLIU9tS8GaKTJjZfVtdqCqBsmOUQgX5WJauBERKvBAhMV-n-ZLxHVkLJj51XZwRtyU525DCL67bOTeWa-rz7uB7BdSkQyQ=@emersion.fr>
In-Reply-To
<a34d74bc-802d-1f74-8d63-b5a883e7548a@gmail.com> (view parent)
DKIM signature
pass
Download raw message
On Tuesday, June 4, 2019 7:44 PM, Tudor Roman <tudurom@gmail.com> wrote:
> Self explanatory:
>
> dmesg | head
>
> The shell hangs, the dmesg process remains. Not a zombie, probably
> trying to read something.

Indeed, good catch! Attaching gdb to dmesg reveals that it blocks on a
write call. We might be leaking a file descriptor in the pipeline code…
Tudor Roman
Details
Message ID
<109525d3-9d0b-7afa-d36d-ecab4f58e7b2@gmail.com>
In-Reply-To
<Qof8ejgyACmijfLIU9tS8GaKTJjZfVtdqCqBsmOUQgX5WJauBERKvBAhMV-n-ZLxHVkLJj51XZwRtyU525DCL67bOTeWa-rz7uB7BdSkQyQ=@emersion.fr> (view parent)
DKIM signature
pass
Download raw message
I even found a fix and implemented it in my own shell.

The fix is to open the pipe with the pipe2 function, which accepts 
flags, with the O_CLOEXEC flag that closes the file descriptors when a 
process exits.

In this case, the head process finishes before dmesg, leaving the 
leaking file descriptors waiting. With O_CLOEXEC, the file descriptors 
are closed and dmesg exits normally.

I think macos doesn't have the pipe2 function. It shouldn't be a 
problem, it also doesn't have dmesg. O_CLOEXEC is more of a safeguard 
against such buggy programs.

On 04.06.2019 20:20, Simon Ser wrote:
> On Tuesday, June 4, 2019 7:44 PM, Tudor Roman <tudurom@gmail.com> wrote:
>> Self explanatory:
>>
>> dmesg | head
>>
>> The shell hangs, the dmesg process remains. Not a zombie, probably
>> trying to read something.
> Indeed, good catch! Attaching gdb to dmesg reveals that it blocks on a
> write call. We might be leaking a file descriptor in the pipeline code…
Details
Message ID
<HjqIiFcmSDKz2afRzi4huPaBWdhYh9po3SV20vv8jDN1sPGSSwbJgF5Ed84aT5E7JBL_tdfVKQTXEBPLVPX4eg-2EnYfXoGFn8ZAVMDjXmQ=@emersion.fr>
In-Reply-To
<109525d3-9d0b-7afa-d36d-ecab4f58e7b2@gmail.com> (view parent)
DKIM signature
pass
Download raw message
On Tuesday, June 4, 2019 8:54 PM, Tudor Roman <tudurom@gmail.com> wrote:
> I even found a fix and implemented it in my own shell.
>
> The fix is to open the pipe with the pipe2 function, which accepts
> flags, with the O_CLOEXEC flag that closes the file descriptors when a
> process exits.

Small correction: O_CLOEXEC closes FDs on exec, not when a process exits

> In this case, the head process finishes before dmesg, leaving the
> leaking file descriptors waiting. With O_CLOEXEC, the file descriptors
> are closed and dmesg exits normally.

So we are definitely leaking an FD then. Thanks for investigating :)

> I think macos doesn't have the pipe2 function. It shouldn't be a
> problem, it also doesn't have dmesg. O_CLOEXEC is more of a safeguard
> against such buggy programs.

Well, this is not specific to dmesg. For instance it also happens with
journalctl. This isn't because dmesg is buggy, either. It's just that
we're leaking a FD, so dmesg thinks it can write to it because some
other process might be calling read() on the other end.
Tudor Roman
Details
Message ID
<d78cfb24-df91-d847-d7ee-443b312015ce@gmail.com>
In-Reply-To
<HjqIiFcmSDKz2afRzi4huPaBWdhYh9po3SV20vv8jDN1sPGSSwbJgF5Ed84aT5E7JBL_tdfVKQTXEBPLVPX4eg-2EnYfXoGFn8ZAVMDjXmQ=@emersion.fr> (view parent)
DKIM signature
pass
Download raw message
Thanks for clarifying! I am just a student and I learn about the inner 
workings by studying projects like mrsh. It's a very good educational 
resource, thanks for writing it!

On 04.06.2019 21:09, Simon Ser wrote:
> On Tuesday, June 4, 2019 8:54 PM, Tudor Roman <tudurom@gmail.com> wrote:
>> I even found a fix and implemented it in my own shell.
>>
>> The fix is to open the pipe with the pipe2 function, which accepts
>> flags, with the O_CLOEXEC flag that closes the file descriptors when a
>> process exits.
> Small correction: O_CLOEXEC closes FDs on exec, not when a process exits
>
>> In this case, the head process finishes before dmesg, leaving the
>> leaking file descriptors waiting. With O_CLOEXEC, the file descriptors
>> are closed and dmesg exits normally.
> So we are definitely leaking an FD then. Thanks for investigating :)
>
>> I think macos doesn't have the pipe2 function. It shouldn't be a
>> problem, it also doesn't have dmesg. O_CLOEXEC is more of a safeguard
>> against such buggy programs.
> Well, this is not specific to dmesg. For instance it also happens with
> journalctl. This isn't because dmesg is buggy, either. It's just that
> we're leaking a FD, so dmesg thinks it can write to it because some
> other process might be calling read() on the other end.
Details
Message ID
<ph8gffAjTGP4L0RtCs7Ji5xI9G-URoDyvdb8_xqJnGNbyi8plGTxI4V0hoEyBKBIPtcMZ5LaXkweH56mJEUY-oL8pwR4MUKW1JQ8a0JjQbU=@emersion.fr>
In-Reply-To
<d78cfb24-df91-d847-d7ee-443b312015ce@gmail.com> (view parent)
DKIM signature
pass
Download raw message
On Tuesday, June 4, 2019 9:34 PM, Tudor Roman <tudurom@gmail.com> wrote:
> Thanks for clarifying! I am just a student and I learn about the inner
> workings by studying projects like mrsh. It's a very good educational
> resource, thanks for writing it!

Nice! Glad you find it useful :)
Tudor Roman
Details
Message ID
<bc89cb83-0a36-b955-a0df-300ea571fade@gmail.com>
In-Reply-To
<Qof8ejgyACmijfLIU9tS8GaKTJjZfVtdqCqBsmOUQgX5WJauBERKvBAhMV-n-ZLxHVkLJj51XZwRtyU525DCL67bOTeWa-rz7uB7BdSkQyQ=@emersion.fr> (view parent)
DKIM signature
pass
Download raw message
I think that this issue and probably more future issues appear because 
of pipe items being executed in the parent process (except for process 
commands). Virtually all POSIX shells run all of their pipe items in 
separate processes.

echo something | while read -r line; do

     exit 0

done

This will _not_ make the shell exit. Tested in bash, dash and mksh.

This means that if you set a variable's value in a while, the value will 
stay only inside the while. It is expected in all of these shells.

The only shell that runs pipe items in the parent is zsh, and with many 
problems because of the complexity [1].

The design of pipes is based on each item running in a separate process. 
Otherwise, if we were to pipe for example a while into another while, 
the first while's output will be put on the pipe, and then when it comes 
the second while's turn, it will read from the pipe all of the data. The 
pipe becomes a basic queue, uses memory etc.

Now, I think that the leaked FDs we experience are because of the pipes. 
That's because, like I showed earlier, if the pipes are created with 
O_CLOEXEC, they will be closed in the child process of the command.

But, if instead of the process command we have something that runs in 
the parent, like a while, between the first task poll and the second, 
the file descriptors will already be changed: stdin is dup-ed to 
last_stdout, and then dup-ed to dup_stdin after the for loop exits. 
(pipeline.c, lines 46 and 85). I'm not so sure but that might be why 
something like

dmesg | while read -r line; do

     echo line

done

hangs the shell.

My suggestion is to bring the same behaviour as the other shells, to run 
each pipe item in a subprocess. It is expected, after all.


[1]: 
https://github.com/zsh-users/zsh/blob/380da6011862248ee283552a3d37c31e3d816365/Src/exec.c#L344