diff mbox

libio: Fix fmemopen append mode failure (BZ# 20005)

Message ID 1461769021-3707-1-git-send-email-adhemerval.zanella@linaro.org
State New
Headers show

Commit Message

Adhemerval Zanella Netto April 27, 2016, 2:57 p.m. UTC
Based on previous issue pointed out by Andreas Schwab,  I noted fmemopen
with append mode is showing some issue.  The implementation does not
account the file position correctly. The following example shows the
failure:

---
int main ()
{
  char buf[10] = "test";
  FILE *fp = fmemopen (buf, 10, "a+");
  fseek (fp, 0, SEEK_SET);

  int gr;
  if ((gr = getc (fp)) != 't' ||
      (gr = getc (fp)) != 'e' ||
      (gr = getc (fp)) != 's' ||
      (gr = getc (fp)) != 't' ||
      (gr = getc (fp)) != EOF)
    {
      printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
      return 1;
    }

  return 0;
}
---

This is due both how read and write operation update the buffer position,
taking in consideration buffer lenght instead of maximum position defined
by the open mode.  This patch fixes it and also fixes fseek not returning
EINVAL for invalid whence modes.

Tested on x86_64 and i686.

	[BZ #20012]
	* libio/fmemopen.c (fmemopen_read): Use buffer maximum position, not
	length to calculate the buffer to read.
	(fmemopen_write): Set the buffer position based on bytes written.
	(fmemopen_seek): Return EINVAL for invalid whence modes.
---
 ChangeLog                    |  8 ++++
 libio/fmemopen.c             | 15 +++----
 stdio-common/tst-fmemopen3.c | 93 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+), 7 deletions(-)
diff mbox

Patch

diff --git a/ChangeLog b/ChangeLog
index f1084ee..712be81 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@ 
+2016-04-27  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+	[BZ #20012]
+	* libio/fmemopen.c (fmemopen_read): Use buffer maximum position, not
+	length to calculate the buffer to read.
+	(fmemopen_write): Set the buffer position based on bytes written.
+	(fmemopen_seek): Return EINVAL for invalid whence modes.
+
 2016-04-27  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #19831]
diff --git a/libio/fmemopen.c b/libio/fmemopen.c
index 9264b72..5097ca5 100644
--- a/libio/fmemopen.c
+++ b/libio/fmemopen.c
@@ -50,16 +50,14 @@  fmemopen_read (void *cookie, char *b, size_t s)
 
   if (c->pos + s > c->maxpos)
     {
-      if ((size_t) c->pos == c->maxpos)
-	return 0;
-      s = c->size - c->pos;
+      s = c->maxpos - c->pos;
+      if ((size_t) c->pos > c->maxpos)
+	s = 0;
     }
 
   memcpy (b, &(c->buffer[c->pos]), s);
 
   c->pos += s;
-  if ((size_t) c->pos > c->maxpos)
-    c->maxpos = c->pos;
 
   return s;
 }
@@ -86,7 +84,7 @@  fmemopen_write (void *cookie, const char *b, size_t s)
 
   memcpy (&(c->buffer[pos]), b, s);
 
-  c->pos += s;
+  c->pos = pos + s;
   if ((size_t) c->pos > c->maxpos)
     {
       c->maxpos = c->pos;
@@ -123,7 +121,10 @@  fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
     }
 
   if (np < 0 || (size_t) np > c->size)
-    return -1;
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
 
   *p = c->pos = np;
 
diff --git a/stdio-common/tst-fmemopen3.c b/stdio-common/tst-fmemopen3.c
index 250f9ec..af11f20 100644
--- a/stdio-common/tst-fmemopen3.c
+++ b/stdio-common/tst-fmemopen3.c
@@ -186,6 +186,97 @@  do_test_read_seek_negative (void)
 }
 
 static int
+do_test_write_append_2 (void)
+{
+  char buf[10] = "test";
+  FILE *fp = fmemopen (buf, 10, "a+");
+  size_t r = ftell (fp);
+  size_t e = strlen (buf);
+  if (r != e)
+    {
+      printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e);
+      return 1;
+    }
+
+  if (fseek (fp, 0, SEEK_SET) == -1)
+    {
+      printf ("%s: fseek returned -1\n", __FUNCTION__);
+      return 1;
+    }
+
+  int gr;
+  if ((gr = getc (fp)) != 't' ||
+      (gr = getc (fp)) != 'e' ||
+      (gr = getc (fp)) != 's' ||
+      (gr = getc (fp)) != 't' ||
+      (gr = getc (fp)) != EOF)
+    {
+      printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
+      return 1;
+    }
+
+  if (fseek (fp, e+1, SEEK_SET) == -1)
+    {
+      printf ("%s: fseek returned -1\n", __FUNCTION__);
+      return 1;
+    }
+
+  if ((r = ftell (fp)) != e+1)
+    {
+      printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+1);
+      return 1;
+    }
+
+  if ((gr = getc (fp)) != EOF)
+    {
+      printf ("%s: getc failed returned %i\n", __FUNCTION__, gr);
+      return 1;
+    }
+
+  /* Check if internal position is not changed with a getc returning EOF.  */
+  if ((r = ftell (fp)) != e+1)
+    {
+      printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+1);
+      return 1;
+    }
+
+  if (fseek (fp, 0, SEEK_CUR) == -1)
+    {
+      printf ("%s: fseek returned -1\n", __FUNCTION__);
+      return 1;
+    }
+
+  /* This should be overwritten by fprintf + fflush.  */
+  buf[e+2] = 'X';
+
+  if ((r = fprintf (fp, "%d", 101)) != 3)
+    {
+      printf ("%s: fprintf returned %zu, expected %d\n", __FUNCTION__, r, 3);
+      return 1;
+    }
+
+  fflush (fp);
+
+  /* Check if internal position is changed by 3 (strlen of '101').  */
+  if ((r = ftell (fp)) != e+3)
+    {
+      printf ("%s: ftell returned %zu, expected %zu\n", __FUNCTION__, r, e+3);
+      return 1;
+    }
+
+  const char exp[] = "test101";
+  if (strcmp (buf, exp) != 0)
+    {
+      printf ("%s: check failed: %s != %s\n", __FUNCTION__, buf, exp);
+      return 1;
+    }
+
+  fclose(fp);
+
+  return 0;
+}
+
+static int
 do_test (void)
 {
   int ret = 0;
@@ -199,6 +290,8 @@  do_test (void)
 
   ret += do_test_read_seek_negative ();
 
+  ret += do_test_write_append_2 ();
+
   return ret;
 }