Parameter 'wait' passed to xtables_lock() signals three modes of
operation, depending on its value:
0: --wait not specified, do not wait if lock is busy
-1: --wait specified without value, wait indefinitely until lock becomes
free
>0: Wait for 'wait' seconds for lock to become free, abort otherwise
Since fixed commit, the first two cases were treated the same apart from
calling alarm(0), but that is a nop if no alarm is pending. Fix the code
by requesting a non-blocking flock() in the second case. While at it,
restrict the alarm setup to the third case only.
Cc: Jethro Beekman <jethro@fortanix.com>
Cc: howardjohn@google.com
Cc: Antonio Ojea <antonio.ojea.garcia@gmail.com>
Closes: https://bugzilla.netfilter.org/show_bug.cgi?id=1728
Fixes: 07e2107ef0cbc ("xshared: Implement xtables lock timeout using signals")
Signed-off-by: Phil Sutter <phil@nwl.cc>
--- /dev/null
+#!/bin/bash
+
+case "$XT_MULTI" in
+*xtables-legacy-multi)
+ ;;
+*)
+ echo skip $XT_MULTI
+ exit 0
+ ;;
+esac
+
+coproc RESTORE { $XT_MULTI iptables-restore; }
+echo "*filter" >&${RESTORE[1]}
+
+
+$XT_MULTI iptables -A FORWARD -j ACCEPT &
+ipt_pid=$!
+
+waitpid -t 1 $ipt_pid
+[[ $? -eq 3 ]] && {
+ echo "process waits when it should not"
+ exit 1
+}
+wait $ipt_pid
+[[ $? -eq 0 ]] && {
+ echo "process exited 0 despite busy lock"
+ exit 1
+}
+
+t0=$(date +%s)
+$XT_MULTI iptables -w 3 -A FORWARD -j ACCEPT
+t1=$(date +%s)
+[[ $((t1 - t0)) -ge 3 ]] || {
+ echo "wait time not expired"
+ exit 1
+}
+
+$XT_MULTI iptables -w -A FORWARD -j ACCEPT &
+ipt_pid=$!
+
+waitpid -t 3 $ipt_pid
+[[ $? -eq 3 ]] || {
+ echo "no indefinite wait"
+ exit 1
+}
+kill $ipt_pid
+waitpid -t 3 $ipt_pid
+[[ $? -eq 3 ]] && {
+ echo "killed waiting iptables call did not exit in time"
+ exit 1
+}
+
+kill $RESTORE_PID
+wait
+exit 0
return XT_LOCK_FAILED;
}
- if (wait != -1) {
+ if (wait > 0) {
sigact_alarm.sa_handler = alarm_ignore;
sigact_alarm.sa_flags = SA_RESETHAND;
sigemptyset(&sigact_alarm.sa_mask);
alarm(wait);
}
- if (flock(fd, LOCK_EX) == 0)
+ if (flock(fd, LOCK_EX | (wait ? 0 : LOCK_NB)) == 0)
return fd;
if (errno == EINTR) {