Skip to contentSkip to conversation

PROBLEM: flock system call can confuse current->locks and return spurious ENOLCK errno.

DISCUSSION Pat Knight 1 participant 1 message Apr 10, 2002 VFS

lore
Share & tools
lore

Pat Knight

author
#0 lore raw

[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_EX
locks_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 0
flock_lock_file (PID 1060) - return 0 // 1060 has created lock cfe6c3e0

flock_lock_file (PID 1061) - enter LOCK_EX
locks_alloc_lock (PID 1061): current->locks ++ to 1 // allocate new_fl flock_lock_file (PID 1061) - new_fl cfe6c1b8
flock_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_UN
flock_lock_file (PID 1061) - change 1 unlock 1
locks_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_EX
locks_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_UN
flock_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.