[PATCH] Make configuration file "include" directive handling morerobust - Mailing list pgsql-hackers

From Ian Barwick
Subject [PATCH] Make configuration file "include" directive handling morerobust
Date
Msg-id 8c8bcbca-3bd9-dc6e-8986-04a5abdef142@2ndquadrant.com
Whole thread Raw
Responses Re: [PATCH] Make configuration file "include" directive handlingmore robust
List pgsql-hackers
Hi

While poking about with [1], I noticed a few potential issues with the
inclusion handling for configuration files; another issue is demonstrated in [2].

[1] https://www.postgresql.org/message-id/aed6cc9f-98f3-2693-ac81-52bb0052307e%402ndquadrant.com
    ("Stop ALTER SYSTEM from making bad assumptions")
[2]
https://www.postgresql.org/message-id/CY4PR1301MB22001D3FAAB3499C5D41DE23A9E50%40CY4PR1301MB2200.namprd13.prod.outlook.com
    ("First Time Starting Up PostgreSQL and Having Problems")

Specifically these are:

1) Provision of empty include directives
=========================================

The default postgresql.conf file includes these thusly:

     #include_dir = ''                       # include files ending in '.conf' from
                                             # a directory, e.g., 'conf.d'
     #include_if_exists = ''                 # include file only if it exists
     #include = ''                           # include file

Currently, uncommenting them but leaving the value empty (as happened in [2] above) can
result in unexpected behaviour.

For "include" and "include_if_exists", it's a not critical issue as non-existent
files are, well, non-existent, however this will leave behind the cryptic
message "input in flex scanner failed" in pg_file_settings's "error" column, e.g.:

     postgres=# SELECT sourceline, seqno, name, setting, applied, error
                  FROM pg_file_settings
                 WHERE error IS NOT NULL;
      sourceline | seqno | name | setting | applied |            error
     ------------+-------+------+---------+---------+------------------------------
               1 |    45 |      |         | f       | input in flex scanner failed
               1 |    46 |      |         | f       | input in flex scanner failed
     (2 rows)

However, an empty value for "include_dir" will result in the current configuration
file's directory being read, which can result in circular inclusion and triggering
of the nesting depth check.

Patch {1} makes provision of an empty value for any of these directives cause
configuration file processing to report an approprate error, e.g.:

     postgres=# SELECT sourceline, seqno, name, setting, applied, error
                  FROM pg_file_settings
                 WHERE error IS NOT NULL;
      sourceline | seqno | name | setting | applied |                 error
     ------------+-------+------+---------+---------+---------------------------------------
             757 |    45 |      |         | f       | "include" must not be empty
             758 |    46 |      |         | f       | "include_if_exists" must not be empty
             759 |    47 |      |         | f       | "include_dir" must not be empty


2) Circular inclusion of configuration files
============================================

Currently there is a simple maximum nesting threshold (currently 10) which
will stop runaway circular inclusions. However, if triggered, it's not
always obvious what triggered it, and sometimes resource exhaustion
might kick in beforehand (as appeared to be the case in [2] above).

Patch {2} attempts to handle this situation by keeping track of which
files have already been included (based on their absolute, canonical
path) and reporting an error if they were encountered again.

On server startup:

    2019-07-11 09:13:25.610 GMT [71140] LOG:  configuration file "/var/lib/pgsql/data/postgresql.conf" was previously
parsed
    2019-07-11 09:13:25.610 GMT [71140] FATAL:  configuration file "/var/lib/pgsql/data/postgresql.conf" contains
errors

After sending SIGHUP:

     postgres=# SELECT sourceline, seqno, name, setting, applied, error FROM pg_file_settings WHERE error IS NOT NULL;
      sourceline | seqno | name | setting | applied |                                      error

------------+-------+------+---------+---------+--------------------------------------------------------------------------------
             757 |    45 |      |         | f       | configuration file "/var/lib/pgsql/data/postgresql.conf" was
previouslyparsed
 
     (1 row)

3) "include" directives in postgresql.auto.conf and extension control files
===========================================================================

Currently these are parsed and acted on, even though it makes no sense for further
config files to be included in either case.

With "postgresql.auto.conf", if a file is successfully included, its contents
will then be written to "postgresql.auto.conf" and the include directive will be
removed, which seems like a recipe for confusion.

These are admittedly unlikely corner cases, but it's easy enough to stop this
happening on the offchance someone tries to use this to solve some problem in
completely the wrong way.

Patch {3} implements this (note that this patch depends on patch {2}).

Extension example:

    postgres=# CREATE EXTENSION repmgr;
    ERROR:  "include" not permitted in file "/home/barwick/devel/postgres/builds/HEAD/share/extension/repmgr.control"
line8
 
    postgres=# CREATE EXTENSION repmgr;
    ERROR:  "include_dir" not permitted in file
"/home/barwick/devel/postgres/builds/HEAD/share/extension/repmgr.control"line 9
 
    postgres=# CREATE EXTENSION repmgr;
    ERROR:  "include_if_exists" not permitted in file
"/home/barwick/devel/postgres/builds/HEAD/share/extension/repmgr.control"line 10
 

pg.auto.conf example:

    postgres=# ALTER SYSTEM SET default_tablespace ='pg_default';
    ERROR:  could not parse contents of file "postgresql.auto.conf"
     postgres=# SELECT regexp_replace(sourcefile, '^/.+/','') AS sourcefile,
                       seqno, name, setting, applied, error
                  FROM pg_file_settings WHERE error IS NOT NULL;
           sourcefile      | seqno | name | setting | applied |          error
     ----------------------+-------+------+---------+---------+-------------------------
      postgresql.auto.conf |    45 |      |         | f       | "include" not permitted
     (1 row)

The patch also has the side-effect that "include" directives are no longer
(silently) removed from "postgresql.auto.conf"; as the only way they can get
into the file in the first place is by manually editing it, I feel it's
reasonable for the user to be made aware that they're not valid and have to
manually remove them.


Patches
=======

Code:

{1} disallow-empty-include-directives.v1.patch
{2} track-included-files.v1.patch
{3} prevent-disallowed-includes.v1.patch

TAP tests:
{1} tap-test-configuration.v1.patch
{2} tap-test-disallow-empty-include-directives.v1.patch
{3} tap-test-track-included-files.v1.patch
{4} tap-test-prevent-disallowed-includes.v1.patch

Patches apply cleanly to REL_12_STABLE/HEAD, they could be modfied for
all supported versions if required. I can consolidate the patches
if preferred.

Regards

Ian Barwick


-- 
  Ian Barwick                   https://www.2ndQuadrant.com/
  PostgreSQL Development, 24x7 Support, Training & Services

Attachment

pgsql-hackers by date:

Previous
From: Thomas Munro
Date:
Subject: Re: SegFault on 9.6.14
Next
From: Dilip Kumar
Date:
Subject: Re: POC: Cleaning up orphaned files using undo logs