From patchwork Wed Mar 11 21:34:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 11432857 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C7BF892C for ; Wed, 11 Mar 2020 21:35:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 36F8C2074B for ; Wed, 11 Mar 2020 21:35:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1583962542; bh=Xra8VPbIBIEvxvLBaQYJ03gPqE0cmtBpu/Xkd9UujSI=; h=From:To:Cc:Subject:Date:List-ID:From; b=f0OU70W5fRzBNxuRFjrw7cXfMJYV126WdyHpDOBLGlMOs83at1/mXTPJTEbSq5fb6 ZwfYdrWeoJ/VF2Vb2u+8PTvxDOszbXVM6WO8qRfAKasjpp/7Mq2ywqeTnRdlUHjPdR dTyaboOvkiMahreYlb1lEO/lUQ85tWLTA33HoCbw= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729437AbgCKVfl (ORCPT ); Wed, 11 Mar 2020 17:35:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:47744 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726684AbgCKVfk (ORCPT ); Wed, 11 Mar 2020 17:35:40 -0400 Received: from ebiggers-linuxstation.mtv.corp.google.com (unknown [104.132.1.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 0601220749; Wed, 11 Mar 2020 21:35:39 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1583962540; bh=Xra8VPbIBIEvxvLBaQYJ03gPqE0cmtBpu/Xkd9UujSI=; h=From:To:Cc:Subject:Date:From; b=I7iDG+VAKx4/wbfky7u3Jf2rVtgfm3KY3z9QIMttGZf1cEnuiPDJOv2a6MMxvN46a faqLhyO3aMFkMLDVaCMt2cfFfOcYij6WX4IBWt+ssVPutNjEmAN+b0AAkNbs1ClPJS gKeo+AjMbur0L1+INm+EQiFFEZ3WrHxiDE3PRkyY= From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-fscrypt@vger.kernel.org Subject: [xfstests PATCH v2] generic: test fscrypt key eviction racing with inode dirtying Date: Wed, 11 Mar 2020 14:34:53 -0700 Message-Id: <20200311213453.218975-1-ebiggers@kernel.org> X-Mailer: git-send-email 2.25.1.481.gfbce0eb801-goog MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Add a regression test for a bug in the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl fixed by commit 2b4eae95c736 ("fscrypt: don't evict dirty inodes after removing key"). This ioctl is also tested by generic/580 and generic/581, but they didn't cover the case where this bug occurs. This test detects the bug on ext4, f2fs, and ubifs. The multi-threaded part of the test actually still fails on ubifs even with the fix, due to another kernel bug which I'm working on fixing. Signed-off-by: Eric Biggers --- Changed v1 => v2: - Added upstream commit ID. - Removed RFC tag. - Removed some unnecessary output suppressions. tests/generic/900 | 115 ++++++++++++++++++++++++++++++++++++++++++ tests/generic/900.out | 10 ++++ tests/generic/group | 1 + 3 files changed, 126 insertions(+) create mode 100755 tests/generic/900 create mode 100644 tests/generic/900.out diff --git a/tests/generic/900 b/tests/generic/900 new file mode 100755 index 00000000..8c8671ea --- /dev/null +++ b/tests/generic/900 @@ -0,0 +1,115 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2020 Google LLC +# +# FS QA Test No. 900 +# +# Regression test for a bug in the FS_IOC_REMOVE_ENCRYPTION_KEY ioctl fixed by +# commit 2b4eae95c736 ("fscrypt: don't evict dirty inodes after removing key"). +# This bug could cause writes to encrypted files to be lost if they raced with +# the corresponding fscrypt master key being removed. With f2fs, this bug could +# also crash the kernel. +# +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() +{ + # Stop all subprocesses. + touch $tmp.done + wait + + 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 -v 2 +_require_command "$KEYCTL_PROG" keyctl + +_scratch_mkfs_encrypted &>> $seqres.full +_scratch_mount + +dir=$SCRATCH_MNT/dir +runtime=$((4 * TIME_FACTOR)) + +# Create an encrypted directory. +mkdir $dir +_set_encpolicy $dir $TEST_KEY_IDENTIFIER +_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" + +# Start with a single-threaded reproducer: +echo -e "\n# Single-threaded reproducer" +# Keep a fd open to a file past its fscrypt master key being removed. +exec 3>$dir/file +_rm_enckey $SCRATCH_MNT $TEST_KEY_IDENTIFIER +# Write to and close the open fd. +echo contents >&3 +exec 3>&- +# Drop any dentries which might be pinning the inode for "file". +echo 2 > /proc/sys/vm/drop_caches +# In buggy kernels, the inode for "file" was evicted despite the dirty data, +# causing the dirty data to be lost. Check whether the write made it through. +_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" +cat $dir/file +rm -f $dir/file + +# Also run a multi-threaded reproducer. This is included for good measure, as +# this type of thing tends to be good for finding other bugs too. +echo -e "\n# Multi-threaded reproducer" +touch $dir/file + +# One process add/removes the encryption key repeatedly. +( + while [ ! -e $tmp.done ]; do + _add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" > /dev/null + _rm_enckey $SCRATCH_MNT $TEST_KEY_IDENTIFIER &> /dev/null + done +) & + +# Another process repeatedly tries to append to the encrypted file. The file is +# re-opened each time, so that there are chances for the inode to be evicted. +# Failures to open the file due to the key being removed are ignored. +( + touch $tmp.expected + while [ ! -e $tmp.done ]; do + if sh -c "echo -n X >> $dir/file" 2>/dev/null; then + # Keep track of the expected file contents. + echo -n X >> $tmp.expected + fi + done +) & + +# Run for a while. +sleep $runtime + +# Stop all subprocesses. +touch $tmp.done +wait + +# Make sure no writes were lost. +_add_enckey $SCRATCH_MNT "$TEST_RAW_KEY" > /dev/null +stat $tmp.expected >> $seqres.full +stat $dir/file >> $seqres.full +cmp $tmp.expected $dir/file + +echo "Multi-threaded reproducer done" + +# success, all done +status=0 +exit diff --git a/tests/generic/900.out b/tests/generic/900.out new file mode 100644 index 00000000..14fde4c8 --- /dev/null +++ b/tests/generic/900.out @@ -0,0 +1,10 @@ +QA output created by 900 +Added encryption key with identifier 69b2f6edeee720cce0577937eb8a6751 + +# Single-threaded reproducer +Removed encryption key with identifier 69b2f6edeee720cce0577937eb8a6751, but files still busy +Added encryption key with identifier 69b2f6edeee720cce0577937eb8a6751 +contents + +# Multi-threaded reproducer +Multi-threaded reproducer done diff --git a/tests/generic/group b/tests/generic/group index dc95b77b..0852fc31 100644 --- a/tests/generic/group +++ b/tests/generic/group @@ -595,3 +595,4 @@ 591 auto quick rw pipe splice 592 auto quick encrypt 593 auto quick encrypt +900 auto quick encrypt