PROBLEM: flock system call can confuse current->locks and return spurious ENOLCK errno.
[1.] One line summary of the problem:
flock system call can confuse current->locks and return spurious ENOLCK errno.
[2.] Full description of the problem/report:
We had a (largish) program that opens a file and then forks() two children. The children loop rapidly writing to the file, using flock() locks to maintain consistency. Note - the fork means that both child processes are sharing a filp. We found that we would sometimes get ENOLCK failures.
We tracked this down to the value of current->locks going to unsigned 32 bit max int (and thus equalling RLIMIT_LOCKS). This was caused by locks_free_lock decrementing current->locks when it was zero.
We eventually determined that this was due to (correct) flock behaviour of sharing locks held by processes inheriting an open file from a common ancestor. We know we shouldn't be using flock in these circumstances, and have stopped.
The "search" loop in flock_lock_file checks to see if the requested lock is already held by that file pointer. If child A has done flock(f,LOCK_EX) and child B does flock(f,LOCK_EX) before A unlocks, the requested lock is found. flock_lock_file breaks out of the "search" loop with 'goto out'. The code at the 'out' label does a locks_free_lock on the new_fl that was allocated "just in case". B now thinks it has the lock (both A and B have it). However, its current->locks wasn't incremented as the lock already existed. If B now unlocks before A, B's value for current->locks decrements "below" 0. Later on, A tries to free and doesn't find the lock, so never decrements its current->locks.
If current->locks was only used by flock I wouldn't mind if the value was corrupted by bad use of flock. However, fcntl locks also use current->locks. Is there a danger that the corrupt values set by flock could affect fcntl? Also, I think this is a case of user level process behaviour shouldn't "upset" the kernel.
[3.] Keywords (i.e., modules, networking, kernel):
FILE LOCKING, fs, locks.c
[4.] Kernel version (from /proc/version):
Linux version 2.4.17PK (root@budvar) (gcc version 2.95.3 20010315 (SuSE)) #8 Mon Apr 8 14:49:51 BST 2002
But note that we reproduced the problem in 2.4.18-ac1. The bug is in the fs/locks.c file and that is identical in both versions of the kernel.
[5.] Output of Oops.. message (if applicable) with symbolic information
resolved (see Documentation/oops-tracing.txt)
No oops.
[6.] A small shell script or example program which triggers the
problem (if possible)
[7.] Environment
[7.1.] Software
Linux budvar 2.4.17PK #8 Mon Apr 8 14:49:51 BST 2002 i686 unknown
Gnu C 2.95.3 Gnu make 3.79.1 binutils 2.11.90.0.29 util-linux 2.11i mount 2.11i modutils 2.4.8 e2fsprogs 1.24a reiserfsprogs 3.x.0k-pre9 Linux C Library x 1 root root 1384168 Sep 20 2001 /lib/libc.so.6 Dynamic linker (ldd) 2.2.4 Procps 2.0.7 Net-tools 1.60 Kbd 1.04 Sh-utils 2.0 Modules Loaded NVdriver vmnet vmmon serial 3c59x
[7.2.] Processor information (from /proc/cpuinfo):
Not relevant - this is CPU independent and deterministic.
[7.3.] Module information (from /proc/modules):
Not a module related bug.
[7.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem) Not hardware related.
[7.5.] PCI information ('lspci -vvv' as root)
Not hardware related.
[7.6.] SCSI information (from /proc/scsi/scsi)
Not SCSI related.
[7.7.] Other information that might be relevant to the problem
(please look in /proc and include all information that you think to be relevant):
[X.] Other notes, patches, fixes, workarounds:
Timeline showing the bug. 1060 and 1061 are the siblings. Functions are in fs/locks.c.
flock_lock_file (PID 1060) - enter LOCK_EXlocks_alloc_lock (PID 1060): current->locks ++ to 2 // allocate new_fl flock_lock_file (PID 1060) - new_fl cfe6c3e0 flock_lock_file (PID 1060) - change 0 unlock 0flock_lock_file (PID 1060) - return 0 // 1060 has created lock cfe6c3e0
flock_lock_file (PID 1061) - enter LOCK_EXlocks_alloc_lock (PID 1061): current->locks ++ to 1 // allocate new_fl flock_lock_file (PID 1061) - new_fl cfe6c1b8flock_lock_file (PID 1061) - existing cfe6c3e0 // found existing lock locks_free_lock (PID 1061) (caller flock_lock_file) - current->locks -- to 0 flock_lock_file (PID 1061) - return 0 // 1061 uses existing lock, frees new_fl
flock_lock_file (PID 1061) - enter LOCK_UNflock_lock_file (PID 1061) - change 1 unlock 1locks_free_lock (PID 1061) - locks 0 called by _delete_lock // frees cfe6c3e0 locks_free_lock (PID 1061) - (caller _delete_lock) - current->locks -- to
4294967295 // free lock we didn't allocate!!!
flock_lock_file (PID 1061) - return 0
flock_lock_file (PID 1061) - enter LOCK_EXlocks_alloc_lock (PID 1061): locks 4294967295 >= RLIMIT_LOCKS 4294967295 flock_lock_file (PID 1061) - new_fl NULL, return ENOLCK
flock_lock_file (PID 1060) - enter LOCK_UNflock_lock_file (PID 1060) - change 0 unlock 1 // no lock found flock_lock_file (PID 1060) - return 0 // nothing to do, but locks too high.