I’m building a chrome extension what utilizes a native messaging host for getting information about the DOM and transfer them to another app. During my testing i managed to send a message to the chrome extension that was exactly 266 bytes long (270 accounting the 4 bytes for length). After some investigation, I found out that this message length is not the only one which is problematic. Every multiple of 256 (256, 512, 768, 1024, 1280, 1536, 1792, 2048,… and especially 2560), added 10, sometimes 20 and 30, caused the chrome to stop parsing from host’s stdout. The native host, continues to send data and ultimately, an exception “The sender sent an invalid JSON message; message ignored.” appears without context. After that, the chrome invalidates the stdin of the native host, bringing it to a close.
Extension Manifest
[...]
"manifest_version": 3,
"permissions": [
"storage",
"tabs",
"background",
"notifications",
"nativeMessaging",
"scripting",
"debugger"
],
"host_permissions": [
"https://*/*",
"http://*/*"
],
"background": {
"service_worker": "scripts/background.js"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"scripts/content.js"
]
}
],
[...]
Background script (Sample)
const nativeID = "com.nmhost";
var nmhostPort = chrome.runtime.connectNative(nativeID);
const onNativeMessage = function (msg) {
// if (dbgFlag && dbgLvl == 3) {
// console.log("Received: n" + JSON.stringify(msg, null, 4));
// }
let ss = JSON.stringify(msg);
console.log(`Received:[${ss.length}]`);
return;
}
nmhostPort.onMessage.addListener(onNativeMessage);
nmhostPort.onDisconnect.addListener(function () {
console.error("NMHost Disconnected");
});
Native host (C++) (Sample)
/**
* @brief Get the Message from Chromium. If the length of the message is zero or less it does not read the message!
*
* @param buff The message buffer to return
* @return int32_t
*/
int32_t GetExtMessage(char *buff) {
// if stdin fails retry 3 times
for (int i = 0; i < STDIN_RETRY; i++) {
int32_t reqLen = 0;
std::cin.read(reinterpret_cast<char *>(&reqLen), 4);
if (reqLen > 0) {
std::cin.read(buff, reqLen);
return reqLen;
} else {
if (std::cin.fail()) {
std::cerr << "STDIN FAILED!. RETRYING..." << std::endl;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), EOF);
std::this_thread::sleep_for(std::chrono::milliseconds(FAIL_WAIT_MILIS));
}
}
}
return -1;
}
/**
* @brief Sends a message to Chromium
*
* @param msg The message to send
*/
void SendExtMessage(std::string msg) {
uint32_t responseLen = msg.length();
char *bt_len = reinterpret_cast<char *>(&responseLen);
std::cout.write(bt_len, 4);
std::cout.write(msg.c_str(), responseLen);
std::cout.flush();
}
int main(int argc, char *argv[]) {
// set stdin mode to BINARY
if (_setmode(_fileno(stdin), _O_BINARY) == -1) {
Log("[Error]. Could not set output mode to binary");
return 1;
}
//wait a bit
std::this_thread::sleep_for(std::chrono::seconds(5));
// send bytes to stdout
for (int i = 1; i < 100000; i++) {
// if (i == 256 || i == 510 || i == 512 || i == 768 || i == 1024 || i == 1280 || i == 1536 || i == 1792 || i == 2048 ) {
// continue;
// }
//keep in mind that final json value has 10 more bytes which are {"msg":""}
nlohmann::json jj = {{"msg", ""}};
for (int j = 0; j < i; j++) {
jj["msg"] = std::string(jj["msg"]) + "a";
}
std::string s = jj.dump();
// Log("Sending " + s + " bytes");
SendExtMessage(s);
jj["msg"] = "";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return 0;
}
A workaround i found is to add dummy bytes to the messaage is the len is of such numbers, but again it get stuck to len = 2560 and i really don’t like this.
void SendExtMessage(std::string msg) {
uint32_t responseLen = msg.length();
if ((responseLen - 10) % 256 == 0 || responseLen % 256 == 0 || (responseLen - 10 * (responseLen / 256)) % 256 == 0) {
nlohmann::json j = nlohmann::json::parse(msg);
j["xxx"] = 1;
msg = j.dump();
responseLen = msg.length();
}
char *bt_len = reinterpret_cast<char *>(&responseLen);
std::cout.write(bt_len, 4);
std::cout.write(msg.c_str(), responseLen);
std::cout.flush();
Log("[Log] Writing " + std::to_string(responseLen) + " bytes to stdout");
}
Have anyone encountered anything like this?

