Michal Schmidt bbee01
From 563ba9ea6e60774086555998b957edf923e24b46 Mon Sep 17 00:00:00 2001
Michal Schmidt bbee01
From: Michal Schmidt <mschmidt@redhat.com>
Michal Schmidt bbee01
Date: Mon, 17 Oct 2011 11:12:12 +0200
Michal Schmidt bbee01
Subject: [PATCH 2/5] manager: fix a crash in isolating
Michal Schmidt bbee01
Michal Schmidt bbee01
HASHMAP_FOREACH is safe against the removal of the current entry, but
Michal Schmidt bbee01
not against the removal of other entries. job_finish_and_invalidate()
Michal Schmidt bbee01
can recursively remove other entries.
Michal Schmidt bbee01
Michal Schmidt bbee01
It triggered an assertion failure:
Michal Schmidt bbee01
  Assertion 'j->installed' failed at src/manager.c:1218, function
Michal Schmidt bbee01
  transaction_apply(). Aborting.
Michal Schmidt bbee01
Michal Schmidt bbee01
Fix the crash by iterating from the beginning when there is a
Michal Schmidt bbee01
possibility that the iterator could be invalid.
Michal Schmidt bbee01
Michal Schmidt bbee01
It is O(n^2) in the worst case, but that's better than a crash.
Michal Schmidt bbee01
Michal Schmidt bbee01
https://bugzilla.redhat.com/show_bug.cgi?id=717325
Michal Schmidt bbee01
---
Michal Schmidt bbee01
 src/job.c     |   19 ++++++++++++++-----
Michal Schmidt bbee01
 src/manager.c |    7 ++++++-
Michal Schmidt bbee01
 2 files changed, 20 insertions(+), 6 deletions(-)
Michal Schmidt bbee01
Michal Schmidt bbee01
diff --git a/src/job.c b/src/job.c
Michal Schmidt bbee01
index 5c0913b..20971da 100644
Michal Schmidt bbee01
--- a/src/job.c
Michal Schmidt bbee01
+++ b/src/job.c
Michal Schmidt bbee01
@@ -527,6 +527,7 @@ int job_finish_and_invalidate(Job *j, JobResult result) {
Michal Schmidt bbee01
         Unit *other;
Michal Schmidt bbee01
         JobType t;
Michal Schmidt bbee01
         Iterator i;
Michal Schmidt bbee01
+        bool recursed = false;
Michal Schmidt bbee01
 
Michal Schmidt bbee01
         assert(j);
Michal Schmidt bbee01
         assert(j->installed);
Michal Schmidt bbee01
@@ -573,23 +574,29 @@ int job_finish_and_invalidate(Job *j, JobResult result) {
Michal Schmidt bbee01
                                 if (other->meta.job &&
Michal Schmidt bbee01
                                     (other->meta.job->type == JOB_START ||
Michal Schmidt bbee01
                                      other->meta.job->type == JOB_VERIFY_ACTIVE ||
Michal Schmidt bbee01
-                                     other->meta.job->type == JOB_RELOAD_OR_START))
Michal Schmidt bbee01
+                                     other->meta.job->type == JOB_RELOAD_OR_START)) {
Michal Schmidt bbee01
                                         job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY);
Michal Schmidt bbee01
+                                        recursed = true;
Michal Schmidt bbee01
+                                }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
                         SET_FOREACH(other, u->meta.dependencies[UNIT_BOUND_BY], i)
Michal Schmidt bbee01
                                 if (other->meta.job &&
Michal Schmidt bbee01
                                     (other->meta.job->type == JOB_START ||
Michal Schmidt bbee01
                                      other->meta.job->type == JOB_VERIFY_ACTIVE ||
Michal Schmidt bbee01
-                                     other->meta.job->type == JOB_RELOAD_OR_START))
Michal Schmidt bbee01
+                                     other->meta.job->type == JOB_RELOAD_OR_START)) {
Michal Schmidt bbee01
                                         job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY);
