WSL/test/linux/unit_tests/select.c

293 lines
8.2 KiB
C
Raw Permalink Normal View History

/*++
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;
}