Open Bug 1710940 Opened 4 years ago Updated 1 month ago

Crash event is not sent in isolated process

Categories

(GeckoView :: General, defect, P2)

Unspecified
All
defect

Tracking

(firefox103 disabled, firefox104 disabled, firefox105 disabled, firefox106 disabled, firefox107 disabled)

ASSIGNED
Tracking Status
firefox103 --- disabled
firefox104 --- disabled
firefox105 --- disabled
firefox106 --- disabled
firefox107 --- disabled

People

(Reporter: owlish, Assigned: cmartin)

References

(Blocks 3 open bugs)

Details

(Whiteboard: [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107] [geckoview:2022q4] [fxdroid] [geckoview])

Attachments

(1 file)

Affected tests:

  • ContentDelegateTest#crashContent
  • ContentDelegateTest.#rashContent_tapAfterCrash
  • GeckoSessionTestRuleTest#contentCrashIgnored
  • ContentCrashTest#crashContent
Severity: -- → S3
Priority: -- → P2
Whiteboard: [geckoview:m91?]
Whiteboard: [geckoview:m91?]
Whiteboard: [geckoview:2022q3?]
Whiteboard: [geckoview:2022q3?] → [sandboxing] [geckoview:2022q3]

Moving isolated process bugs to the new GeckoView::Sandboxing component.

Component: General → Sandboxing
Assignee: nobody → ohall
Priority: P2 → P1
Whiteboard: [sandboxing] [geckoview:2022q3] → [sandboxing] [geckoview:2022q3] [geckoview:m106]

107

Whiteboard: [sandboxing] [geckoview:2022q3] [geckoview:m106] → [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107]
Depends on: 1793784
See Also: → 1644486

This bug is related to issues reading the auxiliary vector at /proc/<crashing-pid>/auxv. Currently tested using the breakpad minidump writer.

The rust minidump writer bug of Improving handling of the auxiliary vector on Linux will likely become related to this bug on the change.

P2, waiting for gsvelto's new rust minidump writer to land.

Priority: P1 → P2
Whiteboard: [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107] → [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107] [geckoview:2022q4]
Blocks: 1698178
Type: task → defect
Assignee: ohall → nobody

FYI I've briefly investigated what would be needed to generate crash reports from isolated processes and it might require quite a bit of work. Going through Google documentation I discovered that it shouldn't be possible to use ptrace() like we currently do to inspect the child process. Specifically we need a broker process for ptrace()-ing the crashed child. That is the parent cannot ptrace_attach() to the child but must instead have the child fork a broker process that will do the ptrace() calls on its behalf.

The architecture of minidump-writer doesn't currently support this and will require a fairly significant refactoring.

Component: Sandboxing → General
Whiteboard: [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107] [geckoview:2022q4] → [sandboxing] [geckoview:2022q3] [geckoview:m106] [geckoview:m107] [geckoview:2022q4] [fxdroid] [geckoview]

Hi Gabriele,

We're looking at moving forward with isolated processes on Android.

I did some preliminary testing to see what the current state is of these junit failures. (A lot has changed since last we looked at this a couple of years ago with the rust minidump writer and bug 1620998!)

Here is a try from a couple of days ago running these tests.

In a failing isolated process run we get:

MOZ_CRASH(Crash via about:crashcontent)
...
04-25 14:55:05.682 31343 31343 I Binder:31343_3: type=1400 audit(0.0:3922): avc: denied { signal } for scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:r:isolated_app:s0:c512,c768 tclass=process permissive=1

// (GeckoCrashHelper never appears in the logs)

In a passing non-isolated process run, we see a lot less avc: denied calls overall, and a stack that looks like this:

MOZ_CRASH(Crash via about:crashcontent)
...
04-24 15:04:07.582 17499 17516 D GeckoCrashHelper: minidump_writer::linux::sections::mappings: retrieving build id for MappingInfo { start_address: 481648033792, size: 24576, system_mapping_info: SystemMappingInfo { start_address: 481648033792, end_address: 481648058368 }, offset: 0, permissions: MMPermissions(READ | WRITE | EXECUTE | PRIVATE), name: Some("/system/bin/app_process64") }

I'm still tracing and understanding how CrashHelper service works.

Could you take a look at this when you have some time?

Flags: needinfo?(gsvelto)

