generic: test encryption key revocation during concurrent I/O
authorEric Biggers <ebiggers@google.com>
Fri, 31 Mar 2017 19:48:36 +0000 (12:48 -0700)
committerEryu Guan <eguan@redhat.com>
Sat, 1 Apr 2017 06:05:32 +0000 (14:05 +0800)
Add a test which revokes a keyring key while other processes are
performing I/O on an encrypted file that was "unlocked" using that key.
The crashes unpatched kernels with filesystem encryption enabled.

This bug was present in kernels v4.2 and later.  It has been fixed in
v4.11-rc4, v4.10.7, v4.9.20, and v4.4.59.

Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Jaegeuk Kim <jaegeuk@kernel.org>
Cc: Richard Weinberger <richard@nod.at>
Cc: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>
Reviewed-by: Eryu Guan <eguan@redhat.com>
Signed-off-by: Eryu Guan <eguan@redhat.com>
common/encrypt
tests/generic/421 [new file with mode: 0755]
tests/generic/421.out [new file with mode: 0644]
tests/generic/group

index f09104d117c79b1c60a13ce1b6a2bf548ead0909..85f71d5b4c36fe3e24f9e2465fa5a08b80e420d1 100644 (file)
@@ -144,3 +144,11 @@ _unlink_encryption_key()
        local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
        $KEYCTL_PROG unlink $keyid >>$seqres.full
 }
+
+# Revoke an encryption key from the keyring, given its key descriptor.
+_revoke_encryption_key()
+{
+       local keydesc=$1
+       local keyid=$($KEYCTL_PROG search @s logon $FSTYP:$keydesc)
+       $KEYCTL_PROG revoke $keyid >>$seqres.full
+}
diff --git a/tests/generic/421 b/tests/generic/421
new file mode 100755 (executable)
index 0000000..1b2f66f
--- /dev/null
@@ -0,0 +1,110 @@
+#! /bin/bash
+# FS QA Test generic/421
+#
+# Test revoking an encryption key during concurrent I/O.  Regression test for
+# 1b53cf9815bb ("fscrypt: remove broken support for detecting keyring key
+# revocation").
+#
+#-----------------------------------------------------------------------
+# Copyright (c) 2017 Google, Inc.  All Rights Reserved.
+#
+# Author: Eric Biggers <ebiggers@google.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#-----------------------------------------------------------------------
+#
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1       # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+. ./common/encrypt
+
+# remove previous $seqres.full before test
+rm -f $seqres.full
+
+# real QA test starts here
+_supported_fs generic
+_supported_os Linux
+_require_scratch_encryption
+_require_xfs_io_command "set_encpolicy"
+_require_command "$KEYCTL_PROG" keyctl
+
+_new_session_keyring
+_scratch_mkfs_encrypted &>> $seqres.full
+_scratch_mount
+
+dir=$SCRATCH_MNT/encrypted_dir
+file=$dir/file
+
+# 4 processes, 2 MB per process
+nproc=4
+slice=2
+
+# Create an encrypted file and sync its data to disk.
+rm -rf $dir
+mkdir $dir
+keydesc=$(_generate_encryption_key)
+$XFS_IO_PROG -c "set_encpolicy $keydesc" $dir
+$XFS_IO_PROG -f $file -c "pwrite 0 $((nproc*slice))M" -c "fsync" > /dev/null
+
+# Create processes to read from the encrypted file.  Use fadvise to wipe the
+# pagecache before each read, ensuring that each read actually does decryption.
+for ((proc = 0; proc < nproc; proc++)); do
+       (
+               range="$((proc * slice))M ${slice}M"
+               while [ ! -e $tmp.done ]; do
+                       $XFS_IO_PROG $file -c "fadvise -d $range" \
+                                          -c "pread $range" &> /dev/null
+               done
+       ) &
+done
+
+# Wait a second for the readers to start up.
+sleep 1
+
+# Revoke the encryption key.
+keyid=$(_revoke_encryption_key $keydesc)
+
+# Now try to open the file again.  In buggy kernels this caused concurrent
+# readers to crash with a NULL pointer dereference during decryption.
+#
+# Note that the fix also made filenames stop "immediately" reverting to their
+# ciphertext on key revocation.  Therefore, the name of the file we're opening
+# here may be in either plaintext or ciphertext depending on the kernel version,
+# and ciphertext names are unpredictable anyway, so just use 'find' to find it.
+cat $(find $dir -type f) > /dev/null
+
+# Wait for readers to exit
+touch $tmp.done
+wait
+
+# success, all done
+echo "Didn't crash!"
+status=0
+exit
diff --git a/tests/generic/421.out b/tests/generic/421.out
new file mode 100644 (file)
index 0000000..a317c41
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 421
+Didn't crash!
index 91da46abc553cc62e3b2b15840a179b56b09e288..3c7c5e4b7427bbe0a041c39f7ee0b7ab19c9f278 100644 (file)
 418 auto rw
 419 auto quick encrypt
 420 auto quick punch
+421 auto quick encrypt dangerous