Skip to content

Commit 6a9c5a3

Browse files
john-bell-unityJohn Bell
andauthored
Bugfix/macos oob read (#2066)
* Use separate storage for strings to avoid reading unallocated pages on MacOS (it doesn't always allocate the full 1024 chars for a dirent). * Removed some duplicated code * Clean up knock-ons from changing result type to be DirectoryEntry* * Bringing directory iteration more in line with other, similar code after discussion with Alex T --------- Co-authored-by: John Bell <john.bell@john.bell-QLHM>
1 parent aa3453b commit 6a9c5a3

File tree

2 files changed

+48
-32
lines changed

2 files changed

+48
-32
lines changed

external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.c

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ static void ConvertDirent(const struct dirent* entry, struct DirectoryEntry* out
346346
// We use Marshal.PtrToStringAnsi on the managed side, which takes a pointer to
347347
// the start of the unmanaged string. Give the caller back a pointer to the
348348
// location of the start of the string that exists in their own byte buffer.
349-
outputEntry->Name = entry->d_name;
349+
outputEntry->Name = strdup(entry->d_name);
350350
#if !defined(DT_UNKNOWN)
351351
// AIX has no d_type, and since we can't get the directory that goes with
352352
// the filename from ReadDir, we can't stat the file. Return unknown and
@@ -380,12 +380,22 @@ int32_t SystemNative_GetReadDirRBufferSize(void)
380380
#endif
381381
}
382382

383-
#if READDIR_SORT
384-
static int cmpstring(const void *p1, const void *p2)
383+
static int CompareByName(const void *p1, const void *p2)
385384
{
386-
return strcmp(((struct dirent*) p1)->d_name, ((struct dirent*) p2)->d_name);
385+
const struct DirectoryEntry* directoryEntry1 = (const struct DirectoryEntry*)p1;
386+
const struct DirectoryEntry* directoryEntry2 = (const struct DirectoryEntry*)p2;
387+
388+
// Sort NULL values to the end of the array. This can happen when
389+
// a file is deleted while GetFiles is called.
390+
if (directoryEntry1->Name == directoryEntry2->Name)
391+
return 0;
392+
if (directoryEntry1->Name == NULL)
393+
return 1;
394+
if (directoryEntry2->Name == NULL)
395+
return -1;
396+
397+
return strcmp(directoryEntry1->Name, directoryEntry2->Name);
387398
}
388-
#endif
389399

390400
// To reduce the number of string copies, the caller of this function is responsible to ensure the memory
391401
// referenced by outputEntry remains valid until it is read.
@@ -453,51 +463,55 @@ int32_t SystemNative_ReadDirR(struct DIRWrapper* dirWrapper, uint8_t* buffer, in
453463
(void)buffer; // unused
454464
(void)bufferSize; // unused
455465
errno = 0;
456-
457-
#if READDIR_SORT
458-
struct dirent* entry;
466+
bool endOfEntries = false;
459467

460468
if (!dirWrapper->result)
461469
{
470+
struct dirent* entry;
462471
size_t numEntries = 0;
463-
while ((entry = readdir(dirWrapper->dir))) numEntries++;
472+
while ((entry = readdir(dirWrapper->dir)))
473+
numEntries++;
464474
if (numEntries)
465475
{
466-
dirWrapper->result = malloc(numEntries * sizeof(struct dirent));
476+
// Use calloc to ensure the array is zero-initialized.
477+
dirWrapper->result = calloc(numEntries, sizeof(struct DirectoryEntry));
467478
dirWrapper->curIndex = 0;
479+
468480
#if HAVE_REWINDDIR
469481
rewinddir (dirWrapper->dir);
470482
#else
471483
closedir(dirWrapper->dir);
472484
dirWrapper->dir = opendir(dirWrapper->dirPath);
473485
#endif
486+
487+
// If we iterate fewer entries than exist because some files were deleted
488+
// since the time we computed numEntries above, that will be fine. Those
489+
// extra entries will be zero-initialized and will be sorted to the end
490+
// of the array by the qsort below.
474491
size_t index = 0;
475492
while ((entry = readdir(dirWrapper->dir)) && index < numEntries)
476493
{
477-
memcpy(&((struct dirent*)dirWrapper->result)[index], entry, sizeof(struct dirent));
494+
ConvertDirent(entry, &dirWrapper->result[index]);
478495
index++;
479496
}
480497

481-
qsort(dirWrapper->result, numEntries, sizeof(struct dirent), cmpstring);
482-
dirWrapper->numEntries = index;
498+
dirWrapper->numEntries = index;
499+
qsort(dirWrapper->result, dirWrapper->numEntries, sizeof(*dirWrapper->result), CompareByName);
483500
}
484501
}
485502

486503
if (dirWrapper->curIndex < dirWrapper->numEntries)
487504
{
488-
entry = &((struct dirent*)dirWrapper->result)[dirWrapper->curIndex];
505+
*outputEntry = dirWrapper->result[dirWrapper->curIndex];
489506
dirWrapper->curIndex++;
490507
}
491-
492508
else
493-
entry = NULL;
494-
495-
#else
496-
struct dirent* entry = readdir(dirWrapper->dir);
497-
#endif
509+
{
510+
endOfEntries = true;
511+
}
498512

499513
// 0 returned with null result -> end-of-stream
500-
if (entry == NULL)
514+
if (endOfEntries)
501515
{
502516
memset(outputEntry, 0, sizeof(*outputEntry)); // managed out param must be initialized
503517

@@ -510,7 +524,7 @@ int32_t SystemNative_ReadDirR(struct DIRWrapper* dirWrapper, uint8_t* buffer, in
510524
return -1;
511525
}
512526
#endif
513-
ConvertDirent(entry, outputEntry);
527+
514528
return 0;
515529
}
516530

@@ -523,13 +537,11 @@ struct DIRWrapper* SystemNative_OpenDir(const char* path)
523537

524538
struct DIRWrapper* ret = (struct DIRWrapper*)malloc(sizeof(struct DIRWrapper));
525539
ret->dir = dir;
526-
#if READDIR_SORT
527540
ret->result = NULL;
528541
ret->curIndex = 0;
529542
ret->numEntries = 0;
530543
#if !HAVE_REWINDDIR
531544
ret->dirPath = strdup(path);
532-
#endif
533545
#endif
534546
return ret;
535547
}
@@ -538,16 +550,24 @@ int32_t SystemNative_CloseDir(struct DIRWrapper* dirWrapper)
538550
{
539551
assert(dirWrapper != NULL);
540552
int32_t ret = closedir(dirWrapper->dir);
541-
#if READDIR_SORT
553+
542554
if (dirWrapper->result)
543-
free (dirWrapper->result);
555+
{
556+
for (size_t i = 0; i < dirWrapper->numEntries; i++)
557+
{
558+
free(dirWrapper->result[i].Name);
559+
}
560+
561+
free(dirWrapper->result);
562+
}
544563
dirWrapper->result = NULL;
564+
545565
#if !HAVE_REWINDDIR
546566
if (dirWrapper->dirPath)
547567
free(dirWrapper->dirPath);
548568
#endif
549569
free(dirWrapper);
550-
#endif
570+
551571
return ret;
552572
}
553573

external/corefx-bugfix/src/Native/Unix/System.Native/pal_io.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,19 +338,15 @@ enum NotifyEvents
338338
PAL_IN_ISDIR = 0x40000000,
339339
};
340340

341-
#define READDIR_SORT 1
342-
343341
struct DIRWrapper
344342
{
345343
DIR* dir;
346-
#if READDIR_SORT
347-
void* result;
344+
struct DirectoryEntry* result;
348345
size_t curIndex;
349346
size_t numEntries;
350347
#if !HAVE_REWINDDIR
351348
char* dirPath;
352349
#endif
353-
#endif
354350
};
355351

356352

0 commit comments

Comments
 (0)