[njs] Fixed TOCTOU in fs.mkdir() introduced in cb2ff67e595d.

Dmitry Volyntsev xeioex at nginx.com
Mon Jul 27 14:36:20 UTC 2020


details:   https://hg.nginx.org/njs/rev/6078d0c735b4
branches:  
changeset: 1481:6078d0c735b4
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Jul 27 14:18:15 2020 +0000
description:
Fixed TOCTOU in fs.mkdir() introduced in cb2ff67e595d.

Found by Coverity (CID 1465508).

diffstat:

 src/njs_fs.c |  75 +++++++++++++++++++++++++++++------------------------------
 1 files changed, 37 insertions(+), 38 deletions(-)

diffs (137 lines):

diff -r cb2ff67e595d -r 6078d0c735b4 src/njs_fs.c
--- a/src/njs_fs.c	Wed Jul 15 15:51:06 2020 +0300
+++ b/src/njs_fs.c	Mon Jul 27 14:18:15 2020 +0000
@@ -1135,96 +1135,95 @@ static njs_int_t
 njs_fs_make_path(njs_vm_t *vm, const char *path, mode_t md,
     njs_bool_t recursive, njs_value_t *retval)
 {
-    size_t       size;
+    int          err;
     ssize_t      length;
     njs_int_t    ret;
-    const char   *p, *prev;
+    const char   *p, *prev, *end;
     njs_value_t  value;
     struct stat  sb;
     char         path_buf[MAXPATHLEN];
 
     njs_set_undefined(retval);
 
+    end = path + njs_strlen(path);
+
     if (!recursive) {
         ret = mkdir(path, md);
         if (ret != 0) {
+            err = errno;
             goto failed;
         }
 
         return NJS_OK;
     }
 
-    ret = stat(path, &sb);
-    if (ret == 0) {
-        if (!S_ISDIR(sb.st_mode)) {
-            errno = ENOTDIR;
-            goto failed;
-        }
-
-        return NJS_OK;
-    }
-
-    if (errno != ENOENT) {
-        goto failed;
-    }
-
     p = path;
     prev = p;
 
     for ( ;; ) {
         p = strchr(prev + 1, '/');
         if (p == NULL) {
-            break;
+            p = end;
         }
 
         if (njs_slow_path((p - path) > MAXPATHLEN)) {
             njs_internal_error(vm, "too large path");
-            return NJS_OK;
+            return NJS_ERROR;
         }
 
         memcpy(&path_buf[prev - path], &path[prev - path], p - prev);
         path_buf[p - path] = '\0';
 
-        ret = stat(path_buf, &sb);
-        if (ret == 0) {
-            if (!S_ISDIR(sb.st_mode)) {
-                errno = ENOTDIR;
-                goto failed;
+        ret = mkdir(path_buf, md);
+        err = errno;
+
+        switch (ret) {
+        case 0:
+            break;
+
+        case EACCES:
+        case ENOTDIR:
+        case EPERM:
+            goto failed;
+
+        case EEXIST:
+        default:
+            ret = stat(path_buf, &sb);
+            if (ret == 0) {
+                if (!S_ISDIR(sb.st_mode)) {
+                    err = ENOTDIR;
+                    goto failed;
+                }
+
+                break;
             }
 
-        } else {
-            ret = mkdir(path_buf, md);
-            if (ret != 0) {
-                goto failed;
-            }
+            goto failed;
+        }
+
+        if (p == end) {
+            break;
         }
 
         path_buf[p - path] = '/';
         prev = p;
     }
 
-    ret = mkdir(path, md);
-    if (ret != 0 && errno != EEXIST) {
-        goto failed;
-    }
-
     return NJS_OK;
 
 failed:
 
-    size = njs_strlen(path);
-    length = njs_utf8_length((u_char *) path, size);
+    length = njs_utf8_length((u_char *) path, end - path);
     if (njs_slow_path(length < 0)) {
         length = 0;
     }
 
-    ret = njs_string_new(vm, &value, (u_char *) path, size, length);
+    ret = njs_string_new(vm, &value, (u_char *) path, end - path, length);
     if (ret != NJS_OK) {
         return NJS_ERROR;
     }
 
-    return njs_fs_error(vm, "mkdir", strerror(errno), &value, errno,
-                        retval);
+    return njs_fs_error(vm, "mkdir", strerror(err), &value, err, retval);
 }
 
 


More information about the nginx-devel mailing list