mirror of
https://github.com/microsoft/WSL.git
synced 2025-07-03 23:33:21 +00:00
293 lines
8.2 KiB
C
293 lines
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;
|
||
|
}
|