Skip to content

Commit

Permalink
For macOS preadv and pwritev can fail with EINVAL when the total leng…
Browse files Browse the repository at this point in the history
…th of all vectors overflows a 32-bit integer.
  • Loading branch information
adamsitnik committed Nov 19, 2024
1 parent 6e5c47a commit 5cf908a
Showing 1 changed file with 29 additions and 19 deletions.
48 changes: 29 additions & 19 deletions src/native/libs/System.Native/pal_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1950,35 +1950,45 @@ int32_t SystemNative_PWrite(intptr_t fd, void* buffer, int32_t bufferSize, int64
}

#if (HAVE_PREADV || HAVE_PWRITEV) && !defined(TARGET_WASM)
static int GetAllowedVectorCount(int32_t vectorCount)
static int GetAllowedVectorCount(IOVector* vectors, int32_t vectorCount)
{
#if defined(_SC_IOV_MAX)
static volatile int s_iovMax = 0;

int iovMax = s_iovMax;
if (iovMax == 0)
{
// For macOS arm64 the IOV_MAX reports 1024, but fails with EINVAL for such inputs.
// That is why we prefer _SC_IOV_MAX over IOV_MAX.
iovMax = (int)sysconf(_SC_IOV_MAX);
s_iovMax = iovMax;
}
#elif defined(IOV_MAX)
int iovMax = IOV_MAX;
#if defined(IOV_MAX)
const int IovMax = IOV_MAX;
#else
// In theory all the platforms that we support define IOV_MAX,
// but we want to be extra safe and provde a fallback
// in case it turns out to not be true.
// 16 is low, but supported on every platform.
int iovMax = 16;
const int IovMax = 16;
#endif

int allowedCount = (int)vectorCount;

// We need to respect the limit of items that can be passed in iov.
// In case of writes, the managed code is responsible for handling incomplete writes.
// In case of reads, we simply returns the number of bytes read and it's up to the users.
if (iovMax < allowedCount)
if (IovMax < allowedCount)
{
allowedCount = IovMax;
}

#if defined(TARGET_APPLE)
// For macOS preadv and pwritev can fail with EINVAL when the total length
// of all vectors overflows a 32-bit integer.
size_t totalLength = 0;
for (int i = 0; i < allowedCount; i++)
{
allowedCount = iovMax;
totalLength += vectors[i].Count;

if (totalLength > INT_MAX && i > 1)
{
allowedCount = i - 1;
break;
}
}
#else
(void)vectors;
#endif

return allowedCount;
}
Expand All @@ -1992,7 +2002,7 @@ int64_t SystemNative_PReadV(intptr_t fd, IOVector* vectors, int32_t vectorCount,
int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM
int allowedVectorCount = GetAllowedVectorCount(vectorCount);
int allowedVectorCount = GetAllowedVectorCount(vectors, vectorCount);
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, allowedVectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
#else
int64_t current;
Expand Down Expand Up @@ -2033,7 +2043,7 @@ int64_t SystemNative_PWriteV(intptr_t fd, IOVector* vectors, int32_t vectorCount
int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PWRITEV && !defined(TARGET_WASM) // pwritev is buggy on WASM
int allowedVectorCount = GetAllowedVectorCount(vectorCount);
int allowedVectorCount = GetAllowedVectorCount(vectors, vectorCount);
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, allowedVectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
#else
int64_t current;
Expand Down

0 comments on commit 5cf908a

Please sign in to comment.