Red Hat Bugzilla – Bug 25234
Last modified: 2016-11-24 10:09:37 EST
While troubleshooting code from another programmer that suddenly failed on
a newer release of Linux, I found what I believe to be a bug in the
implementation of fcntl.
Here is a copy of the bug report minus the boring stuff:
After review of an error referenced during the testing and operation of a
keylock device attached through a serial port to the ******* product, I
have discovered a bug in the use of fcntl in Caldera 2.2 and Redhat 7.0.
This bug affects the ability of fcntl to modify open flags on an open file
Intent Of Code Sequence
In **********, most serial port operations occur within serial.c. It
contains a series of functions that ********* calls to open, close, read
and write from serial ports.
The function within serial.c opens all ports in a non-blocking mode. This
means that when a read operation is called, the point of execution will
not stop at the read even if the input buffer is empty.
When a read operation executes and there is a timeout value (in this case,
a 15 second timeout) the program will call the fcntl function. This
function call should remove the O_NDELAY (NONBLOCK) flag from the set of
parameters being used. At this point in execution, all read operations
for that file descriptor are set to blocking mode, meaning the point of
execution will stop at the read until the input buffer contains data.
The timeout period is timed through alarm, which will generate a SIGALRM
signal when it concludes its count-down. Using signal and through some
other techniques we wont describe here, the point of execution will
return one line past the read function call.
In our tests of serial.prg, we found that if the fcntl function call for
both the F_GETFL and F_SETFL were called from separate lines, the fcntl
call would fail to remove the O_NDELAY (NONBLOCK) flag. When we combined
the two steps into the same command sequence, it worked.
Here is how the two sequences were written:
CODE THAT FAILED
I = fcntl(fd,F_GETFL);
I &= ~O_NDELAY;
CODE THAT SUCCEEDED
fcntl(fd, F_SETFL, fcntl(fd,F_GETFL) & ~O_NDELAY):
Logically, both sequences should have worked. I have found documents
supporting the use of both methods. There seems to be no explanation why
the original code sequence failed to work on later versions of GLIBC.
The following is sample test code I used to determine my conclusion:
unsigned char test;
cnt = 0;
fd = open("/dev/tty",O_NOCTTY | O_NDELAY);
//********** works ***************
rc = fcntl(fd,F_SETFL,fcntl(fd,F_GETFL) & ~O_NDELAY);
//*********** doesnt work **************
//i = fcntl(fd,F_GETFL);
//i &= ~O_NDELAY;
//rc = fcntl(fd,F_SETFL,&i);
if(rc<0) printf("Error: unable to execute fcntl");
rc = read(fd,(void *)test,sizeof(test)-1);
printf("\nOUTPUT: %s\n", test);
I conclude that this is likely a bug in the implementation of the newer
GLIBC library distributed by Caldera and RedHat.
By-the-way, I personally wouldn't have written code like this. I usually
keep everything blocking, and use 'select'. Nevertheless, it does seem
odd that one of the methods work and the other does not.
This is a bug in your code, not in glibc. The 2 sequences do different
things (and the one which failes is bogus).
fcntl F_SETFL has the new file flags as third int argument, it does
not take an address of an int. Change &i into i and everything should
work fine (note that your code where you call fcntl(fd, F_GETLK)
directly in fcntl(fd, F_SETLK, ) call passes the flags, not an address,
so it is correct.