Simplify coding around path_contains_parent_reference().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 31 Jan 2022 18:53:38 +0000 (13:53 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 31 Jan 2022 18:53:38 +0000 (13:53 -0500)
Given the existing stipulation that path_contains_parent_reference()
must only be invoked on canonicalized paths, we can simplify things
in the wake of commit c10f830c5.  It is now only possible to see
".." at the start of a relative path.  That means we can simplify
path_contains_parent_reference() itself quite a bit, and it makes
the two existing outside call sites dead code, since they'd already
checked that the path is absolute.

We could now fold path_contains_parent_reference() into its only
remaining caller path_is_relative_and_below_cwd().  But it seems
better to leave it as a separately callable function, in case any
extensions are using it.

Also document the pre-existing requirement for
path_is_relative_and_below_cwd's input to be likewise canonicalized.

Shenhao Wang and Tom Lane

Discussion: https://postgr.es/m/OSBPR01MB4214FA221FFE046F11F2AD74F2D49@OSBPR01MB4214.jpnprd01.prod.outlook.com

contrib/adminpack/adminpack.c
src/backend/utils/adt/genfile.c
src/port/path.c

index 45e5ae31f6b397cc2df8e799cb5aedc3653daab7..d7d84d096f745c0819fad9efb7da5c7bd7a3d138 100644 (file)
@@ -88,12 +88,6 @@ convert_and_check_filename(text *arg)
     */
    if (is_absolute_path(filename))
    {
-       /* Disallow '/a/b/data/..' */
-       if (path_contains_parent_reference(filename))
-           ereport(ERROR,
-                   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                    errmsg("reference to parent directory (\"..\") not allowed")));
-
        /* Allow absolute paths if within DataDir */
        if (!path_is_prefix_of_path(DataDir, filename))
            ereport(ERROR,
index 542bbacaa249b294d4270f03c96c0381dc2068da..fe6863d8b4466ebc5b22b799b71538aab6e988c8 100644 (file)
@@ -72,12 +72,6 @@ convert_and_check_filename(text *arg)
     */
    if (is_absolute_path(filename))
    {
-       /* Disallow '/a/b/data/..' */
-       if (path_contains_parent_reference(filename))
-           ereport(ERROR,
-                   (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                    errmsg("reference to parent directory (\"..\") not allowed")));
-
        /*
         * Allow absolute paths if within DataDir or Log_directory, even
         * though Log_directory might be outside DataDir.
index ea1d8f6ff58121a5e85794b3f173df5947cc82cc..05fe812f757b9fa132135997508fc4ecadb3ca2d 100644 (file)
@@ -494,27 +494,21 @@ canonicalize_path(char *path)
  * Detect whether a path contains any parent-directory references ("..")
  *
  * The input *must* have been put through canonicalize_path previously.
- *
- * This is a bit tricky because we mustn't be fooled by "..a.." (legal)
- * nor "C:.." (legal on Unix but not Windows).
  */
 bool
 path_contains_parent_reference(const char *path)
 {
-   int         path_len;
-
-   path = skip_drive(path);    /* C: shouldn't affect our conclusion */
-
-   path_len = strlen(path);
-
    /*
-    * ".." could be the whole path; otherwise, if it's present it must be at
-    * the beginning, in the middle, or at the end.
+    * Once canonicalized, an absolute path cannot contain any ".." at all,
+    * while a relative path could contain ".."(s) only at the start.  So it
+    * is sufficient to check the start of the path, after skipping any
+    * Windows drive/network specifier.
     */
-   if (strcmp(path, "..") == 0 ||
-       strncmp(path, "../", 3) == 0 ||
-       strstr(path, "/../") != NULL ||
-       (path_len >= 3 && strcmp(path + path_len - 3, "/..") == 0))
+   path = skip_drive(path);    /* C: shouldn't affect our conclusion */
+
+   if (path[0] == '.' &&
+       path[1] == '.' &&
+       (path[2] == '\0' || path[2] == '/'))
        return true;
 
    return false;
@@ -522,10 +516,11 @@ path_contains_parent_reference(const char *path)
 
 /*
  * Detect whether a path is only in or below the current working directory.
+ *
+ * The input *must* have been put through canonicalize_path previously.
+ *
  * An absolute path that matches the current working directory should
- * return false (we only want relative to the cwd).  We don't allow
- * "/../" even if that would keep us under the cwd (it is too hard to
- * track that).
+ * return false (we only want relative to the cwd).
  */
 bool
 path_is_relative_and_below_cwd(const char *path)