| #! /bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright 2020 Google LLC |
| # |
| # FS QA Test No. 595 |
| # |
| # 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. |
| # |
| . ./common/preamble |
| _begin_fstest auto quick encrypt |
| |
| # Override the default cleanup function. |
| _cleanup() |
| { |
| # Stop all subprocesses. |
| touch $tmp.done |
| wait |
| |
| rm -f $tmp.* |
| } |
| |
| # Import common functions. |
| . ./common/filter |
| . ./common/encrypt |
| |
| _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 |