| // Copyright 2014 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <brillo/dbus/data_serialization.h> | |
| #include <sys/wait.h> | |
| #include <unistd.h> | |
| #include <base/check.h> | |
| #include <base/logging.h> | |
| #include <base/posix/eintr_wrapper.h> | |
| #include <brillo/any.h> | |
| #include <brillo/variant_dictionary.h> | |
| namespace brillo::dbus_utils { | |
| namespace details { | |
| namespace { | |
| AutoVariantUnwrapState g_auto_variant_unwrap_state = | |
| AutoVariantUnwrapState::kDumpWithoutCrash; | |
| } // namespace | |
| bool DescendIntoVariantIfPresent(::dbus::MessageReader** reader_ref, | |
| ::dbus::MessageReader* variant_reader, | |
| bool for_any) { | |
| if ((*reader_ref)->GetDataType() != ::dbus::Message::VARIANT) | |
| return true; | |
| if (!for_any) { | |
| switch (g_auto_variant_unwrap_state) { | |
| case AutoVariantUnwrapState::kEnabled: | |
| // Do nothing. | |
| break; | |
| case AutoVariantUnwrapState::kDumpWithoutCrash: { | |
| // TODO(b/289932268): Callers of this function, which is often | |
| // message readers, should know the schema of the argument, | |
| // so unwrapping VARIANT should be done explicitly, i.e., this part | |
| // should not be executed conceptually. Though, unfortunately, this | |
| // function is (also) used in generated code, and so currently there | |
| // are many callsites, and fixing each caller one-by-one is not so | |
| // easy. | |
| // To be safer, record crash log here to see whether there are no | |
| // unexpected uses. | |
| // base::DumpWithoutCrash won't work with platform packages, so we | |
| // fork then let the child process crash to make a crash report. | |
| // | |
| // If the process has a seccomp policy active and doesn't allow fork, | |
| // then we'll crash immediately instead of in the background, | |
| // but it's not feasible to detect, and this situation in general | |
| // should be uncommon enough. | |
| pid_t pid = fork(); | |
| if (pid == 0) { | |
| // Let the child process just crash. | |
| RAW_LOG(FATAL, "Variant unwrapping is done unexpectedly"); | |
| } | |
| // Collect the crashed child process. | |
| // Do it as a blocking operation, since this is unexpected so we do not | |
| // worry about its performance. | |
| auto ret = HANDLE_EINTR(waitpid(pid, nullptr, 0)); | |
| if (ret != 0) { | |
| PLOG(ERROR) << "failed on waitpid(" << pid << ")"; | |
| } | |
| break; | |
| } | |
| case AutoVariantUnwrapState::kDisabled: | |
| // Unexpected variant, but return as "succeeded" without do anything, | |
| // So this function will look "no-op" from callers. | |
| // Often, following message reading calls will fail due to type | |
| // mismatch. | |
| LOG(ERROR) << "Unexpceted variant unwrap"; | |
| return true; | |
| } | |
| } | |
| if (!(*reader_ref)->PopVariant(variant_reader)) | |
| return false; | |
| *reader_ref = variant_reader; | |
| return true; | |
| } | |
| } // namespace details | |
| void AppendValueToWriter(dbus::MessageWriter* writer, bool value) { | |
| writer->AppendBool(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, uint8_t value) { | |
| writer->AppendByte(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, int16_t value) { | |
| writer->AppendInt16(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, uint16_t value) { | |
| writer->AppendUint16(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, int32_t value) { | |
| writer->AppendInt32(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, uint32_t value) { | |
| writer->AppendUint32(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, int64_t value) { | |
| writer->AppendInt64(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, uint64_t value) { | |
| writer->AppendUint64(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, double value) { | |
| writer->AppendDouble(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, | |
| const std::string& value) { | |
| writer->AppendString(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, const char* value) { | |
| AppendValueToWriter(writer, std::string(value)); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, | |
| const dbus::ObjectPath& value) { | |
| writer->AppendObjectPath(value); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, | |
| const base::ScopedFD& value) { | |
| writer->AppendFileDescriptor(value.get()); | |
| } | |
| void AppendValueToWriter(dbus::MessageWriter* writer, | |
| const brillo::Any& value) { | |
| value.AppendToDBusMessageWriter(writer); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////// | |
| bool PopValueFromReader(dbus::MessageReader* reader, bool* value) { | |
| return reader->PopBool(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, uint8_t* value) { | |
| return reader->PopByte(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, int16_t* value) { | |
| return reader->PopInt16(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, uint16_t* value) { | |
| return reader->PopUint16(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, int32_t* value) { | |
| return reader->PopInt32(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, uint32_t* value) { | |
| return reader->PopUint32(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, int64_t* value) { | |
| return reader->PopInt64(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, uint64_t* value) { | |
| return reader->PopUint64(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, double* value) { | |
| return reader->PopDouble(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, std::string* value) { | |
| return reader->PopString(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, dbus::ObjectPath* value) { | |
| return reader->PopObjectPath(value); | |
| } | |
| bool PopValueFromReader(dbus::MessageReader* reader, base::ScopedFD* value) { | |
| return reader->PopFileDescriptor(value); | |
| } | |
| namespace { | |
| // Helper methods for PopValueFromReader(dbus::MessageReader*, Any*) | |
| // implementation. Pops a value of particular type from |reader| and assigns | |
| // it to |value| of type Any. | |
| template <typename T> | |
| bool PopTypedValueFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| T data{}; | |
| if (!PopValueFromReader(reader, &data)) | |
| return false; | |
| *value = std::move(data); | |
| return true; | |
| } | |
| // std::vector<T> overload. | |
| template <typename T> | |
| bool PopTypedArrayFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| return PopTypedValueFromReader<std::vector<T>>(reader, value); | |
| } | |
| // std::map<KEY, VALUE> overload. | |
| template <typename KEY, typename VALUE> | |
| bool PopTypedMapFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| return PopTypedValueFromReader<std::map<KEY, VALUE>>(reader, value); | |
| } | |
| // Helper methods for reading common ARRAY signatures into a Variant. | |
| // Note that only common types are supported. If an additional specific | |
| // type signature is required, feel free to add support for it. | |
| bool PopArrayValueFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| std::string signature = reader->GetDataSignature(); | |
| if (signature == "ab") | |
| return PopTypedArrayFromReader<bool>(reader, value); | |
| else if (signature == "ay") | |
| return PopTypedArrayFromReader<uint8_t>(reader, value); | |
| else if (signature == "an") | |
| return PopTypedArrayFromReader<int16_t>(reader, value); | |
| else if (signature == "aq") | |
| return PopTypedArrayFromReader<uint16_t>(reader, value); | |
| else if (signature == "ai") | |
| return PopTypedArrayFromReader<int32_t>(reader, value); | |
| else if (signature == "au") | |
| return PopTypedArrayFromReader<uint32_t>(reader, value); | |
| else if (signature == "ax") | |
| return PopTypedArrayFromReader<int64_t>(reader, value); | |
| else if (signature == "at") | |
| return PopTypedArrayFromReader<uint64_t>(reader, value); | |
| else if (signature == "ad") | |
| return PopTypedArrayFromReader<double>(reader, value); | |
| else if (signature == "as") | |
| return PopTypedArrayFromReader<std::string>(reader, value); | |
| else if (signature == "ao") | |
| return PopTypedArrayFromReader<dbus::ObjectPath>(reader, value); | |
| else if (signature == "av") | |
| return PopTypedArrayFromReader<brillo::Any>(reader, value); | |
| else if (signature == "a{ss}") | |
| return PopTypedMapFromReader<std::string, std::string>(reader, value); | |
| else if (signature == "a{sv}") | |
| return PopTypedValueFromReader<brillo::VariantDictionary>(reader, value); | |
| else if (signature == "aa{ss}") | |
| return PopTypedArrayFromReader<std::map<std::string, std::string>>(reader, | |
| value); | |
| else if (signature == "aay") | |
| return PopTypedArrayFromReader<std::vector<uint8_t>>(reader, value); | |
| else if (signature == "aa{sv}") | |
| return PopTypedArrayFromReader<brillo::VariantDictionary>(reader, value); | |
| else if (signature == "a{sa{ss}}") | |
| return PopTypedMapFromReader<std::string, | |
| std::map<std::string, std::string>>(reader, | |
| value); | |
| else if (signature == "a{sa{sv}}") | |
| return PopTypedMapFromReader<std::string, brillo::VariantDictionary>(reader, | |
| value); | |
| else if (signature == "a{qay}") | |
| return PopTypedMapFromReader<uint16_t, std::vector<uint8_t>>(reader, value); | |
| else if (signature == "a{say}") | |
| return PopTypedMapFromReader<std::string, std::vector<uint8_t>>(reader, | |
| value); | |
| else if (signature == "a{uv}") | |
| return PopTypedMapFromReader<uint32_t, brillo::Any>(reader, value); | |
| else if (signature == "a(su)") | |
| return PopTypedArrayFromReader<std::tuple<std::string, uint32_t>>(reader, | |
| value); | |
| else if (signature == "a{uu}") | |
| return PopTypedMapFromReader<uint32_t, uint32_t>(reader, value); | |
| else if (signature == "a(uu)") | |
| return PopTypedArrayFromReader<std::tuple<uint32_t, uint32_t>>(reader, | |
| value); | |
| else if (signature == "a(ubay)") | |
| return PopTypedArrayFromReader< | |
| std::tuple<uint32_t, bool, std::vector<uint8_t>>>(reader, value); | |
| // When a use case for particular array signature is found, feel free | |
| // to add handing for it here. | |
| LOG(ERROR) << "Variant de-serialization of array containing data of " | |
| << "type '" << signature << "' is not yet supported"; | |
| return false; | |
| } | |
| // Helper methods for reading common STRUCT signatures into a Variant. | |
| // Note that only common types are supported. If an additional specific | |
| // type signature is required, feel free to add support for it. | |
| bool PopStructValueFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| std::string signature = reader->GetDataSignature(); | |
| if (signature == "(ii)") | |
| return PopTypedValueFromReader<std::tuple<int, int>>(reader, value); | |
| else if (signature == "(ss)") | |
| return PopTypedValueFromReader<std::tuple<std::string, std::string>>(reader, | |
| value); | |
| else if (signature == "(ub)") | |
| return PopTypedValueFromReader<std::tuple<uint32_t, bool>>(reader, value); | |
| else if (signature == "(uu)") | |
| return PopTypedValueFromReader<std::tuple<uint32_t, uint32_t>>(reader, | |
| value); | |
| else if (signature == "(ua{sv})") | |
| return PopTypedValueFromReader< | |
| std::tuple<uint32_t, brillo::VariantDictionary>>(reader, value); | |
| // When a use case for particular struct signature is found, feel free | |
| // to add handing for it here. | |
| LOG(ERROR) << "Variant de-serialization of structs of type '" << signature | |
| << "' is not yet supported"; | |
| return false; | |
| } | |
| } // anonymous namespace | |
| bool PopValueFromReader(dbus::MessageReader* reader, brillo::Any* value) { | |
| dbus::MessageReader variant_reader(nullptr); | |
| if (!details::DescendIntoVariantIfPresent(&reader, &variant_reader, | |
| /*for_any=*/true)) | |
| return false; | |
| switch (reader->GetDataType()) { | |
| case dbus::Message::BYTE: | |
| return PopTypedValueFromReader<uint8_t>(reader, value); | |
| case dbus::Message::BOOL: | |
| return PopTypedValueFromReader<bool>(reader, value); | |
| case dbus::Message::INT16: | |
| return PopTypedValueFromReader<int16_t>(reader, value); | |
| case dbus::Message::UINT16: | |
| return PopTypedValueFromReader<uint16_t>(reader, value); | |
| case dbus::Message::INT32: | |
| return PopTypedValueFromReader<int32_t>(reader, value); | |
| case dbus::Message::UINT32: | |
| return PopTypedValueFromReader<uint32_t>(reader, value); | |
| case dbus::Message::INT64: | |
| return PopTypedValueFromReader<int64_t>(reader, value); | |
| case dbus::Message::UINT64: | |
| return PopTypedValueFromReader<uint64_t>(reader, value); | |
| case dbus::Message::DOUBLE: | |
| return PopTypedValueFromReader<double>(reader, value); | |
| case dbus::Message::STRING: | |
| return PopTypedValueFromReader<std::string>(reader, value); | |
| case dbus::Message::OBJECT_PATH: | |
| return PopTypedValueFromReader<dbus::ObjectPath>(reader, value); | |
| case dbus::Message::ARRAY: | |
| return PopArrayValueFromReader(reader, value); | |
| case dbus::Message::STRUCT: | |
| return PopStructValueFromReader(reader, value); | |
| case dbus::Message::DICT_ENTRY: | |
| LOG(ERROR) << "Variant of DICT_ENTRY is invalid"; | |
| return false; | |
| case dbus::Message::VARIANT: | |
| LOG(ERROR) << "Variant containing a variant is invalid"; | |
| return false; | |
| case dbus::Message::UNIX_FD: | |
| CHECK(dbus::IsDBusTypeUnixFdSupported()) << "UNIX_FD data not supported"; | |
| // File descriptors don't use copyable types. Cannot be returned via | |
| // brillo::Any. Fail here. | |
| LOG(ERROR) << "Cannot return FileDescriptor via Any"; | |
| return false; | |
| default: | |
| LOG(FATAL) << "Unknown D-Bus data type: " << variant_reader.GetDataType(); | |
| } | |
| } | |
| void SetAutoVariantUnwrapState(AutoVariantUnwrapState state) { | |
| details::g_auto_variant_unwrap_state = state; | |
| } | |
| AutoVariantUnwrapState GetAutoVariantUnwrapState() { | |
| return details::g_auto_variant_unwrap_state; | |
| } | |
| } // namespace brillo::dbus_utils |