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 0c15674a0e5a..eec9d4687629 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 a34bf9da0b71..992e2de1458c 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 e662f1b1a7b7..3c7c919cd11a 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 10d6bc3e13c6..c72fd5a05a82 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;