This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
If not using smallbuf and len > sizeof(d_name), Move() is illegal.
authorJarkko Hietaniemi <jhi@iki.fi>
Wed, 3 Feb 2016 14:53:54 +0000 (09:53 -0500)
committerJarkko Hietaniemi <jhi@iki.fi>
Sun, 7 Feb 2016 13:23:46 +0000 (08:23 -0500)
Coverity CID 135024: Out-of-bounds access (OVERRUN)

If the len is not <= sizeof(smallbuf), the len is at least
sizeof(smallbuf) + 1, which means at least 257.  Now, if the
sizeof(dirent->d_name) is < 257, the Move() would access bytes
beyond the end of d_name[].  Yes, this would need for the d_namlen
(for example) to be out of sync with d_name[].  But paranoia is good.

Because of the severity of the problem (indicating serious mess),
returning NULL instead of partial result is probably better.

Possible portability problem: can d_name ever be not an array,
but instead a bare char pointer?  If that can happen, the sizeof(d_name)
is wrong, and in that case we have to have some other way of figuring
out the maximum size for a directory entry name.

The smallbuf probably could/should be of size MAXPATHLEN.

sv.c

diff --git a/sv.c b/sv.c
index f2736b7..71c398b 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -13029,7 +13029,7 @@ Perl_dirp_dup(pTHX_ DIR *const dp, CLONE_PARAMS *const param)
 #if defined(HAS_FCHDIR) && defined(HAS_TELLDIR) && defined(HAS_SEEKDIR)
     DIR *pwd;
     const Direntry_t *dirent;
-    char smallbuf[256];
+    char smallbuf[256]; /* XXX MAXPATHLEN, surely? */
     char *name = NULL;
     STRLEN len = 0;
     long pos;
@@ -13079,6 +13079,14 @@ Perl_dirp_dup(pTHX_ DIR *const dp, CLONE_PARAMS *const param)
     pos = PerlDir_tell(dp);
     if ((dirent = PerlDir_read(dp))) {
        len = d_namlen(dirent);
+        if (len > sizeof(dirent->d_name)) {
+            /* If the len is somehow magically longer than the
+             * maximum length of the directory entry, even though
+             * we could fit it in a buffer, we could not copy it
+             * from the dirent.  Bail out. */
+            PerlDir_close(ret);
+            return (DIR*)NULL;
+        }
        if (len <= sizeof smallbuf) name = smallbuf;
        else Newx(name, len, char);
        Move(dirent->d_name, name, len, char);