| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright (c) 2017 Google, Inc. All Rights Reserved. |
| * Author: Eric Biggers <ebiggers@google.com> |
| * |
| * t_encrypted_d_revalidate |
| * |
| * Test that ->d_revalidate() for encrypted dentries doesn't oops the |
| * kernel by incorrectly not dropping out of RCU mode. To do this, try |
| * to look up a negative dentry while another thread deletes its parent |
| * directory. Fixed by commit 03a8bb0e53d9 ("ext4/fscrypto: avoid RCU |
| * lookup in d_revalidate"). |
| * |
| * This doesn't always reproduce reliably, but we give it a few seconds. |
| */ |
| |
| #include <errno.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #define TIMEOUT 10 |
| #define DIR_NAME "dir" |
| #define FILE_NAME DIR_NAME "/file" |
| |
| static volatile sig_atomic_t timed_out = 0; |
| |
| static void alarm_handler(int sig) |
| { |
| timed_out = 1; |
| } |
| |
| static void __attribute__((noreturn)) |
| die(int err, const char *msg) |
| { |
| fprintf(stderr, "ERROR: %s", msg); |
| if (err) |
| fprintf(stderr, ": %s", strerror(err)); |
| fputc('\n', stderr); |
| exit(1); |
| } |
| |
| static void *stat_thread(void *_arg) |
| { |
| struct stat stbuf; |
| |
| for (;;) { |
| if (stat(FILE_NAME, &stbuf) == 0) |
| die(0, "stat should have failed"); |
| if (errno != ENOENT) |
| die(errno, "stat"); |
| } |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| long ncpus; |
| long num_stat_threads; |
| long i; |
| struct stat stbuf; |
| |
| if (argc != 2) { |
| fprintf(stderr, "Usage: %s DIR\n", argv[0]); |
| return 2; |
| } |
| |
| if (chdir(argv[1]) != 0) |
| die(errno, "chdir"); |
| |
| ncpus = sysconf(_SC_NPROCESSORS_ONLN); |
| if (ncpus > 1) |
| num_stat_threads = ncpus - 1; |
| else |
| num_stat_threads = 1; |
| |
| for (i = 0; i < num_stat_threads; i++) { |
| pthread_t thread; |
| int err; |
| |
| err = pthread_create(&thread, NULL, stat_thread, NULL); |
| if (err) |
| die(err, "pthread_create"); |
| } |
| |
| if (signal(SIGALRM, alarm_handler) == SIG_ERR) |
| die(errno, "signal"); |
| |
| alarm(TIMEOUT); |
| |
| while (!timed_out) { |
| if (mkdir(DIR_NAME, 0777) != 0) |
| die(errno, "mkdir"); |
| if (stat(FILE_NAME, &stbuf) == 0) |
| die(0, "stat should have failed"); |
| if (errno != ENOENT) |
| die(errno, "stat"); |
| if (rmdir(DIR_NAME) != 0) |
| die(errno, "rmdir"); |
| } |
| |
| printf("t_encrypted_d_revalidate finished\n"); |
| return 0; |
| } |