fgetpos32 does not signal overflow when [63:32] bits of position offset are
non-zero, and returns truncated value. fgetpos32 signals overflow only when
[31] bit is set. Implement simple overflow check by comparing the returned
position value with raw offset from _lseeki64().
---
 mingw-w64-crt/stdio/fgetpos.c | 48 ++++++++++++++++++++++++++++++++++-
 1 file changed, 47 insertions(+), 1 deletion(-)

diff --git a/mingw-w64-crt/stdio/fgetpos.c b/mingw-w64-crt/stdio/fgetpos.c
index 924c603c0295..aa3feda108e1 100644
--- a/mingw-w64-crt/stdio/fgetpos.c
+++ b/mingw-w64-crt/stdio/fgetpos.c
@@ -5,17 +5,63 @@
  */
 
 #include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <io.h>
 
 /* Define 64-bit fgetpos() function via 32-bit fgetpos32() function */
 _CRTIMP int __cdecl fgetpos32(FILE *__restrict__ _File, long *__restrict__ 
_Pos);
 int __cdecl fgetpos(FILE *__restrict__ _File, fpos_t *__restrict__ _Pos)
 {
+  __int64 raw_off64;
   long pos32;
   int ret;
+  int fd;
+  int old_errno;
 
+  old_errno = errno;
+
+  /* Function fgetpos32() on success returns 0 and sets pos32 to [30:0] bits of
+   * 64-bit position offset. If the [31] bit of 64-bit position offset is set
+   * then fgetpos32() returns -1, sets pos32 to -1 and do not change errno.
+   * Non-zero value in [63:32] bits of 64-bit position offset does not trigger
+   * any error condition. In other error cases fgetpos32() returns -1,
+   * sets pos32 to -1 and errno to error value. */
+
+  errno = 0;
   ret = fgetpos32(_File, &pos32);
+  if (ret != 0 || pos32 < 0)
+  {
+    if (errno == 0) /* This detects overflow when [31] bit is set. */
+      errno = EOVERFLOW;
+    *_Pos = pos32;
+    return ret;
+  }
+
+  /* Now try to detect overflow when [63:32] bits of offset are set.
+   * Do it by comparing the raw fd-based 64-bit offset returned by
+   * _lseeki64() that is not further than the half of the maximal
+   * possible 31-bit position offset returned by fgetpos32().
+   * Because FILE* stream use read/write buffers and translation modes,
+   * the FILE* stream position and raw fd position can be different
+   * and raw fd position be used directly for FILE* stream position.
+   */
+  fd = fileno(_File);
+  if (fd >= 0)
+  {
+    raw_off64 = _lseeki64(fd, 0, SEEK_CUR);
+    if (raw_off64 > LONG_MAX && raw_off64 - pos32 > LONG_MAX/2)
+    {
+      errno = EOVERFLOW;
+      *_Pos = -1;
+      return -1;
+    }
+  }
+
+  errno = old_errno;
+
   *_Pos = pos32;
-  return ret;
+  return 0;
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(fgetpos))(FILE *__restrict__ _File, fpos_t 
*__restrict__ _Pos) = fgetpos;
 
-- 
2.20.1



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to