WSL/test/linux/unit_tests/select.c
WSL Team 697572d664 Initial open source commit for WSL.
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016.

The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021:

Number of commits on the main branch: 2930
Number of contributors: 31

Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
2025-05-15 12:09:45 -07:00

293 lines
No EOL
8.2 KiB
C

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
select.c
Abstract:
This file is the select system call test.
--*/
#include "lxtcommon.h"
#include "unittests.h"
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#if defined(__aarch64__)
//
// ARM64 glibc converts select to pselect.
//
#define __ARCH_WANT_SYSCALL_DEPRECATED
#include <linux/unistd.h>
#define SYS_select __NR_select
#endif
#define LxtSelect(_nfds, _readfds, _writefds, _exceptfds, _timeout) \
(syscall(SYS_select, _nfds, _readfds, _writefds, _exceptfds, _timeout))
#define LXT_NAME "Select"
#define LXT_SELECT_TEST_FILE "/data/test/select_test.bin"
#define LXT_SHORT_TIMEOUT 1
LXT_VARIATION_HANDLER SelectFdBufferSize;
LXT_VARIATION_HANDLER SelectMaxNfds;
//
// Global constants
//
static const LXT_VARIATION g_LxtVariations[] = {{"FD buffer sizes", SelectFdBufferSize}, {"Max nfds", SelectMaxNfds}};
int SelectTestEntry(int Argc, char* Argv[])
/*++
--*/
{
LXT_ARGS Args;
int Result;
LxtCheckResult(LxtInitialize(Argc, Argv, &Args, LXT_NAME));
LxtCheckResult(LxtRunVariations(&Args, g_LxtVariations, LXT_COUNT_OF(g_LxtVariations)));
ErrorExit:
LxtUninitialize();
return !LXT_SUCCESS(Result);
}
int SelectFdBufferSize(PLXT_ARGS Args)
{
unsigned char* Address;
int Fds[sizeof(unsigned long) * 8 * 2];
int Index;
void* MapResult;
int Result;
fd_set* ReadSet;
unsigned char* ReadSetBuffer;
int ReadSetCount;
struct timeval Timeout;
Address = NULL;
memset(Fds, -1, sizeof(Fds));
Result = LXT_RESULT_FAILURE;
//
// Open files that will be used for select.
//
for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)
{
LxtCheckErrno(Fds[Index] = open(LXT_SELECT_TEST_FILE, O_RDWR | O_CREAT, S_IRWXU));
}
//
// Create a read\write page followed by a no access page. The readset buffer
// will be adjusted so it is just before the no access page.
//
LxtCheckMapErrno(Address = mmap(NULL, PAGE_SIZE * 2, (PROT_READ | PROT_WRITE), (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0));
LxtCheckErrno(mprotect(Address + PAGE_SIZE, PAGE_SIZE, PROT_NONE));
//
// A ReadSetCount of 0 should not touch the buffer.
//
ReadSetCount = 0;
ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned char);
memset(ReadSetBuffer, -1, sizeof(unsigned char));
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));
LxtCheckEqual(*ReadSetBuffer, (unsigned char)-1, "%c");
//
// Test select with different sized buffers that have all of the bits set
// and a ReadSetCount of 1. The expectation is that the write will fail if
// the buffer is less an an unsigned long. If larger, the values will be
// zeroed out to an unsigned long but not more.
//
ReadSetCount = 1;
ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned char);
memset(ReadSetBuffer, -1, sizeof(unsigned char));
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrnoFailure(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout), EFAULT);
LxtCheckEqual(*ReadSetBuffer, (unsigned char)-1, "%c");
if (sizeof(unsigned int) != sizeof(unsigned long))
{
ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned int);
memset(ReadSetBuffer, -1, sizeof(unsigned int));
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrnoFailure(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout), EFAULT);
LxtCheckEqual(*(unsigned int*)ReadSetBuffer, (unsigned int)-1, "%d");
}
ReadSetBuffer = (Address + PAGE_SIZE) - sizeof(unsigned long);
memset(ReadSetBuffer, -1, sizeof(unsigned long));
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));
LxtCheckEqual(*((unsigned long*)ReadSetBuffer), 0, "%ld");
ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);
memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));
LxtCheckEqual(*((unsigned long*)ReadSetBuffer), 0, "%ld");
LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), -1, "%ld");
//
// Test with a ReadSetCount of exactly the number of bits in an unsigned
// long.
//
ReadSetCount = sizeof(unsigned long) * 8;
ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);
memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));
LxtCheckNotEqual(*((unsigned long*)ReadSetBuffer), -1, "%ld");
LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), -1, "%ld");
//
// And again with one larger.
//
ReadSetCount = sizeof(unsigned long) * 8 + 1;
ReadSetBuffer = (Address + PAGE_SIZE) - (sizeof(unsigned long) * 2);
memset(ReadSetBuffer, -1, sizeof(unsigned long) * 2);
ReadSet = (fd_set*)ReadSetBuffer;
memset(&Timeout, 0, sizeof(Timeout));
LxtCheckErrno(LxtSelect(ReadSetCount, ReadSet, NULL, NULL, &Timeout));
LxtCheckNotEqual(*((unsigned long*)ReadSetBuffer), -1, "%ld");
LxtCheckEqual(*(((unsigned long*)ReadSetBuffer + 1)), 1, "%ld");
Result = LXT_RESULT_SUCCESS;
ErrorExit:
for (Index = 0; Index < LXT_COUNT_OF(Fds); ++Index)
{
if (Fds[Index] != -1)
{
LxtClose(Fds[Index]);
}
}
if (Address != NULL)
{
munmap(Address, PAGE_SIZE * 2);
}
return Result;
}
int SelectMaxNfds(PLXT_ARGS Args)
{
int Result;
int Fd;
int NumFd;
fd_set ReadSet;
struct timeval Timeout;
struct rlimit MaxFds;
//
// Initialize locals.
//
Fd = -1;
Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
//
// Open a file that will be used for select.
//
LxtCheckErrno(Fd = open(LXT_SELECT_TEST_FILE, (O_RDWR | O_CREAT), S_IRWXU));
//
// Create the select set and set the FD in it.
//
FD_ZERO(&ReadSet);
FD_SET(Fd, &ReadSet);
//
// -ve values for 'nfds' should return EINVAL.
//
LxtCheckErrnoFailure(select(-1, &ReadSet, NULL, NULL, &Timeout), EINVAL);
LxtLogInfo("Waiting on select to succeed..");
//
// Set 'nfds' to > FD_SETSIZE. Kernel seems to ignore anything above the
// current ulimit(RLIMIT_NOFILE).
//
// N.B The value chosen for 'nfds' is > nr_open (which is the upper limit
// for RLIMIT_NOFILE and by default set to 1048576). As per the man
// page, EINVAL is returned if nfds exceeds the RLIMIT_NOFILE resource
// limit, but that doesn't seem to be case.
//
LxtCheckErrno(NumFd = select(1048576 + 100, &ReadSet, NULL, NULL, &Timeout));
LxtCheckEqual(NumFd, 1, "%d");
if (FD_ISSET(Fd, &ReadSet) == 0)
{
LxtLogError(
"Select was satisfied but file descriptor is not set "
"for read!");
Result = -1;
goto ErrorExit;
}
//
// Provide a bad file descriptor to select. As per the man page kernel
// ignores any FD > maximum FD currently opened by the process. But,
// in testing it seems like it does perform that check.
//
// N.B Below assumes that 200 > #FD's opened by the process.
//
FD_ZERO(&ReadSet);
FD_SET(200, &ReadSet);
//
// Kernel ignores anything above FD_SETSIZE.
//
LxtCheckErrnoFailure(select(201, &ReadSet, NULL, NULL, &Timeout), EBADF);
Result = LXT_RESULT_SUCCESS;
ErrorExit:
if (Fd != -1)
{
close(Fd);
}
return Result;
}