NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/57374: fcntl(F_GETLK) with SEEK_CUR returns wrong answer when unlocked
>Number: 57374
>Category: kern
>Synopsis: fcntl(F_GETLK) with SEEK_CUR returns wrong answer when unlocked
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sat Apr 22 13:40:00 +0000 2023
>Originator: Taylor R Campbell
>Release: current
>Organization:
The NetBSD Flockdation
>Environment:
flocked into a fever
>Description:
When fcntl(F_GETLK) is queried:
- with .l_start = S for some S,
- with .l_whence = SEEK_CUR (meaning: interpret .l_start relative to the file's current offset),
- on a file with no locks,
- on a file with nonzero current offset P,
what it returns is .l_start = S + P, .l_type = F_UNLCK, and .l_whence = SEEK_CUR.
This seems wrong: the returned range may not even overlap the queried range. It seems to me it should either return:
(a) .l_start = S, .l_type = F_UNLCK, .l_whence = SEEK_CUR; or
(b) .l_start = S + P, .l_type = F_UNLCK, .l_whence = SEEK_SET.
If I'm reading POSIX right, only option (a) is permitted:
> If no lock is found that would prevent this lock from being created, then the structure shall be left unchanged except for the lock type which shall be set to F_UNLCK.
https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html
The current behaviour happens because the former do_fcntl_lock (now moved to vn_advlock, but nxr has not yet seen this change as of press time) adds the file offset to .l_start in the case .l_whence = SEEK_CUR, but doesn't change .l_whence to reflect this:
246 if (fl->l_whence == SEEK_CUR) {
247 vn_lock(vp, LK_SHARED | LK_RETRY);
248 fl->l_start += fp->f_offset;
249 VOP_UNLOCK(vp);
250 }
https://nxr.netbsd.org/xref/src/sys/kern/sys_descrip.c?r=1.40#246
And when there is no lock, lf_getlock leaves the other members of the struct flock alone:
787 if ((block = lf_getblock(lock)) != NULL) {
...
799 } else {
800 fl->l_type = F_UNLCK;
801 }
https://nxr.netbsd.org/xref/src/sys/kern/vfs_lockf.c?r=1.78#799
And the caller of all this, sys_fcntl, just copies out the struct flock as it exists when do_fcntl_lock returns:
384 error = do_fcntl_lock(fd, cmd, &fl);
385 if (cmd == F_GETLK && error == 0)
386 error = copyout(&fl, SCARG(uap, arg), sizeof(fl));
https://nxr.netbsd.org/xref/src/sys/kern/sys_descrip.c?r=1.40#384
>How-To-Repeat:
$ cat test.c
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
struct flock fl = {
.l_start = 1,
.l_len = 2,
.l_pid = getpid(),
.l_type = F_RDLCK,
.l_whence = SEEK_CUR,
};
int fd;
setprogname(argv[0]);
if (argc != 2)
errx(1, "Usage: %s <file>", getprogname());
if ((fd = open(argv[1], O_RDWR)) == -1)
err(1, "open");
printf("start=%lld len=%lld pid=%lld type=%d whence=%d\n",
(long long)fl.l_start,
(long long)fl.l_len,
(long long)fl.l_pid,
fl.l_type,
fl.l_whence);
if (lseek(fd, 3, SEEK_SET) == -1)
err(1, "lseek");
if (fcntl(fd, F_GETLK, &fl) == -1)
err(1, "fcntl(F_GETLK)");
printf("start=%lld len=%lld pid=%lld type=%d whence=%d\n",
(long long)fl.l_start,
(long long)fl.l_len,
(long long)fl.l_pid,
fl.l_type,
fl.l_whence);
if (close(fd) == -1)
warn("close");
fflush(stderr);
return ferror(stderr);
}
$ make test.o test
cc -O2 -c test.c
cc -O2 -o test test.c
$ ./test test.o
start=1 len=2 pid=20836 type=1 whence=1
start=4 len=2 pid=20836 type=2 whence=1
>Fix:
Yes, please!
Home |
Main Index |
Thread Index |
Old Index