unplugged-system/external/ltp/testcases/kernel/syscalls/fork/fork13.c

124 lines
3.0 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2010 Red Hat, Inc.
* Copyright (C) 2022 Cyril Hrubis <chrubis@suse.cz>
*/
/*\
* [Description]
*
* A race in pid generation that causes pids to be reused immediately
*
* From the mainline commit 5fdee8c4a5e1800489ce61963208f8cc55e42ea1:
*
* A program that repeatedly forks and waits is susceptible to having
* the same pid repeated, especially when it competes with another
* instance of the same program. This is really bad for bash
* implementation. Furthermore, many shell scripts assume that pid
* numbers will not be used for some length of time.
*
* [Race Description]
* ---------------------------------------------------------------------
* A B
*
* // pid == offset == n // pid == offset == n + 1
* test_and_set_bit(offset, map->page)
* test_and_set_bit(offset, map->page);
* pid_ns->last_pid = pid;
* pid_ns->last_pid = pid;
* // pid == n + 1 is freed (wait())
*
* // Next fork()...
* last = pid_ns->last_pid; // == n
* pid = last + 1;
* ---------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "tst_test.h"
#define PID_MAX 32768
#define PID_MAX_STR "32768"
#define RETURN 256
#define MAX_ITERATIONS 1000000
/* The distance mod PIDMAX between two pids, where the first pid is
expected to be smaller than the second. */
static int pid_distance(pid_t first, pid_t second)
{
return (second + PID_MAX - first) % PID_MAX;
}
static void check(void)
{
pid_t prev_pid = 0;
pid_t pid;
int i, distance, reaped, status, retval;
for (i = 0; i < MAX_ITERATIONS; i++) {
retval = i % RETURN;
pid = SAFE_FORK();
if (!pid)
exit(retval);
if (prev_pid) {
distance = pid_distance(prev_pid, pid);
if (distance == 0) {
tst_res(TFAIL,
"Unexpected pid sequence: prev_pid=%i, pid=%i for iteration=%i",
prev_pid, pid, i);
return;
}
}
prev_pid = pid;
reaped = SAFE_WAITPID(pid, &status, 0);
if (reaped != pid) {
tst_res(TFAIL,
"Wrong pid %i returned from waitpid() expected %i",
reaped, pid);
return;
}
if (WEXITSTATUS(status) != retval) {
tst_res(TFAIL,
"Wrong process exit value %i expected %i",
WEXITSTATUS(status), retval);
return;
}
if (!tst_remaining_runtime()) {
tst_res(TINFO, "Runtime exhausted, exiting...");
break;
}
}
tst_res(TPASS, "%i pids forked, all passed", i);
}
static struct tst_test test = {
.needs_root = 1,
.forks_child = 1,
.max_runtime = 600,
.test_all = check,
.save_restore = (const struct tst_path_val[]) {
{"/proc/sys/kernel/pid_max", PID_MAX_STR, TST_SR_TBROK},
{}
},
.tags = (const struct tst_tag[]) {
{"linux-git", "5fdee8c4a5e1"},
{}
}
};