generic: test encryption key revocation during concurrent I/O

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>
This commit is contained in:
Eric Biggers
2017-03-31 12:48:36 -07:00
committed by Eryu Guan
parent 9d0c046534
commit 98ac3be360
4 changed files with 121 additions and 0 deletions
+8
View 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
}
+110
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
QA output created by 421
Didn't crash!
+1
View File
@@ -423,3 +423,4 @@
418 auto rw
419 auto quick encrypt
420 auto quick punch
421 auto quick encrypt dangerous