[rpm/f18] - fix build-time double-free on file capability processing (#956190) - check for stale locks when op

Panu Matilainen pmatilai at fedoraproject.org
Mon Aug 26 08:48:04 UTC 2013


commit fdb7b534920a0a22965af8e98ba4f2361c2dab7c
Author: Panu Matilainen <pmatilai at redhat.com>
Date:   Mon Aug 26 11:44:02 2013 +0300

    - fix build-time double-free on file capability processing (#956190)
    - check for stale locks when opening write-cursors (#860500)
    - serialize BDB environment open/close (#924417)

 rpm-4.10.x-caps-double-free.patch |   13 ++++
 rpm-4.10.x-cursor-failchk.patch   |   58 +++++++++++++++
 rpm-4.10.x-db-serialize.patch     |  139 +++++++++++++++++++++++++++++++++++++
 rpm.spec                          |   14 ++++-
 4 files changed, 223 insertions(+), 1 deletions(-)
---
diff --git a/rpm-4.10.x-caps-double-free.patch b/rpm-4.10.x-caps-double-free.patch
new file mode 100644
index 0000000..d8d80d5
--- /dev/null
+++ b/rpm-4.10.x-caps-double-free.patch
@@ -0,0 +1,13 @@
+diff --git a/build/files.c b/build/files.c
+index 30061cf..df1eedf 100644
+--- a/build/files.c
++++ b/build/files.c
+@@ -1481,7 +1481,7 @@ static rpmRC addFile(FileList fl, const char * diskPath,
+ 	}
+ 
+ 	if (fl->currentCaps) {
+-	    flp->caps = fl->currentCaps;
++	    flp->caps = xstrdup(fl->currentCaps);
+ 	} else {
+ 	    flp->caps = xstrdup("");
+ 	}
diff --git a/rpm-4.10.x-cursor-failchk.patch b/rpm-4.10.x-cursor-failchk.patch
new file mode 100644
index 0000000..c5ecc03
--- /dev/null
+++ b/rpm-4.10.x-cursor-failchk.patch
@@ -0,0 +1,58 @@
+commit 918b2892b46036ac46f07c3156ed78b45e4e2ee2
+Author: Panu Matilainen <pmatilai at redhat.com>
+Date:   Tue Feb 5 10:11:19 2013 +0200
+
+    Check for stale db locks when opening write-cursors
+    
+    - During long-running transactions its entirely possible for some
+      other player to come and go leaving stale locks behind and cause
+      the transaction to get stuck until the cavalry comes along in the
+      form of somebody else opening the rpmdb, clearing the blockage.
+    - Presumably dbenv->failchk() is not entirely free of cost so we only
+      do this for writes which are way more critical and also more prone to
+      getting stuck.
+    - dbenv->failchk() could return DB_RUNRECOVER in which case we should
+      abort everything but we lack a mechanism to do it... just add
+      a reminder comment for now.
+    
+    (cherry picked from commit 29e7c4b3bd1e67f9de1eaaf9fecf82cae281a7e6)
+
+diff --git a/lib/backend/db3.c b/lib/backend/db3.c
+index bbf9577..ed2a5f8 100644
+--- a/lib/backend/db3.c
++++ b/lib/backend/db3.c
+@@ -244,7 +244,7 @@ dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags)
+ 	DB * db = dbi->dbi_db;
+ 	DBC * cursor;
+ 	int cflags;
+-	int rc;
++	int rc = 0;
+ 	uint32_t eflags = db_envflags(db);
+ 	
+        /* DB_WRITECURSOR requires CDB and writable db */
+@@ -255,8 +255,23 @@ dbiCursor dbiCursorInit(dbiIndex dbi, unsigned int flags)
+ 	} else
+ 	    cflags = 0;
+ 
+-	rc = db->cursor(db, NULL, &cursor, cflags);
+-	rc = cvtdberr(dbi, "db->cursor", rc, _debug);
++	/*
++	 * Check for stale locks which could block writes "forever".
++	 * XXX: Should we also do this on reads? Reads are less likely
++	 *      to get blocked so it seems excessive...
++	 * XXX: On DB_RUNRECOVER, we should abort everything. Now
++	 *      we'll just fail to open a cursor again and again and again.
++	 */
++	if (cflags & DB_WRITECURSOR) {
++	    DB_ENV *dbenv = db->get_env(db);
++	    rc = dbenv->failchk(dbenv, 0);
++	    rc = cvtdberr(dbi, "dbenv->failchk", rc, _debug);
++	}
++
++	if (rc == 0) {
++	    rc = db->cursor(db, NULL, &cursor, cflags);
++	    rc = cvtdberr(dbi, "db->cursor", rc, _debug);
++	}
+ 
+ 	if (rc == 0) {
+ 	    dbc = xcalloc(1, sizeof(*dbc));
diff --git a/rpm-4.10.x-db-serialize.patch b/rpm-4.10.x-db-serialize.patch
new file mode 100644
index 0000000..a98684c
--- /dev/null
+++ b/rpm-4.10.x-db-serialize.patch
@@ -0,0 +1,139 @@
+commit 7e291848bcaea13b3d973901703ec3c760195335
+Author: Panu Matilainen <pmatilai at redhat.com>
+Date:   Tue May 28 08:56:22 2013 +0300
+
+    Serialize BDB environment open/close (RhBug:924417 etc)
+    
+    - Introduce Yet Another Broken Lock[*] to serialize BDB environment open:
+      otherwise we can end up calling dbenv->failchk() while another process
+      is just joining the environment, leading to transient "Thread died in..."
+      DB_RUNRECOVER errors. Also prevents races on chrooted operations where
+      we remove the entire environment on close.
+    - This should also make it possible to handle at least some cases of
+      real DB_RUNRECOVER errors by just nuking the environment but that's
+      another topic...
+    
+      [*] YABL as this is nowhere near foolproof or sufficient for all
+          the possible variants, but better than not having it...
+    
+    (cherry picked from commit ad874d60e3804f1bcd64f3510e1e2dfbf81456cd)
+
+diff --git a/lib/backend/db3.c b/lib/backend/db3.c
+index ed2a5f8..f46c9ff 100644
+--- a/lib/backend/db3.c
++++ b/lib/backend/db3.c
+@@ -57,10 +57,42 @@ static uint32_t db_envflags(DB * db)
+     return eflags;
+ }
+ 
++/*
++ * Try to acquire db environment open/close serialization lock.
++ * Return the open, locked fd on success, -1 on failure.
++ */
++static int serialize_env(const char *dbhome)
++{
++    char *lock_path = rstrscat(NULL, dbhome, "/.dbenv.lock", NULL);
++    mode_t oldmask = umask(022);
++    int fd = open(lock_path, (O_RDWR|O_CREAT), 0644);
++    umask(oldmask);
++
++    if (fd >= 0) {
++	int rc;
++	struct flock info;
++	memset(&info, 0, sizeof(info));
++	info.l_type = F_WRLCK;
++	info.l_whence = SEEK_SET;
++	do {
++	    rc = fcntl(fd, F_SETLKW, &info);
++	} while (rc == -1 && errno == EINTR);
++	    
++	if (rc == -1) {
++	    close(fd);
++	    fd = -1;
++	}
++    }
++
++    free(lock_path);
++    return fd;
++}
++
+ static int db_fini(rpmdb rdb, const char * dbhome)
+ {
+     DB_ENV * dbenv = rdb->db_dbenv;
+     int rc;
++    int lockfd = -1;
+     uint32_t eflags = 0;
+ 
+     if (dbenv == NULL)
+@@ -72,6 +104,9 @@ static int db_fini(rpmdb rdb, const char * dbhome)
+     }
+ 
+     (void) dbenv->get_open_flags(dbenv, &eflags);
++    if (!(eflags & DB_PRIVATE))
++	lockfd = serialize_env(dbhome);
++
+     rc = dbenv->close(dbenv, 0);
+     rc = dbapi_err(rdb, "dbenv->close", rc, _debug);
+ 
+@@ -89,6 +124,10 @@ static int db_fini(rpmdb rdb, const char * dbhome)
+ 	rpmlog(RPMLOG_DEBUG, "removed  db environment %s\n", dbhome);
+ 
+     }
++
++    if (lockfd >= 0)
++	close(lockfd);
++
+     return rc;
+ }
+ 
+@@ -122,6 +161,7 @@ static int db_init(rpmdb rdb, const char * dbhome)
+     DB_ENV *dbenv = NULL;
+     int rc, xx;
+     int retry_open = 2;
++    int lockfd = -1;
+     struct dbConfig_s * cfg = &rdb->cfg;
+     /* This is our setup, thou shall not have other setups before us */
+     uint32_t eflags = (DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB);
+@@ -176,6 +216,24 @@ static int db_init(rpmdb rdb, const char * dbhome)
+     }
+ 
+     /*
++     * Serialize shared environment open (and clock) via fcntl() lock.
++     * Otherwise we can end up calling dbenv->failchk() while another
++     * process is joining the environment, leading to transient
++     * DB_RUNRECOVER errors. Also prevents races wrt removing the
++     * environment (eg chrooted operation). Silently fall back to
++     * private environment on failure to allow non-privileged queries
++     * to "work", broken as it might be.
++     */
++    if (!(eflags & DB_PRIVATE)) {
++	lockfd = serialize_env(dbhome);
++	if (lockfd < 0) {
++	    eflags |= DB_PRIVATE;
++	    retry_open--;
++	    rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n");
++	}
++    }
++
++    /*
+      * Actually open the environment. Fall back to private environment
+      * if we dont have permission to join/create shared environment or
+      * system doesn't support it..
+@@ -208,6 +266,8 @@ static int db_init(rpmdb rdb, const char * dbhome)
+     rdb->db_dbenv = dbenv;
+     rdb->db_opens = 1;
+ 
++    if (lockfd >= 0)
++	close(lockfd);
+     return 0;
+ 
+ errxit:
+@@ -216,6 +276,8 @@ errxit:
+ 	xx = dbenv->close(dbenv, 0);
+ 	xx = dbapi_err(rdb, "dbenv->close", xx, _debug);
+     }
++    if (lockfd >= 0)
++	close(lockfd);
+     return rc;
+ }
+ 
diff --git a/rpm.spec b/rpm.spec
index aa89828..59513f0 100644
--- a/rpm.spec
+++ b/rpm.spec
@@ -21,7 +21,7 @@
 Summary: The RPM package management system
 Name: rpm
 Version: %{rpmver}
-Release: %{?snapver:0.%{snapver}.}1%{?dist}
+Release: %{?snapver:0.%{snapver}.}2%{?dist}
 Group: System Environment/Base
 Url: http://www.rpm.org/
 Source0: http://rpm.org/releases/rpm-4.10.x/%{name}-%{srcver}.tar.bz2
@@ -45,6 +45,9 @@ Patch5: rpm-4.9.90-armhfp.patch
 Patch6: rpm-4.9.0-armhfp-logic.patch
 
 # Patches already in upstream
+Patch100: rpm-4.10.x-caps-double-free.patch
+Patch101: rpm-4.10.x-cursor-failchk.patch
+Patch102: rpm-4.10.x-db-serialize.patch
 
 # These are not yet upstream
 Patch301: rpm-4.6.0-niagara.patch
@@ -218,6 +221,10 @@ packages on a system.
 %patch3 -p1 -b .no-man-dirs
 %patch4 -p1 -b .use-gpg2
 
+%patch100 -p1 -b .caps-double-free
+%patch101 -p1 -b .cursor-failchk
+%patch102 -p1 -b .db-serialize
+
 %patch301 -p1 -b .niagara
 %patch302 -p1 -b .geode
 %patch304 -p1 -b .ldflags
@@ -448,6 +455,11 @@ exit 0
 %doc COPYING doc/librpm/html/*
 
 %changelog
+* Mon Aug 26 2013 Panu Matilainen <pmatilai at redhat.com> - 4.10.3.1-2
+- fix build-time double-free on file capability processing (#956190)
+- check for stale locks when opening write-cursors (#860500)
+- serialize BDB environment open/close (#924417)
+
 * Wed Feb 06 2013 Panu Matilainen <pmatilai at redhat.com> - 4.10.3.1-1
 - update to 4.10.3.1 (http://rpm.org/wiki/Releases/4.10.3.1)
 


More information about the scm-commits mailing list