mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-21 01:12:26 +08:00
4945ffc88a
Add more tests for unusual situations fgets() might see: * zero size file * zero sized buffer * NULL buffer * NUL data * writable stream * closed stream Reviewed-by: Florian Weimer <fweimer@redhat.com>
254 lines
6.3 KiB
C
254 lines
6.3 KiB
C
/* Test for additional fgets error handling.
|
|
Copyright (C) 2024 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include <libc-diag.h>
|
|
#include <stdio.h>
|
|
#include <error.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <mcheck.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <support/support.h>
|
|
#include <support/check.h>
|
|
|
|
/* This avoids compiler warnings about passing NULL where a valid
|
|
pointer is expected. */
|
|
static void *volatile null = NULL;
|
|
|
|
/* Implementation of our FILE stream backend. */
|
|
|
|
static int bytes_read;
|
|
static int cookie_valid = 0;
|
|
struct Cookie {
|
|
const char *buffer;
|
|
int bufptr;
|
|
int bufsz;
|
|
};
|
|
|
|
#define VALIDATE_COOKIE() if (! cookie_valid) { \
|
|
FAIL ("call to %s after file closed", __FUNCTION__); \
|
|
return -1; \
|
|
}
|
|
|
|
static ssize_t
|
|
io_read (void *vcookie, char *buf, size_t size)
|
|
{
|
|
struct Cookie *cookie = (struct Cookie *) vcookie;
|
|
|
|
VALIDATE_COOKIE ();
|
|
|
|
if (size > cookie->bufsz - cookie->bufptr)
|
|
size = cookie->bufsz - cookie->bufptr;
|
|
|
|
memcpy (buf, cookie->buffer + cookie->bufptr, size);
|
|
cookie->bufptr += size;
|
|
bytes_read += size;
|
|
return size;
|
|
}
|
|
|
|
static ssize_t
|
|
io_write (void *vcookie, const char *buf, size_t size)
|
|
{
|
|
VALIDATE_COOKIE ();
|
|
FAIL_EXIT1 ("io_write called");
|
|
}
|
|
|
|
static int
|
|
io_seek (void *vcookie, off64_t *position, int whence)
|
|
{
|
|
VALIDATE_COOKIE ();
|
|
FAIL_EXIT1 ("io_seek called");
|
|
}
|
|
|
|
static int
|
|
io_clean (void *vcookie)
|
|
{
|
|
struct Cookie *cookie = (struct Cookie *) vcookie;
|
|
|
|
VALIDATE_COOKIE ();
|
|
|
|
cookie->buffer = NULL;
|
|
cookie->bufsz = 0;
|
|
cookie->bufptr = 0;
|
|
|
|
cookie_valid = 0;
|
|
free (cookie);
|
|
return 0;
|
|
}
|
|
|
|
cookie_io_functions_t io_funcs = {
|
|
.read = io_read,
|
|
.write = io_write,
|
|
.seek = io_seek,
|
|
.close = io_clean
|
|
};
|
|
|
|
FILE *
|
|
io_open (const char *buffer, int buflen, const char *mode, void **vcookie)
|
|
{
|
|
FILE *f;
|
|
struct Cookie *cookie;
|
|
|
|
cookie = (struct Cookie *) xcalloc (1, sizeof (struct Cookie));
|
|
*vcookie = cookie;
|
|
cookie_valid = 1;
|
|
|
|
cookie->buffer = buffer;
|
|
cookie->bufsz = buflen;
|
|
bytes_read = 0;
|
|
|
|
f = fopencookie (cookie, mode, io_funcs);
|
|
if (f == NULL)
|
|
FAIL_EXIT1 ("fopencookie failed");
|
|
|
|
clearerr (f);
|
|
return f;
|
|
}
|
|
|
|
/* The test cases. */
|
|
|
|
#define my_open(s,l,m) io_open (s, l, m, (void *) &cookie)
|
|
|
|
#define TEST_COMPARE_0x11(buf, len) \
|
|
TEST_COMPARE_BLOB (buf + (len), sizeof (buf) - (len), \
|
|
buf2, sizeof (buf) - (len));
|
|
|
|
#define check_flags(f, expected_eof, expected_err) \
|
|
{ \
|
|
if (expected_eof) \
|
|
TEST_VERIFY (feof (f) != 0); \
|
|
else \
|
|
TEST_VERIFY (feof (f) == 0); \
|
|
if (expected_err) \
|
|
TEST_VERIFY (ferror (f) != 0); \
|
|
else \
|
|
TEST_VERIFY (ferror (f) == 0); \
|
|
}
|
|
|
|
static int
|
|
do_test (void)
|
|
{
|
|
FILE *f;
|
|
struct Cookie *cookie;
|
|
char buf [10];
|
|
char buf2 [10];
|
|
char *returned_string;
|
|
|
|
memset (buf2, 0x11, sizeof (buf2));
|
|
|
|
printf ("testing base operation...\n");
|
|
f = my_open ("hello\n", 6, "r");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == buf);
|
|
TEST_COMPARE_BLOB (buf, bytes_read + 1, "hello\n\0", 7);
|
|
TEST_COMPARE_0x11 (buf, bytes_read + 1);
|
|
check_flags (f, 0, 0);
|
|
|
|
fclose (f);
|
|
|
|
printf ("testing zero size file...\n");
|
|
f = my_open ("hello\n", 0, "r");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
check_flags (f, 1, 0);
|
|
fclose (f);
|
|
|
|
printf ("testing zero size buffer...\n");
|
|
f = my_open ("hello\n", 6, "r");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, 0, f);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
check_flags (f, 0, 0);
|
|
fclose (f);
|
|
|
|
printf ("testing NULL buffer with empty stream...\n");
|
|
f = my_open ("hello\n", 0, "r");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
|
|
returned_string = fgets (null, sizeof (buf), f);
|
|
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
check_flags (f, 1, 0);
|
|
fclose (f);
|
|
|
|
printf ("testing embedded NUL...\n");
|
|
f = my_open ("hel\0lo\n", 7, "r");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == buf);
|
|
TEST_COMPARE_BLOB (buf, bytes_read + 1, "hel\0lo\n\0", 8);
|
|
TEST_COMPARE_0x11 (buf, bytes_read + 1);
|
|
check_flags (f, 0, 0);
|
|
fclose (f);
|
|
|
|
printf ("testing writable stream...\n");
|
|
f = my_open ("hel\0lo\n", 7, "w");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
check_flags (f, 0, 1);
|
|
fclose (f);
|
|
|
|
printf ("testing closed fd stream...\n");
|
|
int fd = open ("/dev/null", O_RDONLY);
|
|
f = fdopen (fd, "r");
|
|
close (fd);
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
check_flags (f, 0, 1);
|
|
fclose (f);
|
|
|
|
#ifdef IO_DEBUG
|
|
/* These tests only pass if glibc is built with -DIO_DEBUG, but are
|
|
included for reference. */
|
|
|
|
printf ("testing NULL descriptor...\n");
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, null);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
|
|
printf ("testing closed descriptor...\n");
|
|
f = my_open ("hello\n", 7, "r");
|
|
fclose (f);
|
|
memset (buf, 0x11, sizeof (buf));
|
|
returned_string = fgets (buf, sizeof (buf) - 1, f);
|
|
TEST_VERIFY (returned_string == NULL);
|
|
TEST_VERIFY (bytes_read == 0);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include <support/test-driver.c>
|