Can the renderer be killed on mojom message validation failure?

668 views
Skip to first unread message

Justin Lulejian

unread,
Mar 26, 2025, 1:33:04 PMMar 26
to chromium-mojo
Hi All,

I'm looking into adding message validation to a mojom parameter that we pass around quite frequently (`string extension_id`) using StructTraits

FWIU the recommended guidance is to report a bad message and kill the renderer if you get invalid data from the render (assuming it's compromised) and if that's true we want to keep that behavior too.

But AFAIK the StructTraits::Read() method returning false doesn't kill the renderer (Mojom Interface Definition Language (IDL) (Message Validation) and Mojo C++ Bindings API (Connection Errors) say the disconnect handler is called but not that the renderer is killed), but is there a way to do this without duplicating the same validation logic after receiving the message?

I tried forcing a mojom::ReportBadMessage() inside of my StructTraits::Read() to see what would happen but I hit the DCHECK below (when running ServiceWorker/TtsApiServiceWorkerTest.SpeakError/0). My guess is it's saying I reported a bad message already when it tried to call NotifyBadMessage() automatically? 

[2478260:2478260:0326/130100.832063:ERROR:content/browser/renderer_host/render_process_host_impl.cc:5706] Terminating render process for bad Mojo message: Received bad user message: intentional kill
[2478260:2478260:0326/130100.832111:ERROR:content/browser/bad_message.cc:29] Terminating renderer for bad IPC message, reason 123
[0326/130100.849029:ERROR:third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h:64] tag not found
[0326/130100.861375:ERROR:third_party/crashpad/crashpad/util/file/file_io_posix.cc:145] open /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq: No such file or directory (2)
[0326/130100.861436:ERROR:third_party/crashpad/crashpad/util/file/file_io_posix.cc:145] open /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq: No such file or directory (2)
[2478260:2478260:0326/130100.876813:ERROR:base/process/process_posix.cc:315] Unable to terminate process 2478376: Interrupted system call (4)
[2478260:2478260:0326/130100.876922:ERROR:mojo/public/cpp/bindings/lib/validation_errors.cc:117] Invalid message: VALIDATION_ERROR_DESERIALIZATION_FAILED
[2478260:2478260:0326/130100.876944:FATAL:mojo/public/cpp/bindings/lib/message.cc:507] DCHECK failed: handle_.is_valid().
#0 0x55ad1af8e4e2 base::debug::CollectStackTrace()
#1 0x55ad1af759e1 base::debug::StackTrace::StackTrace()
#2 0x55ad1ae7411a logging::LogMessage::Flush()
#3 0x55ad1ae73fec logging::LogMessage::~LogMessage()
#4 0x55ad1ae57a21 logging::(anonymous namespace)::DCheckLogMessage::~DCheckLogMessage()
#5 0x55ad1ae57363 logging::CheckError::~CheckError()
#6 0x55ad1cf27d98 mojo::Message::NotifyBadMessage()
#7 0x55ad1cf2f7e3 mojo::internal::ReportValidationError()
#8 0x55ad1cf2f94c mojo::internal::ReportValidationErrorForMessage()
#9 0x55ad13fe8add extensions::mojom::EventRouterStubDispatch::Accept()
#10 0x55ad1cf071f2 mojo::InterfaceEndpointClient::HandleValidatedMessage()
#11 0x55ad1cf10b35 mojo::MessageDispatcher::Accept()
#12 0x55ad1cf09383 mojo::InterfaceEndpointClient::HandleIncomingMessage()
#13 0x55ad1cf15d9d mojo::internal::MultiplexRouter::ProcessIncomingMessage()
#14 0x55ad1cf152e6 mojo::internal::MultiplexRouter::Accept()
#15 0x55ad1cf10b73 mojo::MessageDispatcher::Accept()
#16 0x55ad1cf034f0 mojo::Connector::DispatchMessage()
#17 0x55ad1cf03cfe mojo::Connector::ReadAllAvailableMessages()
#18 0x55ad1cf0419b mojo::Connector::CallDispatchNextMessageFromPipe()
#19 0x55ad10c3f441 base::OnceCallback<>::Run()
#20 0x55ad1aee046d base::TaskAnnotator::RunTaskImpl()
#21 0x55ad1af1640b base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl()
#22 0x55ad1af1579b base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()
#23 0x55ad1af16ba5 base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()
#24 0x55ad1afad729 base::MessagePumpGlib::Run()
#25 0x55ad1af17551 base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run()
#26 0x55ad1aeb9006 base::RunLoop::Run()
#27 0x55ad24e1e3b1 extensions::ExtensionBackgroundPageWaiter::WaitForBackgroundWorkerInitialized()
#28 0x55ad1c4f887b extensions::ChromeTestExtensionLoader::WaitForExtensionReady()
#29 0x55ad1c4f6dac extensions::ChromeTestExtensionLoader::LoadExtension()
#30 0x55ad1ada5f11 extensions::ExtensionBrowserTest::LoadExtension()
#31 0x55ad1ad9ec1b extensions::ExtensionApiTest::RunExtensionTest()
#32 0x55ad1ad9e8e9 extensions::ExtensionApiTest::RunExtensionTest()
#33 0x55ad122f419a extensions::TtsApiServiceWorkerTest_SpeakError_Test::RunTestOnMainThread()
#34 0x55ad1bd11eb1 content::BrowserTestBase::ProxyRunTestOnMainThreadLoop()
#35 0x55ad10c3f441 base::OnceCallback<>::Run()
#36 0x55ad182f0221 content::BrowserMainRunnerImpl::Run()
#37 0x55ad182ea050 content::BrowserMain()
#38 0x55ad1a201b32 content::RunBrowserProcessMain()
#39 0x55ad1a2043c6 content::ContentMainRunnerImpl::RunBrowser()
#40 0x55ad1a203bb1 content::ContentMainRunnerImpl::Run()
#41 0x55ad1a200569 content::RunContentProcess()
#42 0x55ad1a2006d4 content::ContentMain()
#43 0x55ad1bd10f09 content::BrowserTestBase::SetUp()
#44 0x55ad1ae2d289 InProcessBrowserTest::SetUp()
#45 0x55ad194bbed0 testing::Test::Run()
#46 0x55ad194bcd02 testing::TestInfo::Run()
#47 0x55ad194bd827 testing::TestSuite::Run()
#48 0x55ad194cb2c7 testing::internal::UnitTestImpl::RunAllTests()
#49 0x55ad194caa9f testing::UnitTest::Run()
#50 0x55ad1aff2f0a base::TestSuite::Run()
#51 0x55ad24efe9bc ChromeTestSuiteRunner::RunTestSuite()
#52 0x55ad24efecef ChromeTestLauncherDelegate::RunTestSuite()
#53 0x55ad1bd867c1 content::LaunchTestsInternal()
#54 0x55ad1bd86dc5 content::LaunchTests()
#55 0x55ad24efef6a LaunchChromeTests()
#56 0x55ad1ad76ec5 main
#57 0x7f2797a01d68 (/usr/lib/x86_64-linux-gnu/libc.so.6+0x29d67)
#58 0x7f2797a01e25 __libc_start_main
#59 0x55ad10c3202a _start
Task trace:
#0 0x55ad1cf0410d mojo::Connector::PostDispatchNextMessageFromPipe()
#1 0x55ad1bdeea55 mojo::SimpleWatcher::Context::Notify()

--
Best regards,
Justin

Justin Lulejian | SWE - Chrome Extensions, US-REMOTE-CT | jlul...@google.com

Emily Andrews (LEDBETTER)

unread,
Mar 26, 2025, 1:37:47 PMMar 26
to chromium-mojo, Justin Lulejian
I'm working on something that could help!

Just have to finish massaging the compiler to not have it blow up the binary size:

The design is that you can add a tag to a particular message and have it validate the message on the send side.

The tag can be added like so: feature kBarValidationFeature { const string name = "BarValidationFeature"; const bool default_state = true; }; interface Foo { [SendValidation=kBarValidationFeature] Bar(Struct struct_needing_validation); };



Sent from Outlook



From: 'Justin Lulejian' via chromium-mojo <chromi...@chromium.org>
Sent: Wednesday, March 26, 2025 12:28 PM
To: chromium-mojo <chromi...@chromium.org>
Subject: [EXTERNAL] Can the renderer be killed on mojom message validation failure?
 
--
You received this message because you are subscribed to the Google Groups "chromium-mojo" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-moj...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/chromium-mojo/CAFuoSPEcsnmfJ0H1VJwUgUX5-Y-WtT0s_%2BApnaqcPkuFbAvjyQ%40mail.gmail.com.

Joe Mason

unread,
Mar 26, 2025, 1:46:03 PMMar 26
to Justin Lulejian, chromium-mojo
The disconnect handler kills the renderer.

I'm not sure if browser<->renderer mojo connections install a special disconnect handler that does that, or if that's the default and any process that DOESN'T get killed has installed a disconnect handler to avoid it.

--

Marijn Kruisselbrink

unread,
Mar 26, 2025, 2:05:52 PMMar 26
to Joe Mason, Justin Lulejian, chromium-mojo
It's not the disconnect handler that kills the renderer, but validation failure ends up in mojo::NotifyBadMessage, which (via the "process_error_callback" passed to mojo::OutgoingInvitation when launching a child process) ends up in RenderProcessHostImpl::OnMojoError to kill the renderer.

Justin Lulejian

unread,
Mar 26, 2025, 2:14:40 PMMar 26
to Marijn Kruisselbrink, Joe Mason, chromium-mojo
Emily: That seems like a nifty feature! It seems targeted to mojom `feature` message objects, can it be used on a struct like we have?

Joe and Marjin: Ah so the behavior I'm seeking already exists then? That's great news. What I thought I had to do seemed very redundant so I'm glad that's not the case. Thank you for the detailed explanation on where the bad message is being emitted.

Joe Mason

unread,
Mar 26, 2025, 2:16:31 PMMar 26
to Marijn Kruisselbrink, Justin Lulejian, chromium-mojo
Oh, sorry! I knew it was a callback that mojo automatically invoked, but I just assumed that was the disconnect handler.

Regardless the broad strokes are: returning "false" from StructTraits and calling ReportBadMessage both invoke the same mojo error handling: NotifyBadMessage(). And Chrome hooks up NotifyBadMessage() to kill renderer processes.



On Wed, Mar 26, 2025 at 2:05 PM Marijn Kruisselbrink <m...@chromium.org> wrote:

Emily Andrews (LEDBETTER)

unread,
Mar 26, 2025, 3:51:58 PMMar 26
to Marijn Kruisselbrink, Justin Lulejian, Joe Mason, chromium-mojo
What the feature mojom object does is basically it compiles in a FeatureList check to turn on or off the serialization check. The Send Validation tag is associated with the message, and all parameters sent into the message are validated on serialization if the Feature is enabled. 

What I observed when looking through deserialization errors are that most of them pop up on a particular message, and not on a particular struct across many messages. There's usually some niche untested scenario that resulted in the developer making some sort of mistake or there's some sort of bad actor sending bad messages.

Serialization checks do have a performance and code size trade off. It's good to be as strategic as possible when adding them, but some bugs are challenging to solve without them.

Sent from Outlook


From: 'Justin Lulejian' via chromium-mojo <chromi...@chromium.org>
Sent: Wednesday, March 26, 2025 1:12 PM
To: Marijn Kruisselbrink <m...@chromium.org>
Cc: Joe Mason <joenot...@google.com>; chromium-mojo <chromi...@chromium.org>
Subject: [EXTERNAL] Re: Can the renderer be killed on mojom message validation failure?
 
Reply all
Reply to author
Forward
0 new messages