Hi everyone,
While looking at musl's implementation of mkostemp [1] I found a serious
problem in uClibc's implementation. In uClibc, libc/stdlib/mkostemp.c
calls __gen_tempname in libc/misc/internals/tempname.c with kind =
__GT_FILE and opens the file with
fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, mode);
thus completely ignoring the O_APPEND, O_CLOEXEC, and O_*SYNC flags [2].
In fact, it seems to confuse the file mode with the flags. This
defeats the purpose of mkostemp, especially in the case of O_CLOEXEC
which can be done with a followup call to fcntl, but at the loss of
atomicity (and portibility because people expect it to just work).
I'm going to try to this up using ref [3] as the standard. (Thanks Rich
Felker.)
I've written some POC to demonstrate the problem. Here are the results
on glibc, musl and uClibc.
---- glibc --------
cc -o stealfd stealfd.c
cc -D_CLOEXEC -o test-cloexec test.c
cc -U_CLOEXEC -o test-nocloexec test.c
./test-cloexec
Bad file descriptor
Child exited with status 0
abc.UEOGER contains string ''
./test-nocloexec
Child exited with status 0
abc.e7syCR contains string 'test'
ls -al abc.*
-rw------- 1 root root 5 Sep 28 11:09 abc.e7syCR
-rw------- 1 root root 0 Sep 28 11:09 abc.UEOGER
-------------------
---- musl ---------
cc -o stealfd stealfd.c
cc -D_CLOEXEC -o test-cloexec test.c
cc -U_CLOEXEC -o test-nocloexec test.c
./test-cloexec
Bad file descriptor
Child exited with status 0
abc.DfOfhD contains string ''
./test-nocloexec
Child exited with status 0
abc.BfckJO contains string 'test'
ls -al abc.*
-rw------- 1 root root 5 Sep 28 15:06 abc.BfckJO
-rw------- 1 root root 0 Sep 28 15:06 abc.DfOfhD
-------------------
---- uCliblc ------
cc -o stealfd stealfd.c
cc -D_CLOEXEC -o test-cloexec test.c
cc -U_CLOEXEC -o test-nocloexec test.c
./test-cloexec
Child exited with status 0
abc.v741kr contains string 'test'
./test-nocloexec
Child exited with status 0
abc.Qc6iGz contains string 'test'
ls -al abc.*
---------- 1 root root 5 Sep 28 15:10 abc.Qc6iGz
---------- 1 root root 5 Sep 28 15:10 abc.v741kr
------------------
I've inlined the POC for easier reading. To obtain the above results,
just do `make`.
---- Makefile -----
all: stealfd test-cloexec test-nocloexec
@echo
./test-cloexec
@echo
./test-nocloexec
@echo
ls -al abc.*
@rm -f abc.*
stealfd: stealfd.c
$(CC) -o $@ $^
test-cloexec: test.c
$(CC) -D_CLOEXEC -o $@ $^
test-nocloexec: test.c
$(CC) -U_CLOEXEC -o $@ $^
.PHONY: clean
clean:
rm -f stealfd test-cloexec test-nocloexec abc.*
-------------------
---- test.c -------
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
int main() {
int fd, status;
char buff[6];
char template[] = "abc.XXXXXX";
#ifdef _CLOEXEC
fd = mkostemp(template, O_CLOEXEC);
#else
fd = mkostemp(template, 0);
#endif
snprintf(buff, 6, "%d", fd);
if(!fork()) execl("./stealfd", "stealfd", buff, NULL);
wait(&status);
printf("Child exited with status %d\n", WEXITSTATUS(status));
memset(buff, 0, 6);
lseek(fd, 0, SEEK_SET);
errno = 0;
if(read(fd, buff, 6) == -1) {
printf("%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("%s contains string '%s'\n", template, buff);
close(fd);
exit(EXIT_SUCCESS);
}
-------------------
---- stealfd.c ----
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[]) {
int fd;
if(argc < 2) {
printf("Usage %s fd\n", argv[0]);
exit(EXIT_FAILURE);
}
sscanf(argv[1], "%d", &fd);
errno = 0;
if(write(fd, "test\0", 5) == -1)
printf("%s\n", strerror(errno));
close(fd);
exit(EXIT_SUCCESS);
}
-------------------
Ref.
[1] http://thread.gmane.org/gmane.linux.lib.musl.general/6199
[2] See mkstemp(3)
[3] http://austingroupbugs.net/view.php?id=411
--
Anthony G. Basile, Ph. D.
Chair of Information Technology
D'Youville College
Buffalo, NY 14201
(716) 829-8197
_______________________________________________
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc