Package: lsof
Version: 4.91+dfsg-1
Severity: normal
Dear Maintainer,
The output of lsof is misleading when it comes to "flock" and "OFD"
file locks on Linux.
Background: there are three types of advisory file locks in Linux:
- "flock" or "BSD" locks, which are created by the flock system call.
These are associated with a particular *open file description*, so
they are inherited by child processes.
- "fcntl" or "POSIX" locks, which are created by the fcntl system call
with the F_SETLK or F_SETLKW option. These are associated with a
particular *process ID*, so they are not inherited by child
processes.
- "OFD" locks, which are created by the fcntl system call with the
F_OFD_SETLK or F_OFD_SETLKW option. These are like flock locks in
that they are associated with an open file description, but they are
like fcntl locks in that they apply to a particular range of bytes.
The kernel reports all of these locks via /proc/locks, and lsof parses
that file and tries to indicate which processes are currently holding
locks on which files.
However, the information in /proc/locks is incomplete and in some
cases inaccurate: for flock and OFD locks, it tells you that *some
process* is holding a lock on the file, but it doesn't tell you which
one(s), since the lock may have been inherited across a fork. The
proc(5) manpage states:
Because OFD locks are not owned by a single process (since
multiple processes may have file descriptors that refer to the
same open file description), the value -1 is displayed in [the
process ID field] for OFD locks. (Before kernel 4.14, a bug meant
that the PID of the process that initially acquired the lock was
displayed instead of the value -1.)
The manpage doesn't mention that exactly the same "bug" also applies
to flock locks.
As far as I can see, this does appear to be a limitation of the
kernel: I don't know of any way that lsof could possibly figure out
which processes, or which file descriptors, are associated with a
particular lock.
So although I believe this is a bug in lsof, I don't think it is one
that lsof can fix by itself.
Nonetheless, it's a limitation that probably should be better
documented, and perhaps lsof's output could be improved to reflect
this uncertainty - for example, it could display a '?' in the lock
column to indicate "*some* process has a lock on this file; this
particular process might or might not."
Here is an example program:
#define _GNU_SOURCE
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int fd1, fd2, fd3, fd4, status;
pid_t child;
struct flock fl = { 0 };
char cmd[100];
fd1 = open("/tmp/file1", O_RDWR | O_CREAT, 0600);
fd2 = open("/tmp/file1", O_RDWR | O_CREAT, 0600);
fd3 = open("/tmp/file2", O_RDWR | O_CREAT, 0600);
fd4 = open("/tmp/file2", O_RDWR | O_CREAT, 0600);
if (fd1 < 0 || fd2 < 0 || fd3 < 0 || fd4 < 0)
err(1, "open");
if (flock(fd1, LOCK_SH))
err(1, "flock");
fl.l_type = F_WRLCK;
if (fcntl(fd3, F_OFD_SETLKW, &fl))
err(1, "fcntl");
child = fork();
if (child < 0)
err(1, "fork");
else if (child == 0) {
snprintf(cmd, sizeof(cmd), "lsof -p %d,%d -a -d 3-99",
getppid(), getpid());
system(cmd);
}
else {
wait(&status);
}
return 0;
}
Running this program produces:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
a.out 5615 benjamin 3uR REG 0,44 0 517883 /tmp/file1
a.out 5615 benjamin 4uR REG 0,44 0 517883 /tmp/file1
a.out 5615 benjamin 5u REG 0,44 0 517884 /tmp/file2
a.out 5615 benjamin 6u REG 0,44 0 517884 /tmp/file2
a.out 5616 benjamin 3u REG 0,44 0 517883 /tmp/file1
a.out 5616 benjamin 4u REG 0,44 0 517883 /tmp/file1
a.out 5616 benjamin 5u REG 0,44 0 517884 /tmp/file2
a.out 5616 benjamin 6u REG 0,44 0 517884 /tmp/file2
The flock lock is indicated (with an "R") for the parent process, but
not for the child process; the OFD lock is not indicated at all. (If
you run the same program on an older kernel, it will show a "W" in the
fourth and fifth lines.)
To be *really* precise, file descriptors 3 and 5 are the ones holding
the locks; those FDs should have an "R" or "W" next to them while 4
and 6 shouldn't.
-- System Information:
Debian Release: 10.2
APT prefers stable-updates
APT policy: (500, 'stable-updates'), (500, 'stable-debug'), (500, 'stable')
Architecture: amd64 (x86_64)
Foreign Architectures: i386
Kernel: Linux 4.19.0-6-amd64 (SMP w/40 CPU cores)
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8),
LANGUAGE=en_US.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages lsof depends on:
ii libc6 2.28-10
ii libselinux1 2.8-1+b1
lsof recommends no packages.
Versions of packages lsof suggests:
ii perl 5.28.1-6
-- debconf-show failed