This is a known problem that needed bug 1620998 to be addressed. In short, isolated processes need the ptrace() calls to be proxied, which is something that we don't do yet. Before bug 1620998 we had no way of doing it, but now we should be able to. I need some time to figure this out but first I have to deal with main process crashes so I won't be able to tackle this before 1-2 months.

Flags: needinfo?(gsvelto)

Olivia, is there a list of restrictions placed on isolated processes with regards to system calls? I was trying to design the mechanism that the child processes will use to rendez-vous with the crash helper and let it generate the crash report, and I want to be sure my design does not involve bits that aren't required. Among those things I was wondering if an isolated process would be able to connect() to an AF_UNIX socket. My guess is that it shouldn't be able to, given even our sandbox prevents that unless the destination Unix address is explicitly allowed by the security policy.

Flags: needinfo?(ohall)

Thanks for looking into this!

is there a list of restrictions placed on isolated processes with regards to system calls?

I'll try to find some documentation with some specific information. I've found this short video helpful for providing some general expectations.

My guess is that it shouldn't be able to, given even our sandbox prevents that unless the destination Unix address is explicitly allowed by the security policy.

Yeah, that seems like a reasonable assumption to me too.

I'll see if I can find out some more information and leave my ni?.

:owlish, do you have some additional resources or thoughts here?

Flags: needinfo?(bugzeeeeee)

