From fd13fff2de6479de5fcf6d73a37518f35547d811 Mon Sep 17 00:00:00 2001 From: Owen Green Date: Tue, 7 Apr 2020 15:54:06 +0100 Subject: [PATCH] Add strict argument count and type checking to messages, to avoid server crashes. --- include/FluidSCWrapper.hpp | 151 ++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/include/FluidSCWrapper.hpp b/include/FluidSCWrapper.hpp index 695cfc3..dec2057 100644 --- a/include/FluidSCWrapper.hpp +++ b/include/FluidSCWrapper.hpp @@ -510,6 +510,86 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase struct ParamReader { + static const char* oscTagToString(char tag) + { + switch (tag) + { + case 'i': return "integer"; break; + case 'f': return "float"; break; + case 'd': return "double"; break; + case 's': return "string"; break; + case 'b': return "blob"; break; + case 't': return "time tag"; break; + default: return "unknown type"; + } + } + + static const char* argTypeToString(std::string&) + { + return "string"; + } + + template + static std::enable_if_t::value, const char*> + argTypeToString(T&) + { + return "integer"; + } + + template + static std::enable_if_t::value, const char*> + argTypeToString(T&) + { + return "float"; + } + + static const char* argTypeToString(BufferT::type&) + { + return "buffer"; + } + + static const char* argTypeToString(InputBufferT::type&) + { + return "buffer"; + } + + template + static std::enable_if_t::value,const char*> + argTypeToString(P&) + { + return "shared_object"; //not ideal + } + + static bool argTypeOK(std::string&, char tag) + { + return tag == 's'; + } + + template + static std::enable_if_t::value + || std::is_floating_point::value, bool> + argTypeOK(T&, char tag) + { + return tag == 'i' || tag == 'f' || tag == 'd'; + } + + static bool argTypeOK(BufferT::type&, char tag) + { + return tag == 'i'; + } + + static bool argTypeOK(InputBufferT::type&, char tag) + { + return tag == 'i'; + } + + template + static std::enable_if_t::value,bool> + argTypeOK(P&, char tag) + { + return tag == 's'; + } + static auto fromArgs(World*, sc_msg_iter* args, std::string, int) { const char* recv = args->gets(""); @@ -785,14 +865,81 @@ class FluidSCWrapper : public impl::FluidSCWrapperBase auto ft = getInterfaceTable(); void* msgptr = ft->fRTAlloc(x->mWorld, sizeof(MessageData)); MessageData* msg = new (msgptr) MessageData; + msg->name = '/' + Client::getMessageDescriptors().template name(); + msg->wrapper = x; ArgTuple& args = msg->args; + + // type check OSC message + + std::string tags(inArgs->tags + inArgs->count); + bool willContinue = true; + bool typesMatch = true; + + constexpr size_t expectedArgCount = std::tuple_size::value; + + if(tags.size() > expectedArgCount) + { + std::cout << "WARNING: " << msg->name << " received more arguments than expected (got " + << tags.size() << ", expect " << expectedArgCount << ")\n"; + } + + if(tags.size() < expectedArgCount) + { + std::cout << "ERROR: " << msg->name << " received fewer arguments than expected (got " + << tags.size() << ", expect " << expectedArgCount << ")\n"; + willContinue = false; + } + + auto tagsIter = tags.begin(); + auto tagsEnd = tags.end(); + ForEach(args,[&typesMatch,&tagsIter,&tagsEnd](auto& x){ + if(tagsIter == tagsEnd) + { + typesMatch = false; + return; + } + char t = *(tagsIter++); + typesMatch = typesMatch && ParamReader::argTypeOK(x,t); + }); + + willContinue = willContinue && typesMatch; + + if(!typesMatch) + { + auto& report = std::cout; + report << "ERROR: " << msg->name << " type signature incorrect.\nExpect: ("; + size_t i{0}; + ForEach(args, [&i](auto& x){ + report << ParamReader::argTypeToString(x); + if(i < ( expectedArgCount - 1 ) ) + { + report << " ,"; + } + i++; + }); + report << ")\nReceived: ("; + i = 0; + for(auto t: tags) + { + report << ParamReader::oscTagToString(t); + if( i < ( tags.size() - 1 ) ) + { + report << ", "; + } + i++; + } + report << ")\n"; + } + + if(!willContinue) return; + + /// (void) std::initializer_list{ (std::get(args) = ParamReader::fromArgs( x->mWorld, inArgs, std::get(args), 0), 0)...}; - msg->name = '/' + Client::getMessageDescriptors().template name(); - msg->wrapper = x; + x->mDone = false; ft->fDoAsynchronousCommand( x->mWorld, nullptr, getName(), msg,