Tetsuo Handa wrote: > The problem is that TOMOYO is accessing already freed socket from security_file_open() > which later fails with -ENXIO (because we can't get file descriptor of sockets via > /proc/pid/fd/n interface), and the file descriptor is getting released before > security_file_open() completes because we do not raise "struct file"->f_count of > the file which is accessible via /proc/pid/fd/n interface. We can avoid this problem > if we can avoid calling security_file_open() which after all fails with -ENXIO. > How should we handle this race? Let LSM modules check if security_file_open() was > called on a socket? Well, just refusing security_file_open() is not sufficient, for open(O_PATH) allows installing file descriptor where SOCKET_I(inode)->sk can change at any moment, and TOMOYO cannot tell whether it is safe to access SOCKET_I(inode)->sk from security_inode_getattr(). But refusing open(O_PATH) as well might break userspace programs. Oh, no... ---------------------------------------- diff --git a/fs/open.c b/fs/open.c index b5b80469b93d..ea69668e2cd8 100644 --- a/fs/open.c +++ b/fs/open.c @@ -728,6 +728,16 @@ static int do_dentry_open(struct file *f, /* Ensure that we skip any errors that predate opening of the file */ f->f_wb_err = filemap_sample_wb_err(f->f_mapping); + /* + * Sockets must not be opened via /proc/pid/fd/n, even with O_PATH, + * for SOCKET_I(inode)->sk can be kfree()d at any moment after a file + * descriptor obtained by opening /proc/pid/fd/n was installed. + */ + if (unlikely(S_ISSOCK(inode->i_mode))) { + error = (f->f_flags & O_PATH) ? -ENOENT : -ENXIO; + goto cleanup_file; + } + if (unlikely(f->f_flags & O_PATH)) { f->f_mode = FMODE_PATH | FMODE_OPENED; f->f_op = &empty_fops; ---------------------------------------- ---------------------------------------- #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/socket.h> int main(int argc, char *argv[]) { pid_t pid = getpid(); int fd = socket(AF_INET, SOCK_STREAM, 0); char buffer[128] = { }; if (fork() == 0) { struct stat buf = { }; close(fd); snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/fd/%u", pid, fd); fd = open(buffer, __O_PATH); sleep(5); fstat(fd, &buf); _exit(0); } sleep(2); close(fd); return 0; } ----------------------------------------