Add a check whether or not a str is utf8 prior to emplacing
authorNathan Lanza <nathan@lanza.io>
Thu, 15 Nov 2018 19:49:57 +0000 (19:49 +0000)
committerNathan Lanza <nathan@lanza.io>
Thu, 15 Nov 2018 19:49:57 +0000 (19:49 +0000)
Summary:
Highlighing junk data on VSCode can send a query for evaluate which
fails. In particular cases on Windows, this the error message can end
up as a c-string of [-35,-35,-35,-35,...]. Attempting to emplace this
as the error message causes an assert failure.

Prior to emplacing the error message, confirm that it is valid UTF8 to
eliminate errors such as mentione above.

Reviewers: xiaobai, clayborg

Reviewed By: clayborg

Differential Revision: https://reviews.llvm.org/D53008

llvm-svn: 346988

lldb/tools/lldb-vscode/JSONUtils.cpp
lldb/tools/lldb-vscode/JSONUtils.h
lldb/tools/lldb-vscode/VSCode.cpp
lldb/tools/lldb-vscode/lldb-vscode.cpp

index 019a9db..e80d9fc 100644 (file)
 
 namespace lldb_vscode {
 
+void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
+                       llvm::StringRef str) {
+  if (LLVM_LIKELY(llvm::json::isUTF8(str)))
+    obj.try_emplace(key, str.str());
+  else
+    obj.try_emplace(key, llvm::json::fixUTF8(str));
+}
+
 llvm::StringRef GetAsString(const llvm::json::Value &value) {
   if (auto s = value.getAsString())
     return *s;
@@ -124,11 +132,11 @@ std::vector<std::string> GetStrings(const llvm::json::Object *obj,
 
 void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
                     llvm::StringRef key) {
-  
+
   llvm::StringRef value = v.GetValue();
   llvm::StringRef summary = v.GetSummary();
   llvm::StringRef type_name = v.GetType().GetDisplayTypeName();
-  
+
   std::string result;
   llvm::raw_string_ostream strm(result);
   if (!value.empty()) {
@@ -144,7 +152,7 @@ void SetValueForKey(lldb::SBValue &v, llvm::json::Object &object,
       strm << " @ " << llvm::format_hex(address, 0);
   }
   strm.flush();
-  object.try_emplace(key, result);
+  EmplaceSafeString(object, key, result);
 }
 
 void FillResponse(const llvm::json::Object &request,
@@ -153,7 +161,7 @@ void FillResponse(const llvm::json::Object &request,
   // to true by default.
   response.try_emplace("type", "response");
   response.try_emplace("seq", (int64_t)0);
-  response.try_emplace("command", GetString(request, "command"));
+  EmplaceSafeString(response, "command", GetString(request, "command"));
   const int64_t seq = GetSigned(request, "seq", 0);
   response.try_emplace("request_seq", seq);
   response.try_emplace("success", true);
@@ -223,7 +231,7 @@ llvm::json::Value CreateScope(const llvm::StringRef name,
                               int64_t variablesReference,
                               int64_t namedVariables, bool expensive) {
   llvm::json::Object object;
-  object.try_emplace("name", name.str());
+  EmplaceSafeString(object, "name", name.str());
   object.try_emplace("variablesReference", variablesReference);
   object.try_emplace("expensive", expensive);
   object.try_emplace("namedVariables", namedVariables);
@@ -357,7 +365,7 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
   llvm::json::Object event;
   event.try_emplace("seq", 0);
   event.try_emplace("type", "event");
-  event.try_emplace("event", event_name);
+  EmplaceSafeString(event, "event", event_name);
   return event;
 }
 
@@ -388,8 +396,8 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
 llvm::json::Value
 CreateExceptionBreakpointFilter(const ExceptionBreakpoint &bp) {
   llvm::json::Object object;
-  object.try_emplace("filter", bp.filter);
-  object.try_emplace("label", bp.label);
+  EmplaceSafeString(object, "filter", bp.filter);
+  EmplaceSafeString(object, "label", bp.label);
   object.try_emplace("default", bp.default_value);
   return llvm::json::Value(std::move(object));
 }
@@ -467,11 +475,11 @@ llvm::json::Value CreateSource(lldb::SBLineEntry &line_entry) {
   if (file.IsValid()) {
     const char *name = file.GetFilename();
     if (name)
-      object.try_emplace("name", name);
+      EmplaceSafeString(object, "name", name);
     char path[PATH_MAX] = "";
     file.GetPath(path, sizeof(path));
     if (path[0]) {
-      object.try_emplace("path", std::string(path));
+      EmplaceSafeString(object, "path", std::string(path));
     }
   }
   return llvm::json::Value(std::move(object));
@@ -517,7 +525,7 @@ llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
   }
   const auto num_insts = insts.GetSize();
   if (low_pc != LLDB_INVALID_ADDRESS && num_insts > 0) {
-    object.try_emplace("name", frame.GetFunctionName());
+    EmplaceSafeString(object, "name", frame.GetFunctionName());
     SourceReference source;
     llvm::raw_string_ostream src_strm(source.content);
     std::string line;
@@ -540,8 +548,8 @@ llvm::json::Value CreateSource(lldb::SBFrame &frame, int64_t &disasm_line) {
       line.clear();
       llvm::raw_string_ostream line_strm(line);
       line_strm << llvm::formatv("{0:X+}: <{1}> {2} {3,12} {4}", inst_addr,
-                                 inst_offset, llvm::fmt_repeat(' ', spaces),
-                                 m, o);
+                                 inst_offset, llvm::fmt_repeat(' ', spaces), m,
+                                 o);
 
       // If there is a comment append it starting at column 60 or after one
       // space past the last char
@@ -626,7 +634,7 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
   llvm::json::Object object;
   int64_t frame_id = MakeVSCodeFrameID(frame);
   object.try_emplace("id", frame_id);
-  object.try_emplace("name", frame.GetFunctionName());
+  EmplaceSafeString(object, "name", frame.GetFunctionName());
   int64_t disasm_line = 0;
   object.try_emplace("source", CreateSource(frame, disasm_line));
 
@@ -670,9 +678,9 @@ llvm::json::Value CreateThread(lldb::SBThread &thread) {
     std::string thread_with_name(thread_str);
     thread_with_name += ' ';
     thread_with_name += name;
-    object.try_emplace("name", thread_with_name);
+    EmplaceSafeString(object, "name", thread_with_name);
   } else {
-    object.try_emplace("name", std::string(thread_str));
+    EmplaceSafeString(object, "name", std::string(thread_str));
   }
   return llvm::json::Value(std::move(object));
 }
@@ -749,7 +757,7 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
     ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
     if (exc_bp) {
       body.try_emplace("reason", "exception");
-      body.try_emplace("description", exc_bp->label);
+      EmplaceSafeString(body, "description", exc_bp->label);
     } else {
       body.try_emplace("reason", "breakpoint");
     }
@@ -782,7 +790,7 @@ llvm::json::Value CreateThreadStopped(lldb::SBThread &thread,
   if (ObjectContainsKey(body, "description")) {
     char description[1024];
     if (thread.GetStopDescription(description, sizeof(description))) {
-      body.try_emplace("description", std::string(description));
+      EmplaceSafeString(body, "description", std::string(description));
     }
   }
   if (tid == g_vsc.focus_tid) {
@@ -862,12 +870,12 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
                                  int64_t varID, bool format_hex) {
   llvm::json::Object object;
   auto name = v.GetName();
-  object.try_emplace("name", name ? name : "<null>");
+  EmplaceSafeString(object, "name", name ? name : "<null>");
   if (format_hex)
     v.SetFormat(lldb::eFormatHex);
   SetValueForKey(v, object, "value");
   auto type_cstr = v.GetType().GetDisplayTypeName();
-  object.try_emplace("type", type_cstr ? type_cstr : NO_TYPENAME);
+  EmplaceSafeString(object, "type", type_cstr ? type_cstr : NO_TYPENAME);
   if (varID != INT64_MAX)
     object.try_emplace("id", varID);
   if (v.MightHaveChildren())
@@ -878,7 +886,7 @@ llvm::json::Value CreateVariable(lldb::SBValue v, int64_t variablesReference,
   v.GetExpressionPath(evaluateStream);
   const char *evaluateName = evaluateStream.GetData();
   if (evaluateName && evaluateName[0])
-    object.try_emplace("evaluateName", std::string(evaluateName));
+    EmplaceSafeString(object, "evaluateName", std::string(evaluateName));
   return llvm::json::Value(std::move(object));
 }
 
index 1ef3b73..0ca000c 100644 (file)
 #include "VSCodeForward.h"
 
 namespace lldb_vscode {
-  
+
+//------------------------------------------------------------------
+/// Emplace a StringRef in a json::Object after enusring that the
+/// string is valid UTF8. If not, first call llvm::json::fixUTF8
+/// before emplacing.
+///
+/// @param[in] obj
+///     A JSON object that we will attempt to emplace the value in
+///
+/// @param[in] key
+///     The key to use when emplacing the value
+///
+/// @param[in] str
+///     The string to emplace
+//------------------------------------------------------------------
+void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
+                       llvm::StringRef str);
+
 //------------------------------------------------------------------
 /// Extract simple values as a string.
 ///
index d268c45..0f138ec 100644 (file)
@@ -256,7 +256,7 @@ void VSCode::SendOutput(OutputType o, const llvm::StringRef output) {
     break;
   }
   body.try_emplace("category", category);
-  body.try_emplace("output", output.str());
+  EmplaceSafeString(body, "output", output.str());
   event.try_emplace("body", std::move(body));
   SendJSON(llvm::json::Value(std::move(event)));
 }
index 8eacc54..2aad797 100644 (file)
@@ -289,7 +289,7 @@ void SendProcessEvent(LaunchMethod launch_method) {
   exe_fspec.GetPath(exe_path, sizeof(exe_path));
   llvm::json::Object event(CreateEventObject("process"));
   llvm::json::Object body;
-  body.try_emplace("name", std::string(exe_path));
+  EmplaceSafeString(body, "name", std::string(exe_path));
   const auto pid = g_vsc.target.GetProcess().GetProcessID();
   body.try_emplace("systemProcessId", (int64_t)pid);
   body.try_emplace("isLocalProcess", true);
@@ -539,7 +539,7 @@ void request_attach(const llvm::json::Object &request) {
     g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile);
     if (error.Fail()) {
       response.try_emplace("success", false);
-      response.try_emplace("message", std::string(error.GetCString()));
+      EmplaceSafeString(response, "message", std::string(error.GetCString()));
       g_vsc.SendJSON(llvm::json::Value(std::move(response)));
       return;
     }
@@ -591,7 +591,7 @@ void request_attach(const llvm::json::Object &request) {
 
   if (error.Fail()) {
     response.try_emplace("success", false);
-    response.try_emplace("message", std::string(error.GetCString()));
+    EmplaceSafeString(response, "message", std::string(error.GetCString()));
   }
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
   if (error.Success()) {
@@ -813,8 +813,8 @@ void request_exceptionInfo(const llvm::json::Object &request) {
     else if (stopReason == lldb::eStopReasonBreakpoint) {
       ExceptionBreakpoint *exc_bp = g_vsc.GetExceptionBPFromStopReason(thread);
       if (exc_bp) {
-        body.try_emplace("exceptionId", exc_bp->filter);
-        body.try_emplace("description", exc_bp->label);
+        EmplaceSafeString(body, "exceptionId", exc_bp->filter);
+        EmplaceSafeString(body, "description", exc_bp->label);
       } else {
         body.try_emplace("exceptionId", "exception");
       }
@@ -824,7 +824,7 @@ void request_exceptionInfo(const llvm::json::Object &request) {
     if (!ObjectContainsKey(body, "description")) {
       char description[1024];
       if (thread.GetStopDescription(description, sizeof(description))) {
-        body.try_emplace("description", std::string(description));
+        EmplaceSafeString(body, "description", std::string(description));
       }
     }
     body.try_emplace("breakMode", "always");
@@ -951,9 +951,9 @@ void request_evaluate(const llvm::json::Object &request) {
   const auto expression = GetString(arguments, "expression");
 
   if (!expression.empty() && expression[0] == '`') {
-    body.try_emplace("result",
-                     RunLLDBCommands(llvm::StringRef(),
-                                     {expression.substr(1)}));
+    auto result = RunLLDBCommands(llvm::StringRef(),
+                                     {expression.substr(1)});
+    EmplaceSafeString(body, "result", result);
     body.try_emplace("variablesReference", (int64_t)0);
   } else {
     // Always try to get the answer from the local variables if possible. If
@@ -968,13 +968,13 @@ void request_evaluate(const llvm::json::Object &request) {
       response.try_emplace("success", false);
       const char *error_cstr = value.GetError().GetCString();
       if (error_cstr && error_cstr[0])
-        response.try_emplace("message", std::string(error_cstr));
+        EmplaceSafeString(response, "message", std::string(error_cstr));
       else
-        response.try_emplace("message", "evaluate failed");
+        EmplaceSafeString(response, "message", "evaluate failed");
     } else {
       SetValueForKey(value, body, "result");
       auto value_typename = value.GetType().GetDisplayTypeName();
-      body.try_emplace("type", value_typename ? value_typename : NO_TYPENAME);
+      EmplaceSafeString(body, "type", value_typename ? value_typename : NO_TYPENAME);
       if (value.MightHaveChildren()) {
         auto variablesReference = VARIDX_TO_VARREF(g_vsc.variables.GetSize());
         g_vsc.variables.Append(value);
@@ -1241,7 +1241,7 @@ void request_launch(const llvm::json::Object &request) {
     g_vsc.target.AddModule(program.data(), target_triple, uuid_cstr, symfile);
     if (error.Fail()) {
       response.try_emplace("success", false);
-      response.try_emplace("message", std::string(error.GetCString()));
+      EmplaceSafeString(response, "message", std::string(error.GetCString()));
       g_vsc.SendJSON(llvm::json::Value(std::move(response)));
     }
   }
@@ -1279,7 +1279,7 @@ void request_launch(const llvm::json::Object &request) {
   g_vsc.target.Launch(g_vsc.launch_info, error);
   if (error.Fail()) {
     response.try_emplace("success", false);
-    response.try_emplace("message", std::string(error.GetCString()));
+    EmplaceSafeString(response, "message", std::string(error.GetCString()));
   }
   g_vsc.SendJSON(llvm::json::Value(std::move(response)));
 
@@ -1945,7 +1945,7 @@ void request_source(const llvm::json::Object &request) {
   auto sourceReference = GetSigned(source, "sourceReference", -1);
   auto pos = g_vsc.source_map.find((lldb::addr_t)sourceReference);
   if (pos != g_vsc.source_map.end()) {
-    body.try_emplace("content", pos->second.content);
+    EmplaceSafeString(body, "content", pos->second.content);
   } else {
     response.try_emplace("success", false);
   }
@@ -2406,10 +2406,10 @@ void request_setVariable(const llvm::json::Object &request) {
     bool success = variable.SetValueFromCString(value.data(), error);
     if (success) {
       SetValueForKey(variable, body, "value");
-      body.try_emplace("type", variable.GetType().GetDisplayTypeName());
+      EmplaceSafeString(body, "type", variable.GetType().GetDisplayTypeName());
       body.try_emplace("variablesReference", newVariablesReference);
     } else {
-      body.try_emplace("message", std::string(error.GetCString()));
+      EmplaceSafeString(body, "message", std::string(error.GetCString()));
     }
     response.try_emplace("success", success);
   }