1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2010 Red Hat, Inc.
4 * Copyright (C) 2022 Cyril Hrubis <chrubis@suse.cz>
5 */
6
7 /*\
8 * [Description]
9 *
10 * A race in pid generation that causes pids to be reused immediately
11 *
12 * From the mainline commit 5fdee8c4a5e1800489ce61963208f8cc55e42ea1:
13 *
14 * A program that repeatedly forks and waits is susceptible to having
15 * the same pid repeated, especially when it competes with another
16 * instance of the same program. This is really bad for bash
17 * implementation. Furthermore, many shell scripts assume that pid
18 * numbers will not be used for some length of time.
19 *
20 * [Race Description]
21 * ---------------------------------------------------------------------
22 * A B
23 *
24 * // pid == offset == n // pid == offset == n + 1
25 * test_and_set_bit(offset, map->page)
26 * test_and_set_bit(offset, map->page);
27 * pid_ns->last_pid = pid;
28 * pid_ns->last_pid = pid;
29 * // pid == n + 1 is freed (wait())
30 *
31 * // Next fork()...
32 * last = pid_ns->last_pid; // == n
33 * pid = last + 1;
34 * ---------------------------------------------------------------------
35 */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "tst_test.h"
47
48 #define PID_MAX 32768
49 #define PID_MAX_STR "32768"
50 #define RETURN 256
51 #define MAX_ITERATIONS 1000000
52
53 /* The distance mod PIDMAX between two pids, where the first pid is
54 expected to be smaller than the second. */
pid_distance(pid_t first,pid_t second)55 static int pid_distance(pid_t first, pid_t second)
56 {
57 return (second + PID_MAX - first) % PID_MAX;
58 }
59
check(void)60 static void check(void)
61 {
62 pid_t prev_pid = 0;
63 pid_t pid;
64 int i, distance, reaped, status, retval;
65
66 for (i = 0; i < MAX_ITERATIONS; i++) {
67 retval = i % RETURN;
68
69 pid = SAFE_FORK();
70 if (!pid)
71 exit(retval);
72
73 if (prev_pid) {
74 distance = pid_distance(prev_pid, pid);
75 if (distance == 0) {
76 tst_res(TFAIL,
77 "Unexpected pid sequence: prev_pid=%i, pid=%i for iteration=%i",
78 prev_pid, pid, i);
79 return;
80 }
81 }
82
83 prev_pid = pid;
84
85 reaped = SAFE_WAITPID(pid, &status, 0);
86
87 if (reaped != pid) {
88 tst_res(TFAIL,
89 "Wrong pid %i returned from waitpid() expected %i",
90 reaped, pid);
91 return;
92 }
93
94 if (WEXITSTATUS(status) != retval) {
95 tst_res(TFAIL,
96 "Wrong process exit value %i expected %i",
97 WEXITSTATUS(status), retval);
98 return;
99 }
100
101 if (!tst_remaining_runtime()) {
102 tst_res(TINFO, "Runtime exhausted, exiting...");
103 break;
104 }
105 }
106
107 tst_res(TPASS, "%i pids forked, all passed", i);
108 }
109
110 static struct tst_test test = {
111 .needs_root = 1,
112 .forks_child = 1,
113 .max_runtime = 600,
114 .test_all = check,
115 .save_restore = (const struct tst_path_val[]) {
116 {"/proc/sys/kernel/pid_max", PID_MAX_STR, TST_SR_TBROK},
117 {}
118 },
119 .tags = (const struct tst_tag[]) {
120 {"linux-git", "5fdee8c4a5e1"},
121 {}
122 }
123 };
124