~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/openat2/rename_attack_test.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Author: Aleksa Sarai <cyphar@cyphar.com>
  4  * Copyright (C) 2018-2019 SUSE LLC.
  5  */
  6 
  7 #define _GNU_SOURCE
  8 #include <errno.h>
  9 #include <fcntl.h>
 10 #include <sched.h>
 11 #include <sys/stat.h>
 12 #include <sys/types.h>
 13 #include <sys/mount.h>
 14 #include <sys/mman.h>
 15 #include <sys/prctl.h>
 16 #include <signal.h>
 17 #include <stdio.h>
 18 #include <stdlib.h>
 19 #include <stdbool.h>
 20 #include <string.h>
 21 #include <syscall.h>
 22 #include <limits.h>
 23 #include <unistd.h>
 24 
 25 #include "../kselftest.h"
 26 #include "helpers.h"
 27 
 28 /* Construct a test directory with the following structure:
 29  *
 30  * root/
 31  * |-- a/
 32  * |   `-- c/
 33  * `-- b/
 34  */
 35 int setup_testdir(void)
 36 {
 37         int dfd;
 38         char dirname[] = "/tmp/ksft-openat2-rename-attack.XXXXXX";
 39 
 40         /* Make the top-level directory. */
 41         if (!mkdtemp(dirname))
 42                 ksft_exit_fail_msg("setup_testdir: failed to create tmpdir\n");
 43         dfd = open(dirname, O_PATH | O_DIRECTORY);
 44         if (dfd < 0)
 45                 ksft_exit_fail_msg("setup_testdir: failed to open tmpdir\n");
 46 
 47         E_mkdirat(dfd, "a", 0755);
 48         E_mkdirat(dfd, "b", 0755);
 49         E_mkdirat(dfd, "a/c", 0755);
 50 
 51         return dfd;
 52 }
 53 
 54 /* Swap @dirfd/@a and @dirfd/@b constantly. Parent must kill this process. */
 55 pid_t spawn_attack(int dirfd, char *a, char *b)
 56 {
 57         pid_t child = fork();
 58         if (child != 0)
 59                 return child;
 60 
 61         /* If the parent (the test process) dies, kill ourselves too. */
 62         E_prctl(PR_SET_PDEATHSIG, SIGKILL);
 63 
 64         /* Swap @a and @b. */
 65         for (;;)
 66                 renameat2(dirfd, a, dirfd, b, RENAME_EXCHANGE);
 67         exit(1);
 68 }
 69 
 70 #define NUM_RENAME_TESTS 2
 71 #define ROUNDS 400000
 72 
 73 const char *flagname(int resolve)
 74 {
 75         switch (resolve) {
 76         case RESOLVE_IN_ROOT:
 77                 return "RESOLVE_IN_ROOT";
 78         case RESOLVE_BENEATH:
 79                 return "RESOLVE_BENEATH";
 80         }
 81         return "(unknown)";
 82 }
 83 
 84 void test_rename_attack(int resolve)
 85 {
 86         int dfd, afd;
 87         pid_t child;
 88         void (*resultfn)(const char *msg, ...) = ksft_test_result_pass;
 89         int escapes = 0, other_errs = 0, exdevs = 0, eagains = 0, successes = 0;
 90 
 91         struct open_how how = {
 92                 .flags = O_PATH,
 93                 .resolve = resolve,
 94         };
 95 
 96         if (!openat2_supported) {
 97                 how.resolve = 0;
 98                 ksft_print_msg("openat2(2) unsupported -- using openat(2) instead\n");
 99         }
100 
101         dfd = setup_testdir();
102         afd = openat(dfd, "a", O_PATH);
103         if (afd < 0)
104                 ksft_exit_fail_msg("test_rename_attack: failed to open 'a'\n");
105 
106         child = spawn_attack(dfd, "a/c", "b");
107 
108         for (int i = 0; i < ROUNDS; i++) {
109                 int fd;
110                 char *victim_path = "c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../../c/../..";
111 
112                 if (openat2_supported)
113                         fd = sys_openat2(afd, victim_path, &how);
114                 else
115                         fd = sys_openat(afd, victim_path, &how);
116 
117                 if (fd < 0) {
118                         if (fd == -EAGAIN)
119                                 eagains++;
120                         else if (fd == -EXDEV)
121                                 exdevs++;
122                         else if (fd == -ENOENT)
123                                 escapes++; /* escaped outside and got ENOENT... */
124                         else
125                                 other_errs++; /* unexpected error */
126                 } else {
127                         if (fdequal(fd, afd, NULL))
128                                 successes++;
129                         else
130                                 escapes++; /* we got an unexpected fd */
131                 }
132                 close(fd);
133         }
134 
135         if (escapes > 0)
136                 resultfn = ksft_test_result_fail;
137         ksft_print_msg("non-escapes: EAGAIN=%d EXDEV=%d E<other>=%d success=%d\n",
138                        eagains, exdevs, other_errs, successes);
139         resultfn("rename attack with %s (%d runs, got %d escapes)\n",
140                  flagname(resolve), ROUNDS, escapes);
141 
142         /* Should be killed anyway, but might as well make sure. */
143         E_kill(child, SIGKILL);
144 }
145 
146 #define NUM_TESTS NUM_RENAME_TESTS
147 
148 int main(int argc, char **argv)
149 {
150         ksft_print_header();
151         ksft_set_plan(NUM_TESTS);
152 
153         test_rename_attack(RESOLVE_BENEATH);
154         test_rename_attack(RESOLVE_IN_ROOT);
155 
156         if (ksft_get_fail_cnt() + ksft_get_error_cnt() > 0)
157                 ksft_exit_fail();
158         else
159                 ksft_exit_pass();
160 }
161 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php