Note: This bug is displayed in read-only format because the product is no longer active in Red Hat Bugzilla.
For bugs related to Red Hat Enterprise Linux 3 product line. The current stable release is 3.9. For Red Hat Enterprise Linux 6 and above, please visit Red Hat JIRA https://issues.redhat.com/secure/CreateIssue!default.jspa?pid=12332745 to report new issues.

Bug 145992

Summary: crond does not explicitly open file descriptor 0 if it is not available while startup
Product: Red Hat Enterprise Linux 3 Reporter: Mustafa Mahudhawala <mmahudha>
Component: vixie-cronAssignee: Jason Vas Dias <jvdias>
Status: CLOSED ERRATA QA Contact: Brock Organ <borgan>
Severity: medium Docs Contact:
Priority: medium    
Version: 3.0CC: katzj, laroche
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2005-05-20 00:05:24 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
Patch to open fd 0 if not already open none

Description Mustafa Mahudhawala 2005-01-24 16:18:09 UTC
From Bugzilla Helper:
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5)
Gecko/20041107 Firefox/1.0

Description of problem:
The result of crond not explicitly opening file descriptor 0, is that
the cronjobs inherit a closed fd 0 (stdin), which tends to be an issue
in some cases. eg. if apache is periodically restarted from cron
(quite common in web hosting environments) and cron does not open
stdin i.e. fd 0 for it, then apache spams the apache error_log with
following error:

[error] (88)Socket operation on non-socket: apr_accept: (client socket)

Note: this issue manifests itself only if auditd is disabled and
/dev/audit removed (see Bug #131860)

If crond is invoked in same scenario (i.e. with closed fd 0) using
strace, 

# cat test.sh
#!/bin/sh
exec 0<&-
/usr/bin/strace -xv -tt -ff -o /tmp/crond.strace /usr/sbin/crond

following are some of the observations related to this issue:

/tmp/crond.strace.13897:09:21:01.000350 pipe([0, 4]) = 0
/tmp/crond.strace.13897:09:21:01.000453 pipe([5, 6]) = 0

Though pipe using 0 as one of the file descriptors is not an issue in
itself, but later fd 0 is specifically closed, and then a dup2 called,
which fails as follows ..

/tmp/crond.strace.13899:09:21:01.004200 dup2(0, 0)              = -1
EBADF (Bad file descriptor)
and later the cronjob is executed without a valid fd 0.

Analyzing the source of vixie-cron-3.0.1-75.1, this is how it probably
happens ..

acquire_daemonlock(0) in main() of cron.c uses fd 0 for the pid file
(since fd 0 was not available when cron started) this is then
converted to a file pointer using fdopen() and preserved via static
file pointer *fp (check misc.c)

later in do_command() of do_command.c (this is where the actual
execution of cronjob is handled) acquire_daemonlock(1) is called which
closes the static file pointer *fp, this inturn releases fd 0

later from child_process() pipe(stdin_pipe) and pipe(stdout_pipe)
calls are made to communicate with the future child (the cronjob).
here the file descriptor for reading of pipe(stdin_pipe) is 0 since fd
0 has been made available as the lowest file descriptor due to call of
acquire_daemonlock(1). This corresponds to the pipe calls seen in
strace above.

Then after fork(), intentionally, the not required (in child) ends of
the inherited pipes are closed via calls close(stdin_pipe[WRITE_PIPE])
and close(stdout_pipe[READ_PIPE])

But just before this, via the setsid() call, un-intentionally, the
file descriptor for reading of stdin_pipe  i.e. stdin_pipe[READ_PIPE]
gets closed since it was using fd 0 (check compat.c for details on setsid)

Due to this dup2(stdin_pipe[READ_PIPE], STDIN) which ties (duplicates)
the stdin_pipe[READ_PIPE] to the STDIN of the child (i.e. the cronjob)
fails with EBADF (Bad file descriptor) as seen in strace above.

And hence the cronjob is executed without a valid i.e. closed STDIN

If laus is enabled and /dev/audit exist, then possibly, when
/dev/audit is opened via LAUS_OPEN macro just before
acquire_daemonlock(0) in main() in cron.c, it uses up fd 0, this
causes acquire_daemonlock(0) to preserve a non 0 fd via static file
pointer *fp for the pid file, and due to which the the scenario
described above does not occur.

Version-Release number of selected component (if applicable):
vixie-cron-3.0.1-75.1

How reproducible:
Always

Steps to Reproduce:
1. Ensure auditd is disabled and /dev/audit is removed as per Bug #131860
1. Restart crond from a shell script after closing file descriptor zero

# cat test1.sh
#!/bin/sh
exec 0<&-
service crond restart

2. Schedule a cronjob which just attempts to open a file, and check
the file descriptor allocated

*/1 * * * * root /root/test2 >> /tmp/test2

test2 is created from test2.c

# cat test2.c
#include <errno.h>
#include <fcntl.h>
extern int errno;
int main(int argc, char *argv[]) {
        int fd;
        if((fd = open("/etc/hosts",O_RDONLY)) == -1)
                printf("Error No %d\n",errno);
        else
                printf("FD - %d\n", fd);
        close(fd);
        return 0;
}

Compiled using:
# gcc -o test2 test2.c

Check (tail -f) /tmp/test2

Actual Results:  FD - 0

Expected Results:  FD - 3

Additional info:

Comment 1 Mustafa Mahudhawala 2005-01-24 16:21:43 UTC
Created attachment 110129 [details]
Patch to open fd 0 if not already open

This patch needs to be applied after all the other vixie-cron-3.0.1 patches
have been applied

Comment 2 Jason Vas Dias 2005-01-24 16:32:03 UTC
This problem is now fixed in vixie-cron-4.1-2_EL3, which is the latest
version for RHEL-3, currently undergoing QA testing, and which should
be in RHEL-3-U5. Meanwhile, you can download it from:
   http://people.redhat.com/~jvdias/vixie-cron/RHEL-3 


Comment 5 Mustafa Mahudhawala 2005-01-24 17:06:39 UTC
Thank You Jason,
vixie-cron-4.1-2_EL3 works fine.

Btw, just to correct my explaination,

stdin_pipe[READ_PIPE] is un-intentionally closed not by setsid, but by
close(STDIN) called just before dup2(stdin_pipe[READ_PIPE], STDIN)
since it was using fd 0.

Comment 7 Tim Powers 2005-05-20 00:05:24 UTC
An advisory has been issued which should help the problem
described in this bug report. This report is therefore being
closed with a resolution of ERRATA. For more information
on the solution and/or where to find the updated files,
please follow the link below. You may reopen this bug report
if the solution does not work for you.

http://rhn.redhat.com/errata/RHBA-2005-291.html