A further bit of clarification regarding comment 7 and 8. From the information I could find about isolated processes it seems like we wouldn't be able to call ptrace() directly from another non-isolated process. Right now the crash generation flow involves the crash helper process ptrace()-ing a crashed child process to extract the data that will go into the crash report. If this is not possible then we'd need an IPC channel to have been established between the crash helper and the isolated child process (because we can't connect to an external process from the isolated process). When the child process crashes it should fork() itself, call ptrace() on the forked process and then communicate back the results across the IPC channel. If this is the case then it's gonna be quite a bit of work and a dedicated abstraction in the minidump-writer crate for this particular case.

is there a list of restrictions placed on isolated processes with regards to system calls?

You want to look into the AOSP source, e.g. the SELinux policy for isolated process sits in there.

(In reply to Gian-Carlo Pascutto [:gcp] from comment #11)

is there a list of restrictions placed on isolated processes with regards to system calls?

You want to look into the AOSP source, e.g. the SELinux policy for isolated process sits in there.

From what I can tell, the main problem with ptrace is that each isolated process is assigned a unique uid/gid which are different from any regular app or any other isolated service, so ptrace fails the old-fashioned same-uid check (and so do accesses to /proc/%d/{stat,maps,…}, which may cause some other problems outside the scope of this bug; and see also comment #3 about the auxv, I think).

But, I believe that the SELinux policy would also block this, even if there would otherwise be a way for the child process to opt into being ptraced by a different-uid tracer. The only rule I can see that allows an untrusted_app to ptrace applies only if the tracee is also an untrusted_app, not an isolated_app (and the same for isolated_app: it can only ptrace another isolated process). There's also a neverallow rule in the app_domain macro which would cause a policy compilation error if such an allow rule existed.

Thanks for looking into this bug!

Just adding some more docs to add to the reference pile:

Flags: needinfo?(ohall)

It sounds like the only way to ptrace() an isolated child process will be do to something along the lines of what Crashpad does. That is:

  • When we hit the exception handler we need to fork the crashed child process, to generate a new child that will ptrace()-eable from it
  • The crashed child process ptrace()s the newly forked child and generates a minidump
  • The crashed child process finally closes its forked child, informs the parent that it generated a dump and shuts down

There's a few quirks I've deliberately left out from the description above

  • We need a pre-established IPC channel between the main process and the child processes, I'm implementing it in bug 1964600
  • Isolated processes cannot access the filesystem IIRC, so we either need to write the minidump to memory and pass it over IPC to the main process or we need to pass the child process a file descriptor we've opened in the parent
  • The child process wouldn't have access to all the information required for generating a full-fledged crash report, so we'd need to add the missing bits afterwards
  • Obviously we wouldn't want to do all this stuff from within the exception handler, it would be madness. So we probably need something else, maybe a parked thread we'd wake up on demand to generate the minidump from a non-crashed context.

(In reply to Olivia Hall [:olivia] from comment #13)

Thanks for looking into this bug!

Just adding some more docs to add to the reference pile:

Isolated mode's SELinux seems to be Permissive on our emulator image. (getenforce returns Permnissive` ) But isolated mode's service process's UID is different for other service process.

Hi Gabriele, we are working on implementing isolated processes on Android. Would you be able to take on this bug?

Flags: needinfo?(bugzeeeeee) → needinfo?(gsvelto)

(In reply to Gabriele Svelto [:gsvelto] from comment #14)

  • The crashed child process ptrace()s the newly forked child and generates a minidump

My understanding of crashpad's design is a bit different: the forked child process ptraces the crashed process, and proxies low-level operations: basically reading the process's memory, thread registers, and /proc/{pid}/… files. The minidump generation logic remains in the crash agent, with an abstraction layer over where it would normally call ptrace or access /proc directly.

In particular, this means that the proxy, in the clone of the crashed process, can be fairly minimal and may not even need to malloc (I'm not 100% but I think this is what crashpad is going for; e.g., using getdents instead of readdir). That could be useful in case of heap metadata corruption, or a crash inside malloc, or if an async signal interrupts malloc and the handler crashes, etc.

Maybe you've already come to the same conclusion, but I thought I'd mention it for the historical record at least.

  • Obviously we wouldn't want to do all this stuff from within the exception handler, it would be madness.

Not entirely. The signal handler is on the alternative stack so stack exhaustion isn't an issue, and if there were any locks held by the crashing thread then moving to another thread wouldn't help that. The one thing that might matter is if thread-local storage was inconsistent at the time of crash. But, with the approach of using a minimal proxy like what crashpad does, I think the whole thing could be async signal safe (no locks, no TLS).

(In reply to Makoto Kato [:m_kato] from comment #15)

Isolated mode's SELinux seems to be Permissive on our emulator image.

It is, and this has caused problems in the past — I'm pretty sure it's why bug 1670277 was missed by CI and only caught by testing on physical devices. It would be nice if CI could leave enforcement turned on, but I assume that's complicated. In the meantime, for patches that do subtle Linux things, I'd suggest testing manually with setenforce 1.

(In reply to [:owlish] 🦉 PST from comment #16)

Hi Gabriele, we are working on implementing isolated processes on Android. Would you be able to take on this bug?

I don't have the cycles for it, but I've talked about it to Chris Martin last Friday and he might be able to. He just did a pretty thorough overhaul of the minidump-writer interface so he's got fresh knowledge of the affected code. I'm CC'ing both him and our manager Jens.

Flags: needinfo?(gsvelto)

(In reply to Jed Davis [:jld] ⟨⏰|UTC-7⟩ ⟦he/him⟧ from comment #17)

My understanding of crashpad's design is a bit different: the forked child process ptraces the crashed process, and proxies low-level operations: basically reading the process's memory, thread registers, and /proc/{pid}/… files. The minidump generation logic remains in the crash agent, with an abstraction layer over where it would normally call ptrace or access /proc directly.

In particular, this means that the proxy, in the clone of the crashed process, can be fairly minimal and may not even need to malloc (I'm not 100% but I think this is what crashpad is going for; e.g., using getdents instead of readdir). That could be useful in case of heap metadata corruption, or a crash inside malloc, or if an async signal interrupts malloc and the handler crashes, etc.

Maybe you've already come to the same conclusion, but I thought I'd mention it for the historical record at least.

Thanks for the thorough explanation. I hadn't looked at the Crashpad code in a while so I could use a refresher.

Not entirely. The signal handler is on the alternative stack so stack exhaustion isn't an issue, and if there were any locks held by the crashing thread then moving to another thread wouldn't help that. The one thing that might matter is if thread-local storage was inconsistent at the time of crash. But, with the approach of using a minimal proxy like what crashpad does, I think the whole thing could be async signal safe (no locks, no TLS).

Right, I hadn't thought about the possibility of the crashed threads holding locks. The memory allocation angle is non-trivial. If the crashed process got stuck in the memory allocator then the forked process wouldn't be able to do any allocations, at least not via the regular allocator. Breakpad worked around that problem by having a "leaking" allocator that it could use from within those contexts, but it's a very intrusive change to the minidump generation. I need to ponder about this a little bit more.

See Also: → 1984075
See Also: → 1989882
Assignee: nobody → cmartin
Status: NEW → ASSIGNED

FYI to verify that it will work with content processes you can as well use the following wdspec test:
https://searchfox.org/firefox-main/source/testing/web-platform/mozilla/tests/webdriver/harness/detect_crash.py

Please note that via bug 2011830 I'm going to mark this test as expected fail for now. You should update the relevant wpt meta data file and remove the entry before pushing the patch to autoland.

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: