s3:selftest: add samba3.blackbox.smbXsrv_client_ctdb_registered_ips
authorStefan Metzmacher <metze@samba.org>
Fri, 17 Nov 2023 10:46:27 +0000 (11:46 +0100)
committerJule Anger <janger@samba.org>
Sat, 16 Dec 2023 14:29:10 +0000 (14:29 +0000)
This demonstrates the crash that happens if a client connects to a
non-public address first followed by a connect
to public address with the same client_guid and a connection to
the non-public address gets disconnected first, we hit by a
use-after-free talloc_get_type_abort() called from release_ip() as
"xconn" is already gone, taking smbd_release_ip_state with it.

Note that we also need to mark some subtests as flapping
as there's a 2nd problem that happens in the interaction
between smbd processes and ctdb when passing a multichannel
connection to an existing process, it means we sometimes
loose the 'tickle' information within ctdb to that tcp connection.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15523

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Martin Schwenke <martin@meltin.net>
(cherry picked from commit 082c7df4d04c2a94c5413c1d6b7eae7be610f950)

selftest/flapping.d/smbXsrv_client_ctdb_registered_ips [new file with mode: 0644]
selftest/knownfail.d/smbXsrv_client_ctdb_registered_ips [new file with mode: 0644]
source3/script/tests/test_smbXsrv_client_ctdb_registered_ips.sh [new file with mode: 0755]
source3/selftest/tests.py

diff --git a/selftest/flapping.d/smbXsrv_client_ctdb_registered_ips b/selftest/flapping.d/smbXsrv_client_ctdb_registered_ips
new file mode 100644 (file)
index 0000000..740bb87
--- /dev/null
@@ -0,0 +1,4 @@
+^samba3.blackbox.smbXsrv_client_ctdb_registered_ips.step5:.ctdb_gettickles.NUM
+^samba3.blackbox.smbXsrv_client_ctdb_registered_ips.step5:.ctdb_gettickles.DST
+^samba3.blackbox.smbXsrv_client_ctdb_registered_ips.step6:.ctdb_gettickles.NUM
+^samba3.blackbox.smbXsrv_client_ctdb_registered_ips.step6:.ctdb_gettickles.DST
diff --git a/selftest/knownfail.d/smbXsrv_client_ctdb_registered_ips b/selftest/knownfail.d/smbXsrv_client_ctdb_registered_ips
new file mode 100644 (file)
index 0000000..8bce2bb
--- /dev/null
@@ -0,0 +1 @@
+^samba3.blackbox.smbXsrv_client_ctdb_registered_ips.step7:.smbstatus.0.sessions
diff --git a/source3/script/tests/test_smbXsrv_client_ctdb_registered_ips.sh b/source3/script/tests/test_smbXsrv_client_ctdb_registered_ips.sh
new file mode 100755 (executable)
index 0000000..025a0fa
--- /dev/null
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+#
+# Test smbd let cleanup registered ip addresses in a multichannel
+# scenario
+#
+
+if [ $# -lt 3 ]; then
+       echo Usage: test_smbXsrv_client_ctdb_registered_ips.sh SERVERCONFFILE CTDB_IFACE_IP SHARENAME
+       exit 1
+fi
+
+CONF=$1
+CTDB_IFACE_IP=$2
+SHARE=$3
+
+SMBCLIENT="$BINDIR/smbclient"
+SMBSTATUS="$BINDIR/smbstatus"
+CTDB="$BINDIR/ctdb"
+TIMELIMIT="$BINDIR/timelimit"
+
+incdir=$(dirname "$0")/../../../testprogs/blackbox
+. "$incdir"/subunit.sh
+
+failed=0
+
+test_smbclient()
+{
+       name="$1"
+       server="$2"
+       share="$3"
+       cmd="$4"
+       shift
+       shift
+       subunit_start_test "$name"
+       output=$($VALGRIND $SMBCLIENT //$server/$share -c "$cmd" "$@" 2>&1)
+       status=$?
+       if [ x$status = x0 ]; then
+               subunit_pass_test "$name"
+       else
+               echo "$output" | subunit_fail_test "$name"
+       fi
+       return $status
+}
+
+cd "$SELFTEST_TMPDIR" || exit 1
+
+# Create the smbclient communication pipes.
+rm -f smbclient1-stdin smbclient1-stdout smbclient1-stderr
+mkfifo smbclient1-stdin smbclient1-stdout smbclient1-stderr
+rm -f smbclient2-stdin smbclient2-stdout smbclient2-stderr
+mkfifo smbclient2-stdin smbclient2-stdout smbclient2-stderr
+
+smbstatus_num_sessions()
+{
+       # We don't check for died processes
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$SMBSTATUS" "$CONF" --fast --json | jq -M '.sessions | length'
+}
+
+ctdb_add_public_ip()
+{
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" addip ${CTDB_IFACE_IP}/24 lo
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" ipreallocate
+}
+
+ctdb_ip()
+{
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" ip
+}
+
+ctdb_gettickles()
+{
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" gettickles ${CTDB_IFACE_IP}
+}
+
+ctdb_reload_public_ips()
+{
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" reloadips 0
+       UID_WRAPPER_INITIAL_RUID=0 UID_WRAPPER_INITIAL_EUID=0 "$CTDB" ipreallocate
+}
+
+testit_grep_count "step1: smbstatus 0 sessions" '^0$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+test_smbclient "step2: smbclient against node0[${CTDB_IFACE_IP}]" "${CTDB_IFACE_IP}" "${SHARE}" "ls" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+       --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+       || failed=$(expr $failed + 1)
+
+testit_grep_count "step2: smbstatus 0 sessions" '^0$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+CLI_FORCE_INTERACTIVE=1
+export CLI_FORCE_INTERACTIVE
+
+testit "step3: start backgroup smbclient against node0[${CTDB_IFACE_IP}]" true || failed=$(expr $failed + 1)
+
+# Connect a first time
+${SMBCLIENT} //"${CTDB_IFACE_IP}"/"${SHARE}" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+       --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+       <smbclient1-stdin >smbclient1-stdout 2>smbclient1-stderr &
+CLIENT1_PID=$!
+
+exec 100>smbclient1-stdin 101<smbclient1-stdout 102<smbclient1-stderr
+
+testit_grep_count "step3: smbclient1-stdout" 'Try "help" to get a list of possible commands.' 1 $TIMELIMIT 15 head -1 smbclient1-stdout || failed=$(expr $failed + 1)
+
+testit_grep_count "step3: smbstatus 1 session" '^1$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+testit_grep_count "step3: ctdb_ip" "${CTDB_IFACE_IP}" 0 ctdb_ip || failed=$(expr $failed + 1)
+testit_expect_failure_grep "step3: ctdb_gettickles" "Control GET_TCP_TICKLE_LIST failed" ctdb_gettickles || failed=$(expr $failed + 1)
+
+testit "step4: ctdb_add_public_ip" ctdb_add_public_ip || failed=$(expr $failed + 1)
+
+testit_grep_count "step4: ctdb_ip" "^${CTDB_IFACE_IP} 0\$" 1 ctdb_ip || failed=$(expr $failed + 1)
+testit_grep_count "step4: ctdb_gettickles" "Num connections: 0" 1 ctdb_gettickles || failed=$(expr $failed + 1)
+
+testit "step5: start backgroup 2nd smbclient against node0[${CTDB_IFACE_IP}]" true || failed=$(expr $failed + 1)
+# Connect a second time
+${SMBCLIENT} //"${CTDB_IFACE_IP}"/"${SHARE}" -U"${DC_USERNAME}"%"${DC_PASSWORD}" \
+       --option="libsmb:client_guid=6112f7d3-9528-4a2a-8861-0ca129aae6c4" \
+       <smbclient2-stdin >smbclient2-stdout 2>smbclient2-stderr &
+CLIENT2_PID=$!
+
+exec 200>smbclient2-stdin 201<smbclient2-stdout 202<smbclient2-stderr
+
+testit_grep_count "step5: smbclient2-stdout" 'Try "help" to get a list of possible commands.' 1 $TIMELIMIT 15 head -1 smbclient2-stdout || failed=$(expr $failed + 1)
+
+testit_grep_count "step5: smbstatus 2 session" '^2$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+# Only one connection was registered with the public address
+testit_grep_count "step5: ctdb_ip" "^${CTDB_IFACE_IP} 0\$" 1 ctdb_ip || failed=$(expr $failed + 1)
+testit_grep_count "step5: ctdb_gettickles NUM" "Num connections: 1" 1 ctdb_gettickles || failed=$(expr $failed + 1)
+testit_grep_count "step5: ctdb_gettickles DST" "DST: ${CTDB_IFACE_IP}" 1 ctdb_gettickles || failed=$(expr $failed + 1)
+
+unset CLI_FORCE_INTERACTIVE
+
+kill $CLIENT1_PID
+rm -f smbclient1-stdin smbclient1-stdout smbclient1-stderr
+
+testit "step6: sleep 1 second" true || failed=$(expr $failed + 1)
+sleep 1
+
+testit_grep_count "step6: smbstatus 1 session" '^1$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+testit_grep_count "step6: ctdb_ip" "^${CTDB_IFACE_IP} 0\$" 1 ctdb_ip || failed=$(expr $failed + 1)
+testit_grep_count "step6: ctdb_gettickles NUM" "Num connections: 1" 1 ctdb_gettickles || failed=$(expr $failed + 1)
+testit_grep_count "step6: ctdb_gettickles DST" "DST: ${CTDB_IFACE_IP}" 1 ctdb_gettickles || failed=$(expr $failed + 1)
+
+testit "step7: ctdb_reload_public_ips" ctdb_reload_public_ips || failed=$(expr $failed + 1)
+
+testit_grep_count "step7: ctdb_ip" "${CTDB_IFACE_IP}" 0 ctdb_ip || failed=$(expr $failed + 1)
+testit_expect_failure_grep "step3: ctdb_gettickles" "Control GET_TCP_TICKLE_LIST failed" ctdb_gettickles || failed=$(expr $failed + 1)
+
+testit "step7: sleep 2 second" true || failed=$(expr $failed + 1)
+sleep 2
+
+testit_grep_count "step7: smbstatus 0 sessions" '^0$' 1 smbstatus_num_sessions || failed=$(expr $failed + 1)
+
+kill $CLIENT2_PID
+rm -f smbclient2-stdin smbclient2-stdout smbclient2-stderr
+
+testok "$0" "$failed"
index adea6096c030bc35b18731fbae53b6843ccd969d..72828c73f1438d6815c792c00bd25a814fefb77e 100755 (executable)
@@ -1633,6 +1633,12 @@ plantestsuite("samba3.blackbox.smbXsrv_client_cross_node", "clusteredmember:loca
                configuration,
                '$CTDB_SERVER_NAME_NODE0', '$CTDB_SERVER_NAME_NODE1',
                "tmp"])
+plantestsuite("samba3.blackbox.smbXsrv_client_ctdb_registered_ips", "clusteredmember:local",
+              [os.path.join(samba3srcdir,
+                            "script/tests/test_smbXsrv_client_ctdb_registered_ips.sh"),
+               configuration,
+               '$CTDB_IFACE_IP',
+               "tmp"])
 plantestsuite("samba3.blackbox.registry_share", "clusteredmember",
               [os.path.join(samba3srcdir,
                             "script/tests/test_registry_share.sh"),