getline doesn't terminate after fork
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
glibc (Ubuntu) |
Invalid
|
Undecided
|
Unassigned |
Bug Description
1) lsb_release -rd
Description: Ubuntu 16.04.3 LTS
Release: 16.04
2) apt-cache policy libc6
libc6:
Installed: 2.23-0ubuntu10
Candidate: 2.23-0ubuntu10
Version table:
*** 2.23-0ubuntu10 500
500 http://
500 http://
100 /var/lib/
2.23-0ubuntu3 500
3) The following program fails to terminate, although it should when compiled with no optimizations. This can be compiled with both clang and gcc and the same behavior described happens. This doesn't seem to happen with older versions of the library [citation needed].
Contents of 123.txt
```
AA\n
BB\n
CC
```
Code:
```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void h(FILE* fd) {
(void)fd;
pid_t p = fork();
if(!p) {
exit(1);
} else {
int s;
waitpid( p, &s,0);
}
}
int main() {
FILE* fd = fopen("123.txt", "r");
size_t cap = 20;
ssize_t br = 0;
char* buf = malloc(cap);
int linecount = 0;
while(1) {
br = getline( &buf , &cap, fd);
if(br == -1) break;
puts(buf);
h(fd);
}
free(buf);
fclose(fd);
return 0;
}
```
Expected:
```
AA
BB
CC
```
Actual:
```
AA
BB
CC
AA
BB
CC
<doesn't terminate>
```
* I believe it is with how the FILE* object is being buffered and how getline is interacting with the buffer with a combination of fork.
* If we change main to a syscall only version, it works as expected with the fork
```
int main() {
int fd = open("123.txt", O_RDONLY);
size_t cap = 20;
ssize_t br = 0;
char* buf = malloc(cap);
int linecount = 0;
while(1) {
br = read( fd , buf, cap);
if (br == -1) break;
buf[br] = 0;
puts(buf);
if (br < cap) break;
h(fd);
}
free(buf);
close(fd);
return 0;
}
```
* The `fread` version of the program terminates as expected.
* If the fork call is not there -- the program executes as normal.
* If there is any call to `fseek(fd, 0, SEEK_CUR)` after the getline but before the fork, the issues goes away. Not with `ftell(fd)` though.
* If there is the same check after the fork, the file is outputted twice and then the program terminates.
* The issues also goes away if we set the file object to be unbuffered (`setvbuf(fd, NULL, _IONBF, 0);`) right after the file's opening
* Stepping through gdb, I came to an infinite cycle in glibc/libio/
Found by: Yuxuan Ren <email address hidden>
Confirmation Testing/Validation: Lawrence Angrave <email address hidden>
Note that this behavior isn't specific to getline.
Contents of 123.txt: 14991 arbitrary bytes
Code:
```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void h(FILE* fd) {
(void)fd;
pid_t p = fork();
if(!p) {
sleep(2);
exit(1);
} else if (p > 0){
int s;
waitpid( p, &s,0);
} else{
perror("Fork");
exit(2);
}
}
int main() {
FILE* fd = fopen("123.txt", "r");
if (!fd){
perror("fopen");
exit(2);
}
size_t limit = 10;
while(limit--) {
char output[10000] = {0};
ssize_t retval = fread(output, 1, 9, fd);
fprintf(stderr, "fread: %zd\n", retval);
if (retval <= 0){
exit(0);
}
h(fd);
retval = fread(output, 1, 4096, fd);
fprintf(stderr, "fread: %zu\n"...