If the format argument to a command sent to the admin socket is not
among the supported formats ( json, json-pretty, xml, xml-pretty ) the
new_formatter function will return null and the AdminSocketHook::call
function must fall back to a sensible default.
The CephContextHook::call and HelpHook::call failed to do that and a
malformed format argument would cause the mon to crash. A check is added
to each of them and fallback to json-pretty if the format is not
recognized.
To further protect AdminSocketHook::call implementations from similar
problems the format argument is checked immediately after accepting the
command in AdminSocket::do_accept and replaced with json-pretty if it is
not known.
A test case is added for both CephContextHook::call and HelpHook::call
to demonstrate the problem exists and is fixed by the patch.
Three other instances of unsafe calls to new_formatter were found and
a fallback to json-pretty was added. All other calls have been audited
and appear to be safe.
http://tracker.ceph.com/issues/7378 fixes #7378
Signed-off-by: Loic Dachary <loic@dachary.org>
(cherry picked from commit
165e76d4d03ffcc490fd3c2ba60fb37372990d0a)
{
stringstream ss;
Formatter *f = new_formatter(format);
+ if (!f)
+ f = new_formatter("json-pretty");
f->open_object_section("result");
m_client->client_lock.Lock();
if (command == "mds_requests")
return false;
}
cmd_getval(m_cct, cmdmap, "format", format);
+ if (format != "json" && format != "json-pretty" &&
+ format != "xml" && format != "xml-pretty")
+ format = "json-pretty";
cmd_getval(m_cct, cmdmap, "prefix", c);
string firstword;
HelpHook(AdminSocket *as) : m_as(as) {}
bool call(string command, cmdmap_t &cmdmap, string format, bufferlist& out) {
Formatter *f = new_formatter(format);
+ if (!f)
+ f = new_formatter("json-pretty");
f->open_object_section("help");
for (map<string,string>::iterator p = m_as->m_help.begin();
p != m_as->m_help.end();
std::string format, bufferlist *out)
{
Formatter *f = new_formatter(format);
+ if (!f)
+ f = new_formatter("json-pretty");
stringstream ss;
for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {
if (it->first != "prefix") {
bool OSD::asok_command(string command, cmdmap_t& cmdmap, string format,
ostream& ss)
{
- if (format == "")
- format = "json-pretty";
Formatter *f = new_formatter(format);
+ if (!f)
+ f = new_formatter("json-pretty");
if (command == "dump_ops_in_flight") {
op_tracker.dump_ops_in_flight(f);
} else if (command == "dump_historic_ops") {
{
stringstream ss;
Formatter *f = new_formatter(format);
+ if (!f)
+ f = new_formatter("json-pretty");
m_objecter->client_lock.Lock();
m_objecter->dump_requests(f);
m_objecter->client_lock.Unlock();
unittest_confutils_CXXFLAGS = $(UNITTEST_CXXFLAGS)
check_PROGRAMS += unittest_confutils
+unittest_context_SOURCES = test/common/test_context.cc
+unittest_context_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)
+unittest_context_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+check_PROGRAMS += unittest_context
+
unittest_heartbeatmap_SOURCES = test/heartbeat_map.cc
unittest_heartbeatmap_LDADD = $(LIBCOMMON) $(UNITTEST_LDADD) $(CEPH_GLOBAL)
unittest_heartbeatmap_CXXFLAGS = $(UNITTEST_CXXFLAGS)
ASSERT_EQ(true, asoct.shutdown());
}
+TEST(AdminSocket, SendHelp) {
+ std::auto_ptr<AdminSocket>
+ asokc(new AdminSocket(g_ceph_context));
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"help\"}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"xml\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find(">list available commands<"));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"UNSUPPORTED\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
TEST(AdminSocket, SendNoOp) {
std::auto_ptr<AdminSocket>
asokc(new AdminSocket(g_ceph_context));
ASSERT_EQ("test| this thing", result);
ASSERT_EQ(true, asoct.shutdown());
}
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ;
+ * make unittest_admin_socket &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_admin_socket # --gtest_filter=AdminSocket.*
+ * "
+ * End:
+ */
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ *
+ */
+#include "gtest/gtest.h"
+#include "include/types.h"
+#include "include/msgr.h"
+#include "common/ceph_context.h"
+#include "common/config.h"
+
+TEST(CephContext, do_command)
+{
+ CephContext *cct = (new CephContext(CEPH_ENTITY_TYPE_CLIENT))->get();
+
+ string key("key");
+ string value("value");
+ cct->_conf->set_val(key.c_str(), value.c_str(), false);
+ cmdmap_t cmdmap;
+ cmdmap["var"] = key;
+
+ {
+ bufferlist out;
+ cct->do_command("config get", cmdmap, "xml", &out);
+ string s(out.c_str(), out.length());
+ EXPECT_EQ("<config get><key>" + value + "</key></config get>", s);
+ }
+
+ {
+ bufferlist out;
+ cct->do_command("config get", cmdmap, "UNSUPPORTED", &out);
+ string s(out.c_str(), out.length());
+ EXPECT_EQ("{ \"key\": \"value\"}", s);
+ }
+
+ cct->put();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ;
+ * make unittest_context &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_context # --gtest_filter=CephContext.*
+ * "
+ * End:
+ */