fix(stdio): correct position tracking on mode switch and free owned buffers on close
This commit is contained in:
@@ -25,6 +25,11 @@
|
||||
* changes the mode only.
|
||||
* 9. remove/rename lifecycle; tmpfile write+rewind+read.
|
||||
* 10. Level 2: tmpnam/ctermid/setbuffer/setlinebuf/fopen64.
|
||||
* 11. Read -> write mode switch: after a partial read the logical
|
||||
* position survives the buffered-data discard, so an overwrite
|
||||
* lands at the right offset.
|
||||
* 12. fclose leak check: 200 open/write/close cycles leave no live
|
||||
* allocations (probed with the allocator's heap walk).
|
||||
*
|
||||
* Failure mode (-f): fseek past EOF then fread returns 0 with feof set
|
||||
* and no crash; fopen of a nonexistent path returns NULL; fdopen(-1)
|
||||
@@ -50,6 +55,11 @@
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* The allocator's heap-walk consistency probe (hidden; linked in via
|
||||
* malloc.c). Returns the live block count, or (size_t)-1 on disagreement. */
|
||||
extern size_t
|
||||
__vlibc_malloc_check(void); // NOLINT(bugprone-reserved-identifier)
|
||||
|
||||
static int failures;
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. */
|
||||
@@ -586,6 +596,78 @@ name_scenario(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 11. Read -> write mode switch keeps the logical position. */
|
||||
static void
|
||||
modeswitch_scenario(void)
|
||||
{
|
||||
const char path[] = "/tmp/vlibc-test-stdio-modeswitch.bin";
|
||||
char got[16];
|
||||
FILE *f;
|
||||
unsigned long r;
|
||||
|
||||
f = fopen(path, "w+b");
|
||||
check(f != NULL, "modeswitch fopen w+b succeeds");
|
||||
if (f == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
check(fwrite("abcdefgh", 1, 8, f) == 8, "modeswitch writes the 8-byte seed");
|
||||
check(fflush(f) == 0, "modeswitch fflush returns 0");
|
||||
check(fseek(f, 0, SEEK_SET) == 0, "modeswitch seeks back to the start");
|
||||
check(fgetc(f) == 'a', "modeswitch reads 'a'");
|
||||
check(fgetc(f) == 'b', "modeswitch reads 'b' (6 bytes left buffered)");
|
||||
check(ftello(f) == 2, "modeswitch ftello == 2 after the two reads");
|
||||
check(fputc('X', f) == 'X', "modeswitch fputc('X') switches back to writing");
|
||||
check(ftello(f) == 3, "modeswitch ftello == 3 after the overwrite");
|
||||
check(fseeko(f, 0, SEEK_CUR) == 0, "modeswitch fseeko(0, SEEK_CUR) returns 0");
|
||||
check(ftello(f) == 3, "modeswitch ftello stays 3 after the no-op seek");
|
||||
check(fclose(f) == 0, "modeswitch fclose returns 0");
|
||||
|
||||
f = fopen(path, "rb");
|
||||
check(f != NULL, "modeswitch reopen rb succeeds");
|
||||
if (f != NULL)
|
||||
{
|
||||
r = fread(got, 1, 8, f);
|
||||
check(r == 8 && mem_eq((const unsigned char *)got, (const unsigned char *)"abXdefgh", 8),
|
||||
"modeswitch: the overwrite landed at offset 2 (\"abXdefgh\")");
|
||||
check(fclose(f) == 0, "modeswitch fclose after the verify returns 0");
|
||||
}
|
||||
check(remove(path) == 0, "remove deletes the modeswitch file");
|
||||
}
|
||||
|
||||
/* 12. fclose releases the owned buffer (no leak across many cycles). */
|
||||
static void
|
||||
fclose_leak_scenario(void)
|
||||
{
|
||||
const char path[] = "/tmp/vlibc-test-stdio-leak.txt";
|
||||
size_t before;
|
||||
size_t after;
|
||||
int i;
|
||||
|
||||
before = __vlibc_malloc_check();
|
||||
check(before != (size_t)-1, "allocator heap walk is consistent before the cycles");
|
||||
if (before == (size_t)-1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < 200; i++)
|
||||
{
|
||||
FILE *f = fopen(path, "w");
|
||||
|
||||
if (f == NULL || fputc('x', f) == EOF || fclose(f) != 0)
|
||||
{
|
||||
(void)fclose(f); /* safe when f == NULL */
|
||||
say(2, "FAIL: leak-cycle fopen/fputc/fclose\n");
|
||||
failures++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
after = __vlibc_malloc_check();
|
||||
check(after == before,
|
||||
"fclose frees the FILE struct and its owned buffer (no leak over 200 cycles)");
|
||||
check(remove(path) == 0, "remove deletes the leak-cycle file");
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/* 10. Level 2 additions. */
|
||||
@@ -702,6 +784,8 @@ main(int argc, char **argv)
|
||||
fdopen_scenario();
|
||||
freopen_scenario();
|
||||
name_scenario();
|
||||
modeswitch_scenario();
|
||||
fclose_leak_scenario();
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
level2_scenario();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user