Bug 1380858

Summary: Race within conf_reload_path() results in a kernel panic
Product: Red Hat Enterprise Linux 6 Reporter: Kyle Walker <kwalker>
Component: upstartAssignee: Lukáš Nykrýn <lnykryn>
Status: CLOSED ERRATA QA Contact: Frantisek Sumsal <fsumsal>
Severity: urgent Docs Contact:
Priority: unspecified    
Version: 6.9CC: cww, fsorenso, fsumsal, gkeegan, jkurik, tbowling
Target Milestone: rcKeywords: Reproducer
Target Release: ---   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: upstart-0.6.5-17.el6 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-06-19 05:20:15 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1374441, 1461138, 1494481    
Attachments:
Description Flags
Reproducer utility created by Frank Sorenson none

Description Kyle Walker 2016-09-30 19:36:24 UTC
Created attachment 1206354 [details]
Reproducer utility created by Frank Sorenson

Description of problem:
 Within conf_reload_path(), if an override is loaded immediately preceding the underlying configuration file, a race condition can result in a kernel panic.

Using the following files as examples:
    /etc/init/control-alt-delete.conf
    /etc/init/control-alt-delete.override

 1) A override write completes, exercising conf_reload_path() for the underlying configuration file following:
    conf_create_modify_handler
    <snip>
                nih_debug ("Loading configuration and override files for %s", path);

                /* load conf file */
                nih_debug ("Loading configuration file %s", path);
                ret = conf_reload_path (source, path, NULL);
                if (ret < 0) {
                        error_path = path;
                        goto error;
                }

                /* ensure we ignore directory changes (which won't have overrides. */
                if (is_conf_file_std (path)) {
                        struct stat st;
                        if (stat (new_path, &st) == 0) {
                                /* overlay override settings */
                                nih_debug ("Loading override file %s for %s", new_path, path);
                                ret = conf_reload_path (source, path, new_path);
                                if (ret < 0) {
                                        error_path = new_path;
                                        goto error;
                                }
                        }

                }
    <snip>

 2) The .conf file is the first path given to conf_reload_path() which subsequently issues a nih_unref() in:

    conf_reload_path
    <snip>
        path_to_load = ( override_path ? override_path : path);

        /* If there is no corresponding override file, look up the old
         * conf file in memory, and then free it.  In cases of failure,
         * we discard it anyway, so there's no particular reason
         * to keep it around anymore.
         */
        file = (ConfFile *)nih_hash_lookup (source->files, path);
        if (! override_path && file)
                nih_unref (file, source);

        /* Read the file into memory for parsing, if this fails we don't
         * bother creating a new ConfFile structure for it and bail out
         * now.
         */
        buf = nih_file_read (NULL, path_to_load, &len);
        if (! buf)
                return -1;
    <snip>

 3) In the last nih_file_read() the .conf file is overwritten between the two operations:

    nih_file_read()
    <snip>
        if (fstat (fileno (fp), &statbuf) < 0)
                goto error;
    <snip>
        if (fread (file, 1, statbuf.st_size, fp) != (size_t)statbuf.st_size) {
                errno = EILSEQ;
                goto error;
        }
    <snip>

 4) Following the above EILSEQ being returned, the already nih_unref()'d file structure is again issued a nih_unref() when returning to conf_create_modify_handler() in:

    conf_create_modify_handler
    <snip>
                    /* load conf file */
                    nih_debug ("Loading configuration file %s", path);
                    ret = conf_reload_path (source, path, NULL);
                    if (ret < 0) {
                            error_path = path;
                            goto error;
                    }
    <snip>
    error:
            {
                    NihError *err;

                    err = nih_error_get ();
                    nih_error ("%s: %s: %s", error_path,
                                    _("Error while loading configuration file"),
                                    err->message);
                    nih_free (err);
                    if (file)
                            nih_unref (file, source);
            }
    }
    <snip>



Version-Release number of selected component (if applicable):
 upstart-0.6.5-16.el6

How reproducible:
 Intermittently

Steps to Reproduce:
1. Copy an override file to /etc/init/control-alt-delete.override
2. Before upstart loads the override, replace the undelrying /etc/init/control-alt-delete.conf with an empty file
3.

Actual results:
 Periodically, a kernel panic is encountered

Expected results:
 The system continues operation

Additional info:

Comment 4 Lukáš Nykrýn 2018-02-09 07:45:37 UTC
diff --git a/init/conf.c b/init/conf.c
index 215effa..88f13b8 100644
--- a/init/conf.c
+++ b/init/conf.c
@@ -771,6 +771,8 @@ error:
 				_("Error while loading configuration file"),
 				err->message);
 		nih_free (err);
+
+		file = (ConfFile *)nih_hash_lookup (source->files, new_path);
 		if (file)
 			nih_unref (file, source);
 	}

Comment 10 errata-xmlrpc 2018-06-19 05:20:15 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2018:1903