From: John Spray Date: Mon, 25 Jul 2016 11:59:19 +0000 (+0100) Subject: mon: validate states transmitted in beacons X-Git-Tag: ses5-milestone5~271^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F10428%2Fhead;p=ceph.git mon: validate states transmitted in beacons Since FSMap was added, the state of a daemon can lead to an entirely invalid map, but we were letting daemons send any state they wanted. Especially, we must not allow standby daemons to set any state other than STANDBY. Fixes: http://tracker.ceph.com/issues/16592 Signed-off-by: John Spray --- diff --git a/src/mds/MDSMap.cc b/src/mds/MDSMap.cc index 0c15674a0e5..eec9d468762 100644 --- a/src/mds/MDSMap.cc +++ b/src/mds/MDSMap.cc @@ -727,3 +727,29 @@ MDSMap::availability_t MDSMap::is_cluster_available() const return STUCK_UNAVAILABLE; } } + +bool MDSMap::state_transition_valid(DaemonState prev, DaemonState next) +{ + bool state_valid = true; + if (next != prev) { + if (prev == MDSMap::STATE_REPLAY) { + if (next != MDSMap::STATE_RESOLVE && next != MDSMap::STATE_RECONNECT) { + state_valid = false; + } + } else if (prev == MDSMap::STATE_REJOIN) { + if (next != MDSMap::STATE_ACTIVE + && next != MDSMap::STATE_CLIENTREPLAY + && next != MDSMap::STATE_STOPPED) { + state_valid = false; + } + } else if (prev >= MDSMap::STATE_RECONNECT && prev < MDSMap::STATE_ACTIVE) { + // Once I have entered replay, the only allowable transitions are to + // the next next along in the sequence. + if (next != prev + 1) { + state_valid = false; + } + } + } + + return state_valid; +} diff --git a/src/mds/MDSMap.h b/src/mds/MDSMap.h index a34bf9da0b7..992e2de1458 100644 --- a/src/mds/MDSMap.h +++ b/src/mds/MDSMap.h @@ -628,6 +628,8 @@ public: void dump(Formatter *f) const; static void generate_test_instances(list& ls); + + static bool state_transition_valid(DaemonState prev, DaemonState next); }; WRITE_CLASS_ENCODER_FEATURES(MDSMap::mds_info_t) WRITE_CLASS_ENCODER_FEATURES(MDSMap) diff --git a/src/mds/MDSRank.cc b/src/mds/MDSRank.cc index e662f1b1a7b..3c7c919cd11 100644 --- a/src/mds/MDSRank.cc +++ b/src/mds/MDSRank.cc @@ -1409,28 +1409,7 @@ void MDSRankDispatcher::handle_mds_map( } // Validate state transitions while I hold a rank - bool state_valid = true; - if (state != oldstate) { - if (oldstate == MDSMap::STATE_REPLAY) { - if (state != MDSMap::STATE_RESOLVE && state != MDSMap::STATE_RECONNECT) { - state_valid = false; - } - } else if (oldstate == MDSMap::STATE_REJOIN) { - if (state != MDSMap::STATE_ACTIVE - && state != MDSMap::STATE_CLIENTREPLAY - && state != MDSMap::STATE_STOPPED) { - state_valid = false; - } - } else if (oldstate >= MDSMap::STATE_RECONNECT && oldstate < MDSMap::STATE_ACTIVE) { - // Once I have entered replay, the only allowable transitions are to - // the next state along in the sequence. - if (state != oldstate + 1) { - state_valid = false; - } - } - } - - if (!state_valid) { + if (!MDSMap::state_transition_valid(oldstate, state)) { derr << "Invalid state transition " << ceph_mds_state_name(oldstate) << "->" << ceph_mds_state_name(state) << dendl; respawn(); diff --git a/src/mon/MDSMonitor.cc b/src/mon/MDSMonitor.cc index 10d6bc3e13c..c72fd5a05a8 100644 --- a/src/mon/MDSMonitor.cc +++ b/src/mon/MDSMonitor.cc @@ -645,7 +645,23 @@ bool MDSMonitor::prepare_beacon(MonOpRequestRef op) mon->monmap->fsid, m->get_global_id(), m->get_name(), fsmap.get_epoch(), state, seq, CEPH_FEATURES_SUPPORTED_DEFAULT)); + } else if (info.state == MDSMap::STATE_STANDBY && state != info.state) { + // Standby daemons should never modify their own + // state. Reject any attempts to do so. + derr << "standby " << gid << " attempted to change state to " + << ceph_mds_state_name(state) << ", rejecting" << dendl; + return true; + } else if (info.state != MDSMap::STATE_STANDBY && state != info.state && + !MDSMap::state_transition_valid(info.state, state)) { + // Validate state transitions for daemons that hold a rank + derr << "daemon " << gid << " (rank " << info.rank << ") " + << "reported invalid state transition " + << ceph_mds_state_name(info.state) << " -> " + << ceph_mds_state_name(state) << dendl; + return true; } else { + // Made it through special cases and validations, record the + // daemon's reported state to the FSMap. pending_fsmap.modify_daemon(gid, [state, seq](MDSMap::mds_info_t *info) { info->state = state; info->state_seq = seq;