Michal Schmidt bbee01
+                                        recursed = true;
Michal Schmidt bbee01
+                                }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
                         SET_FOREACH(other, u->meta.dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
Michal Schmidt bbee01
                                 if (other->meta.job &&
Michal Schmidt bbee01
                                     !other->meta.job->override &&
Michal Schmidt bbee01
                                     (other->meta.job->type == JOB_START ||
Michal Schmidt bbee01
                                      other->meta.job->type == JOB_VERIFY_ACTIVE ||
Michal Schmidt bbee01
-                                     other->meta.job->type == JOB_RELOAD_OR_START))
Michal Schmidt bbee01
+                                     other->meta.job->type == JOB_RELOAD_OR_START)) {
Michal Schmidt bbee01
                                         job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY);
Michal Schmidt bbee01
+                                        recursed = true;
Michal Schmidt bbee01
+                                }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
                 } else if (t == JOB_STOP) {
Michal Schmidt bbee01
 
Michal Schmidt bbee01
@@ -597,8 +604,10 @@ int job_finish_and_invalidate(Job *j, JobResult result) {
Michal Schmidt bbee01
                                 if (other->meta.job &&
Michal Schmidt bbee01
                                     (other->meta.job->type == JOB_START ||
Michal Schmidt bbee01
                                      other->meta.job->type == JOB_VERIFY_ACTIVE ||
Michal Schmidt bbee01
-                                     other->meta.job->type == JOB_RELOAD_OR_START))
Michal Schmidt bbee01
+                                     other->meta.job->type == JOB_RELOAD_OR_START)) {
Michal Schmidt bbee01
                                         job_finish_and_invalidate(other->meta.job, JOB_DEPENDENCY);
Michal Schmidt bbee01
+                                        recursed = true;
Michal Schmidt bbee01
+                                }
Michal Schmidt bbee01
                 }
Michal Schmidt bbee01
         }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
@@ -626,7 +635,7 @@ finish:
Michal Schmidt bbee01
 
Michal Schmidt bbee01
         manager_check_finished(u->meta.manager);
Michal Schmidt bbee01
 
Michal Schmidt bbee01
-        return 0;
Michal Schmidt bbee01
+        return recursed;
Michal Schmidt bbee01
 }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
 int job_start_timer(Job *j) {
Michal Schmidt bbee01
diff --git a/src/manager.c b/src/manager.c
Michal Schmidt bbee01
index e626347..6d20258 100644
Michal Schmidt bbee01
--- a/src/manager.c
Michal Schmidt bbee01
+++ b/src/manager.c
Michal Schmidt bbee01
@@ -1214,13 +1214,18 @@ static int transaction_apply(Manager *m, JobMode mode) {
Michal Schmidt bbee01
 
Michal Schmidt bbee01
                 /* When isolating first kill all installed jobs which
Michal Schmidt bbee01
                  * aren't part of the new transaction */
Michal Schmidt bbee01
+        rescan:
Michal Schmidt bbee01
                 HASHMAP_FOREACH(j, m->jobs, i) {
Michal Schmidt bbee01
                         assert(j->installed);
Michal Schmidt bbee01
 
Michal Schmidt bbee01
                         if (hashmap_get(m->transaction_jobs, j->unit))
Michal Schmidt bbee01
                                 continue;
Michal Schmidt bbee01
 
Michal Schmidt bbee01
-                        job_finish_and_invalidate(j, JOB_CANCELED);
Michal Schmidt bbee01
+                        /* 'j' itself is safe to remove, but if other jobs
Michal Schmidt bbee01
+                           are invalidated recursively, our iterator may become
Michal Schmidt bbee01
+                           invalid and we need to start over. */
Michal Schmidt bbee01
+                        if (job_finish_and_invalidate(j, JOB_CANCELED) > 0)
Michal Schmidt bbee01
+                                goto rescan;
Michal Schmidt bbee01
                 }
Michal Schmidt bbee01
         }
Michal Schmidt bbee01
 
Michal Schmidt bbee01
-- 
Michal Schmidt bbee01
1.7.4.4
Michal Schmidt bbee01