Path: blob/main/tests/sys/kern/fdgrowtable_test.c
103859 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2020 Rob Wing4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/2627#include <sys/param.h>28#include <sys/filedesc.h>29#include <sys/queue.h>30#include <sys/sysctl.h>31#include <sys/user.h>32#include <sys/wait.h>3334#include <atf-c.h>35#include <fcntl.h>36#include <signal.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142/* linked libraries */43#include <kvm.h>44#include <libutil.h>45#include <libprocstat.h>46#include <pthread.h>4748/* test-case macro */49#define AFILE "afile"5051/*52* The following macros, struct freetable, struct fdescenttbl053* and struct filedesc0 are copied from sys/kern/kern_descrip.c54*/55#define NDFILE 2056#define NDSLOTSIZE sizeof(NDSLOTTYPE)57#define NDENTRIES (NDSLOTSIZE * __CHAR_BIT)58#define NDSLOT(x) ((x) / NDENTRIES)59#define NDBIT(x) ((NDSLOTTYPE)1 << ((x) % NDENTRIES))60#define NDSLOTS(x) (((x) + NDENTRIES - 1) / NDENTRIES)6162struct freetable {63struct fdescenttbl *ft_table;64SLIST_ENTRY(freetable) ft_next;65};6667struct fdescenttbl0 {68int fdt_nfiles;69struct filedescent fdt_ofiles[NDFILE];70};7172struct filedesc0 {73struct filedesc fd_fd;74SLIST_HEAD(, freetable) fd_free;75struct fdescenttbl0 fd_dfiles;76NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];77};7879static void80openfiles(int n)81{82int i, fd;8384ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);85close(fd);86for (i = 0; i < n; i++)87ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1);88}8990/*91* Get a count of the old file descriptor tables on the freelist.92*/93static int94old_tables(kvm_t *kd, struct kinfo_proc *kp)95{96struct filedesc0 fdp0;97struct freetable *ft, tft;98int counter;99100counter = 0;101102ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0);103104SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) {105ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 );106ft = &tft;107counter++;108}109110return (counter);111}112113/*114* The returning struct kinfo_proc stores kernel addresses that will be115* used by kvm_read to retrieve information for the current process.116*/117static struct kinfo_proc *118read_kinfo(kvm_t *kd)119{120struct kinfo_proc *kp;121int procs_found;122123ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL);124ATF_REQUIRE(procs_found == 1);125126return (kp);127}128129/*130* Test a single threaded process that doesn't have a shared131* file descriptor table. The old tables should be freed.132*/133ATF_TC(free_oldtables);134ATF_TC_HEAD(free_oldtables, tc)135{136atf_tc_set_md_var(tc, "require.user", "root");137}138139ATF_TC_BODY(free_oldtables, tc)140{141kvm_t *kd;142struct kinfo_proc *kp;143144ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);145openfiles(128);146kp = read_kinfo(kd);147ATF_CHECK(old_tables(kd,kp) == 0);148}149150static _Noreturn void *151exec_thread(void *args)152{153for (;;)154sleep(1);155}156157/*158* Test a process with two threads that doesn't have a shared file159* descriptor table. The old tables should not be freed.160*/161ATF_TC(oldtables_shared_via_threads);162ATF_TC_HEAD(oldtables_shared_via_threads, tc)163{164atf_tc_set_md_var(tc, "require.user", "root");165}166167ATF_TC_BODY(oldtables_shared_via_threads, tc)168{169pid_t child;170kvm_t *kd;171struct kinfo_proc *kp;172pthread_t thread;173174if ((child = rfork(RFPROC | RFCFDG)) > 0) {175pid_t wpid;176int status;177178wpid = waitpid(child, &status, 0);179ATF_REQUIRE(wpid == child);180ATF_REQUIRE(WIFEXITED(status));181ATF_REQUIRE(WEXITSTATUS(status) == EXIT_SUCCESS);182return;183}184185#define REQUIRE(expression) do { \186if (!(expression)) \187exit(EXIT_FAILURE); \188} while (0)189190REQUIRE(child == 0);191192REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);193REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);194195openfiles(128);196197kp = read_kinfo(kd);198REQUIRE(kp->ki_numthreads > 1);199REQUIRE(old_tables(kd,kp) > 1);200201REQUIRE(pthread_cancel(thread) == 0);202REQUIRE(pthread_join(thread, NULL) == 0);203#undef REQUIRE204205exit(EXIT_SUCCESS);206}207208/*209* Get the reference count of a file descriptor table.210*/211static int212filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp)213{214struct filedesc fdp;215216ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0);217218return (fdp.fd_refcnt);219}220221/*222* Test a single threaded process that shares a file descriptor223* table with another process. The old tables should not be freed.224*/225ATF_TC(oldtables_shared_via_process);226ATF_TC_HEAD(oldtables_shared_via_process, tc)227{228atf_tc_set_md_var(tc, "require.user", "root");229}230231ATF_TC_BODY(oldtables_shared_via_process, tc)232{233kvm_t *kd;234struct kinfo_proc *kp;235int status;236pid_t child, wpid;237238ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);239240/* share the file descriptor table */241ATF_REQUIRE((child = rfork(RFPROC)) != -1);242243if (child == 0) {244openfiles(128);245raise(SIGSTOP);246exit(127);247}248249/* let parent process open some files too */250openfiles(128);251252/* get current status of child */253wpid = waitpid(child, &status, WUNTRACED);254ATF_REQUIRE(wpid == child);255256/* child should be stopped */257ATF_REQUIRE(WIFSTOPPED(status));258259/*260* We want to read kernel data261* before the child exits262* otherwise we'll lose a reference count263* to the file descriptor table264*/265kp = read_kinfo(kd);266267ATF_CHECK(filedesc_refcnt(kd,kp) > 1);268ATF_CHECK(old_tables(kd,kp) > 1);269270kill(child, SIGCONT);271272/* child should have exited */273wpid = waitpid(child, &status, 0);274ATF_REQUIRE(wpid == child);275ATF_REQUIRE(WIFEXITED(status));276ATF_REQUIRE(WEXITSTATUS(status) == 127);277}278279ATF_TP_ADD_TCS(tp)280{281ATF_TP_ADD_TC(tp, free_oldtables);282ATF_TP_ADD_TC(tp, oldtables_shared_via_threads);283ATF_TP_ADD_TC(tp, oldtables_shared_via_process);284return (atf_no_error());285}286287288