*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 4446,4453 **** COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
          is <filename>pg_stat_tmp</filename>. Pointing this at a RAM-based
          file system will decrease physical I/O requirements and can lead to
          improved performance.
!         This parameter can only be set in the <filename>postgresql.conf</>
!         file or on the server command line.
         </para>
        </listitem>
       </varlistentry>
--- 4446,4452 ----
          is <filename>pg_stat_tmp</filename>. Pointing this at a RAM-based
          file system will decrease physical I/O requirements and can lead to
          improved performance.
!         This parameter can only be set at server start.
         </para>
        </listitem>
       </varlistentry>
*** a/src/backend/postmaster/pgstat.c
--- b/src/backend/postmaster/pgstat.c
***************
*** 554,559 **** startup_failed:
--- 554,628 ----
  }
  
  /*
+  * Verify the permissions of stats_temp_directory.
+  *
+  * For security reasons, we want this directory to be tightly controlled;
+  * both so that we don't interfere with other running instances, and so that
+  * others can't inject fake stats.
+  *
+  * The obvious way to do this is to set this function as a GUC check hook; but
+  * this doesn't work because the first time those hooks are run, we have not
+  * changed the current directory to the data directory yet; so the check fails
+  * when it's set to a relative path, as the default value is.  We'd have to
+  * find a way to skip this check in the first run of check hooks.  To avoid this
+  * problem, the stats_temp_directory setting is PGC_POSTMASTER for now, and we
+  * run the check separately in PostmasterMain, after changing directory,
+  * instead.
+  *
+  * If we were to overcome that problem, we should probably also consider having
+  * the stats collector write a full set of files in the new temp directory if
+  * the setting is changed on SIGHUP.
+  */
+ bool
+ check_pgstat_temp_dir_perms(char *dir)
+ {
+ 	int		elevel = FATAL;
+ 	CheckDirErrcode	err;
+ 
+ 	err = checkDirectoryPermissions(dir);
+ 
+ 	switch (err)
+ 	{
+ 		case CKDIR_OK:
+ 			break;
+ 		case CKDIR_NOT_EXISTS:
+ 			ereport(elevel,
+ 					(errcode_for_file_access(),
+ 					 errmsg("stats_temp_directory \"%s\" does not exist",
+ 							dir)));
+ 			break;
+ 		case CKDIR_CANT_READ_PERMS:
+ 			ereport(elevel,
+ 					(errcode_for_file_access(),
+ 					 errmsg("could not read permissions of stats_temp_directory \"%s\": %m",
+ 							dir)));
+ 			break;
+ 		case CKDIR_NOT_DIR:
+ 			ereport(elevel,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("specified stats_temp_directory \"%s\" is not a directory",
+ 							dir)));
+ 			break;
+ 		case CKDIR_WRONG_OWNER:
+ 			ereport(elevel,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("stats_temp_directory \"%s\" has wrong ownership",
+ 							dir),
+ 					 errhint("The server must be started by the user that owns the stats_temp_directory.")));
+ 			break;
+ 		case CKDIR_TOO_ACCESSIBLE:
+ 			ereport(elevel,
+ 					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ 					 errmsg("stats_temp_directory \"%s\" has group or world access",
+ 							dir),
+ 					 errdetail("Permissions should be u=rwx (0700).")));
+ 			break;
+ 	}
+ 
+ 	return err == CKDIR_OK;
+ }
+ 
+ /*
   * subroutine for pgstat_reset_all
   */
  static void
*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 829,834 **** PostmasterMain(int argc, char *argv[])
--- 829,836 ----
  		ExitPostmaster(1);
  	}
  
+ 	(void) check_pgstat_temp_dir_perms(pgstat_stat_directory);
+ 
  	/*
  	 * Now that we are done processing the postmaster arguments, reset
  	 * getopt(3) library so that it will work correctly in subprocesses.
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 3081,3087 **** static struct config_string ConfigureNamesString[] =
  	},
  
  	{
! 		{"stats_temp_directory", PGC_SIGHUP, STATS_COLLECTOR,
  			gettext_noop("Writes temporary statistics files to the specified directory."),
  			NULL,
  			GUC_SUPERUSER_ONLY
--- 3081,3088 ----
  	},
  
  	{
! 		/* see check_pgstat_temp_dir_perms for why this is PGC_POSTMASTER */
! 		{"stats_temp_directory", PGC_POSTMASTER, STATS_COLLECTOR,
  			gettext_noop("Writes temporary statistics files to the specified directory."),
  			NULL,
  			GUC_SUPERUSER_ONLY
*** a/src/include/pgstat.h
--- b/src/include/pgstat.h
***************
*** 750,755 **** extern void pgstat_init(void);
--- 750,756 ----
  extern int	pgstat_start(void);
  extern void pgstat_reset_all(void);
  extern void allow_immediate_pgstat_restart(void);
+ extern bool check_pgstat_temp_dir_perms(char *dir);
  
  #ifdef EXEC_BACKEND
  extern void PgstatCollectorMain(int argc, char *argv[]) __attribute__((noreturn));
